feat(webdav): film verilerini taşıma desteği ekle
Dosya taşıma işlemi sırasında etkilenen film verilerini ve metadatasını korumak için yeni mantık eklendi. `collectMovieRelPathsForMove` ile etkilenen yollar tespit edilirken, `moveMovieDataDir` ile fiziksel veri klasörleri ve metadata.json dosyaları taşınarak `_dupe` referansları güncelleniyor. Aynı kök dizin içinde veya farklı kök dizinler arasında taşıma işlemleri destekleniyor.
This commit is contained in:
144
server/server.js
144
server/server.js
@@ -5084,6 +5084,126 @@ function collectSeriesIdsForPath(info, oldRel, isDirectory) {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectMovieRelPathsForMove(info, oldRel, isDirectory) {
|
||||||
|
const relPaths = new Set();
|
||||||
|
if (!info || typeof info !== "object") return relPaths;
|
||||||
|
const normalizedOldRel = normalizeTrashPath(oldRel);
|
||||||
|
const shouldMatch = (key) => {
|
||||||
|
if (!normalizedOldRel) return true;
|
||||||
|
const normalizedKey = normalizeTrashPath(key);
|
||||||
|
if (normalizedKey === normalizedOldRel) return true;
|
||||||
|
return (
|
||||||
|
isDirectory &&
|
||||||
|
normalizedOldRel &&
|
||||||
|
normalizedKey.startsWith(`${normalizedOldRel}/`)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (info.primaryVideoPath && shouldMatch(info.primaryVideoPath)) {
|
||||||
|
relPaths.add(normalizeTrashPath(info.primaryVideoPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = info.files || {};
|
||||||
|
for (const [key, value] of Object.entries(files)) {
|
||||||
|
if (!value?.movieMatch) continue;
|
||||||
|
if (!shouldMatch(key)) continue;
|
||||||
|
relPaths.add(normalizeTrashPath(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return relPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapRelPathForMove(oldRel, newRel, relPath, isDirectory) {
|
||||||
|
const normalizedOldRel = normalizeTrashPath(oldRel);
|
||||||
|
const normalizedNewRel = normalizeTrashPath(newRel);
|
||||||
|
const normalizedRel = normalizeTrashPath(relPath);
|
||||||
|
if (!normalizedOldRel) return normalizedRel;
|
||||||
|
if (normalizedRel === normalizedOldRel) return normalizedNewRel;
|
||||||
|
if (
|
||||||
|
isDirectory &&
|
||||||
|
normalizedRel.startsWith(`${normalizedOldRel}/`)
|
||||||
|
) {
|
||||||
|
const suffix = normalizedRel.slice(normalizedOldRel.length).replace(/^\/+/, "");
|
||||||
|
return normalizedNewRel
|
||||||
|
? `${normalizedNewRel}${suffix ? `/${suffix}` : ""}`
|
||||||
|
: suffix;
|
||||||
|
}
|
||||||
|
return normalizedRel;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveMovieDataDir(oldKey, newKey, oldRoot, newRoot, newRelPath) {
|
||||||
|
if (!oldKey || !newKey) return false;
|
||||||
|
if (oldKey === newKey) return false;
|
||||||
|
const oldDir = movieDataPathsByKey(oldKey).dir;
|
||||||
|
const newDir = movieDataPathsByKey(newKey).dir;
|
||||||
|
if (!fs.existsSync(oldDir)) return false;
|
||||||
|
if (fs.existsSync(newDir)) {
|
||||||
|
try {
|
||||||
|
fs.rmSync(newDir, { recursive: true, force: true });
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`⚠️ Movie metadata hedefi temizlenemedi (${newDir}): ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
fs.renameSync(oldDir, newDir);
|
||||||
|
} catch (err) {
|
||||||
|
try {
|
||||||
|
fs.cpSync(oldDir, newDir, { recursive: true });
|
||||||
|
fs.rmSync(oldDir, { recursive: true, force: true });
|
||||||
|
} catch (copyErr) {
|
||||||
|
console.warn(`⚠️ Movie metadata taşınamadı (${oldDir}): ${copyErr.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadataPath = path.join(newDir, "metadata.json");
|
||||||
|
if (fs.existsSync(metadataPath)) {
|
||||||
|
try {
|
||||||
|
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
||||||
|
if (metadata?._dupe) {
|
||||||
|
metadata._dupe.folder = newRoot;
|
||||||
|
metadata._dupe.videoPath = newRelPath;
|
||||||
|
metadata._dupe.cacheKey = newKey;
|
||||||
|
}
|
||||||
|
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`⚠️ movie metadata güncellenemedi (${metadataPath}): ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveMovieDataBetweenRoots(oldRoot, newRoot, oldRel, newRel, relPaths, isDirectory) {
|
||||||
|
if (!oldRoot || !newRoot) return false;
|
||||||
|
if (!relPaths || relPaths.size === 0) return false;
|
||||||
|
let movedAny = false;
|
||||||
|
for (const relPath of relPaths) {
|
||||||
|
const mappedRel = mapRelPathForMove(oldRel, newRel, relPath, isDirectory);
|
||||||
|
const oldKey = movieDataKey(oldRoot, relPath);
|
||||||
|
const newKey = movieDataKey(newRoot, mappedRel);
|
||||||
|
if (moveMovieDataDir(oldKey, newKey, oldRoot, newRoot, mappedRel)) {
|
||||||
|
movedAny = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return movedAny;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveMovieDataWithinRoot(rootFolder, oldRel, newRel, relPaths, isDirectory) {
|
||||||
|
if (!rootFolder) return false;
|
||||||
|
if (!relPaths || relPaths.size === 0) return false;
|
||||||
|
let movedAny = false;
|
||||||
|
for (const relPath of relPaths) {
|
||||||
|
const mappedRel = mapRelPathForMove(oldRel, newRel, relPath, isDirectory);
|
||||||
|
const oldKey = movieDataKey(rootFolder, relPath);
|
||||||
|
const newKey = movieDataKey(rootFolder, mappedRel);
|
||||||
|
if (moveMovieDataDir(oldKey, newKey, rootFolder, rootFolder, mappedRel)) {
|
||||||
|
movedAny = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return movedAny;
|
||||||
|
}
|
||||||
|
|
||||||
function updateSeriesJsonAfterRootMove(seriesData, oldRoot, newRoot, oldRel, newRel) {
|
function updateSeriesJsonAfterRootMove(seriesData, oldRoot, newRoot, oldRel, newRel) {
|
||||||
if (!seriesData || typeof seriesData !== "object") return false;
|
if (!seriesData || typeof seriesData !== "object") return false;
|
||||||
let changed = false;
|
let changed = false;
|
||||||
@@ -6797,6 +6917,11 @@ app.post("/api/file/move", requireAuth, (req, res) => {
|
|||||||
sourceRelWithinRoot,
|
sourceRelWithinRoot,
|
||||||
isDirectory
|
isDirectory
|
||||||
);
|
);
|
||||||
|
const affectedMovieRelPaths = collectMovieRelPathsForMove(
|
||||||
|
preMoveInfo,
|
||||||
|
sourceRelWithinRoot,
|
||||||
|
isDirectory
|
||||||
|
);
|
||||||
|
|
||||||
fs.renameSync(sourceFullPath, newFullPath);
|
fs.renameSync(sourceFullPath, newFullPath);
|
||||||
|
|
||||||
@@ -6809,6 +6934,15 @@ app.post("/api/file/move", requireAuth, (req, res) => {
|
|||||||
renameInfoPaths(sourceRoot, sourceRelWithinRoot, destRelWithinRoot);
|
renameInfoPaths(sourceRoot, sourceRelWithinRoot, destRelWithinRoot);
|
||||||
renameSeriesDataPaths(sourceRoot, sourceRelWithinRoot, destRelWithinRoot);
|
renameSeriesDataPaths(sourceRoot, sourceRelWithinRoot, destRelWithinRoot);
|
||||||
renameTrashEntries(sourceRoot, sourceRelWithinRoot, destRelWithinRoot);
|
renameTrashEntries(sourceRoot, sourceRelWithinRoot, destRelWithinRoot);
|
||||||
|
if (affectedMovieRelPaths.size) {
|
||||||
|
moveMovieDataWithinRoot(
|
||||||
|
sourceRoot,
|
||||||
|
sourceRelWithinRoot,
|
||||||
|
destRelWithinRoot,
|
||||||
|
affectedMovieRelPaths,
|
||||||
|
isDirectory
|
||||||
|
);
|
||||||
|
}
|
||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
removeThumbnailsForDirectory(sourceRoot, sourceRelWithinRoot);
|
removeThumbnailsForDirectory(sourceRoot, sourceRelWithinRoot);
|
||||||
} else {
|
} else {
|
||||||
@@ -6833,6 +6967,16 @@ app.post("/api/file/move", requireAuth, (req, res) => {
|
|||||||
affectedSeriesIds
|
affectedSeriesIds
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (affectedMovieRelPaths.size) {
|
||||||
|
moveMovieDataBetweenRoots(
|
||||||
|
sourceRoot,
|
||||||
|
destRoot,
|
||||||
|
sourceRelWithinRoot,
|
||||||
|
destRelWithinRoot,
|
||||||
|
affectedMovieRelPaths,
|
||||||
|
isDirectory
|
||||||
|
);
|
||||||
|
}
|
||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
removeThumbnailsForDirectory(sourceRoot, sourceRelWithinRoot);
|
removeThumbnailsForDirectory(sourceRoot, sourceRelWithinRoot);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user