feat(movies): film tekrarlarını ve önbelleği temizle

Aynı film için birden fazla önbellek girdisi olduğunda en güncel olanı
tutup eski önbellekleri temizleyen mekanizma eklendi. Video yolu
bulunamayan filmlerin metadatası otomatik silinir.
This commit is contained in:
2026-02-01 18:02:08 +03:00
parent e7925aa39f
commit 6ac608a0b1

View File

@@ -7565,7 +7565,7 @@ app.get("/api/movies", requireAuth, (req, res) => {
.readdirSync(MOVIE_DATA_ROOT, { withFileTypes: true }) .readdirSync(MOVIE_DATA_ROOT, { withFileTypes: true })
.filter((d) => d.isDirectory()); .filter((d) => d.isDirectory());
const movies = entries const rawMovies = entries
.map((dirent) => { .map((dirent) => {
const key = dirent.name; const key = dirent.name;
const paths = movieDataPathsByKey(key); const paths = movieDataPathsByKey(key);
@@ -7587,6 +7587,17 @@ app.get("/api/movies", requireAuth, (req, res) => {
const dupe = metadata._dupe || {}; const dupe = metadata._dupe || {};
const rootFolder = dupe.folder || key; const rootFolder = dupe.folder || key;
const videoPath = dupe.videoPath || metadata.videoPath || null; const videoPath = dupe.videoPath || metadata.videoPath || null;
const absVideo = resolveMovieVideoAbsPath(metadata);
if (!absVideo) {
try {
fs.rmSync(paths.dir, { recursive: true, force: true });
} catch (err) {
console.warn(
`⚠️ Movie metadata temizlenemedi (${paths.dir}): ${err.message}`
);
}
return null;
}
const encodedKey = key const encodedKey = key
.split(path.sep) .split(path.sep)
.map(encodeURIComponent) .map(encodeURIComponent)
@@ -7605,6 +7616,14 @@ app.get("/api/movies", requireAuth, (req, res) => {
: null); : null);
const cacheKey = paths.key; const cacheKey = paths.key;
return { return {
_absVideo: absVideo,
_cacheDir: paths.dir,
_cacheKey: cacheKey,
_dedupeKey:
typeof metadata.id === "number" && metadata.id
? `id:${metadata.id}`
: `title:${(metadata.title || metadata.matched_title || rootFolder)
.toLowerCase()}-${year || "unknown"}`,
folder: rootFolder, folder: rootFolder,
cacheKey, cacheKey,
id: id:
@@ -7639,6 +7658,51 @@ app.get("/api/movies", requireAuth, (req, res) => {
}) })
.filter(Boolean); .filter(Boolean);
const dedupedMap = new Map();
for (const item of rawMovies) {
const key = item._dedupeKey;
if (!dedupedMap.has(key)) {
dedupedMap.set(key, item);
continue;
}
const existing = dedupedMap.get(key);
const existingScore =
existing?.metadata?._dupe?.fetchedAt ||
existing?.metadata?._dupe?.matchedAt ||
existing?.metadata?.matchedAt ||
0;
const nextScore =
item?.metadata?._dupe?.fetchedAt ||
item?.metadata?._dupe?.matchedAt ||
item?.metadata?.matchedAt ||
0;
if (nextScore > existingScore) {
if (existing?._cacheDir) {
try {
fs.rmSync(existing._cacheDir, { recursive: true, force: true });
} catch (err) {
console.warn(
`⚠️ Movie metadata temizlenemedi (${existing._cacheDir}): ${err.message}`
);
}
}
dedupedMap.set(key, item);
} else if (item?._cacheDir) {
try {
fs.rmSync(item._cacheDir, { recursive: true, force: true });
} catch (err) {
console.warn(
`⚠️ Movie metadata temizlenemedi (${item._cacheDir}): ${err.message}`
);
}
}
}
const movies = Array.from(dedupedMap.values()).map((item) => {
const { _absVideo, _cacheDir, _cacheKey, _dedupeKey, ...rest } = item;
return rest;
});
movies.sort((a, b) => { movies.sort((a, b) => {
const yearA = a.year || 0; const yearA = a.year || 0;
const yearB = b.year || 0; const yearB = b.year || 0;