feat(tv): Sürüm tabanlı yenileme ile ayrıntılı TV şov yeniden tarama desteği eklendi
Daha güvenilir güncellemeler için sayım tabanlı depolama yerine sürüm tabanlı yenileme mekanizması kullanıldı. Belirli TV kök dizinlerini hedeflemeyi ve seçici önbellek temizlemeyi desteklemek için medya yeniden tarama sistemi geliştirildi. Yalnızca etkilenen dizinler için yeniden taramaları tetiklemek üzere çöp kutusu işlemleri iyileştirildi, böylece gereksiz işleme azaltıldı.
This commit is contained in:
@@ -4128,14 +4128,37 @@ function broadcastSnapshot() {
|
||||
}
|
||||
|
||||
let mediaRescanTask = null;
|
||||
let pendingMediaRescan = { movies: false, tv: false };
|
||||
let lastMediaRescanReason = "manual";
|
||||
let pendingMediaRescan = {
|
||||
movies: false,
|
||||
tv: false,
|
||||
tvRoots: new Set(),
|
||||
clearCacheMovies: false,
|
||||
clearCacheTv: false,
|
||||
reason: "manual"
|
||||
};
|
||||
|
||||
function queueMediaRescan({ movies = false, tv = false, reason = "manual" } = {}) {
|
||||
function queueMediaRescan({
|
||||
movies = false,
|
||||
tv = false,
|
||||
reason = "manual",
|
||||
roots = [],
|
||||
clearCacheMovies = false,
|
||||
clearCacheTv = false
|
||||
} = {}) {
|
||||
if (!movies && !tv) return;
|
||||
pendingMediaRescan.movies = pendingMediaRescan.movies || movies;
|
||||
pendingMediaRescan.tv = pendingMediaRescan.tv || tv;
|
||||
lastMediaRescanReason = reason;
|
||||
pendingMediaRescan.clearCacheMovies =
|
||||
pendingMediaRescan.clearCacheMovies || clearCacheMovies;
|
||||
pendingMediaRescan.clearCacheTv =
|
||||
pendingMediaRescan.clearCacheTv || clearCacheTv;
|
||||
pendingMediaRescan.reason = reason;
|
||||
if (tv && Array.isArray(roots)) {
|
||||
roots.forEach((r) => {
|
||||
const safe = sanitizeRelative(r);
|
||||
if (safe) pendingMediaRescan.tvRoots.add(safe);
|
||||
});
|
||||
}
|
||||
if (!mediaRescanTask) {
|
||||
mediaRescanTask = runQueuedMediaRescan().finally(() => {
|
||||
mediaRescanTask = null;
|
||||
@@ -4145,16 +4168,29 @@ function queueMediaRescan({ movies = false, tv = false, reason = "manual" } = {}
|
||||
|
||||
async function runQueuedMediaRescan() {
|
||||
while (pendingMediaRescan.movies || pendingMediaRescan.tv) {
|
||||
const targets = { ...pendingMediaRescan };
|
||||
pendingMediaRescan = { movies: false, tv: false };
|
||||
const reason = lastMediaRescanReason;
|
||||
const targets = {
|
||||
movies: pendingMediaRescan.movies,
|
||||
tv: pendingMediaRescan.tv,
|
||||
tvRoots: new Set(pendingMediaRescan.tvRoots),
|
||||
clearCacheMovies: pendingMediaRescan.clearCacheMovies,
|
||||
clearCacheTv: pendingMediaRescan.clearCacheTv,
|
||||
reason: pendingMediaRescan.reason
|
||||
};
|
||||
pendingMediaRescan = {
|
||||
movies: false,
|
||||
tv: false,
|
||||
tvRoots: new Set(),
|
||||
clearCacheMovies: false,
|
||||
clearCacheTv: false,
|
||||
reason: "manual"
|
||||
};
|
||||
console.log(
|
||||
`🔁 Medya taraması tetiklendi (${reason}) -> movies:${targets.movies} tv:${targets.tv}`
|
||||
`🔁 Medya taraması tetiklendi (${targets.reason}) -> movies:${targets.movies} tv:${targets.tv} roots:${Array.from(targets.tvRoots).join(",") || "-"}`
|
||||
);
|
||||
try {
|
||||
if (targets.movies) {
|
||||
if (TMDB_API_KEY) {
|
||||
await rebuildMovieMetadata({ clearCache: true });
|
||||
await rebuildMovieMetadata({ clearCache: targets.clearCacheMovies });
|
||||
} else {
|
||||
console.warn("⚠️ TMDB anahtarı tanımsız olduğu için film taraması atlandı.");
|
||||
}
|
||||
@@ -4162,7 +4198,10 @@ async function runQueuedMediaRescan() {
|
||||
|
||||
if (targets.tv) {
|
||||
if (TVDB_API_KEY) {
|
||||
await rebuildTvMetadata({ clearCache: true });
|
||||
await rebuildTvMetadata({
|
||||
clearCache: targets.clearCacheTv,
|
||||
roots: Array.from(targets.tvRoots)
|
||||
});
|
||||
} else {
|
||||
console.warn("⚠️ TVDB anahtarı tanımsız olduğu için dizi taraması atlandı.");
|
||||
}
|
||||
@@ -5125,7 +5164,10 @@ app.delete("/api/file", requireAuth, (req, res) => {
|
||||
queueMediaRescan({
|
||||
movies: mediaFlags.movies,
|
||||
tv: mediaFlags.tv,
|
||||
reason: "trash-add"
|
||||
reason: "trash-add",
|
||||
roots: [folderId],
|
||||
clearCacheMovies: false,
|
||||
clearCacheTv: false
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5598,7 +5640,10 @@ app.post("/api/trash/restore", requireAuth, (req, res) => {
|
||||
queueMediaRescan({
|
||||
movies: mediaFlags.movies,
|
||||
tv: mediaFlags.tv,
|
||||
reason: "trash-restore"
|
||||
reason: "trash-restore",
|
||||
roots: [rootFolder],
|
||||
clearCacheMovies: false,
|
||||
clearCacheTv: false
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6487,12 +6532,19 @@ app.get("/api/music", requireAuth, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
async function rebuildTvMetadata({ clearCache = false } = {}) {
|
||||
async function rebuildTvMetadata({ clearCache = false, roots = null } = {}) {
|
||||
if (!TVDB_API_KEY) {
|
||||
throw new Error("TVDB API erişimi için gerekli anahtar tanımlı değil.");
|
||||
}
|
||||
|
||||
if (clearCache && fs.existsSync(TV_DATA_ROOT)) {
|
||||
const limitedRoots =
|
||||
Array.isArray(roots) && roots.length
|
||||
? roots
|
||||
.map((r) => sanitizeRelative(r))
|
||||
.filter(Boolean)
|
||||
: null;
|
||||
|
||||
if (clearCache && !limitedRoots && fs.existsSync(TV_DATA_ROOT)) {
|
||||
try {
|
||||
fs.rmSync(TV_DATA_ROOT, { recursive: true, force: true });
|
||||
console.log("🧹 TV cache temizlendi.");
|
||||
@@ -6511,10 +6563,15 @@ async function rebuildTvMetadata({ clearCache = false } = {}) {
|
||||
tvdbEpisodeDetailCache.clear();
|
||||
}
|
||||
|
||||
const dirEntries = fs
|
||||
let dirEntries = fs
|
||||
.readdirSync(DOWNLOAD_DIR, { withFileTypes: true })
|
||||
.filter((d) => d.isDirectory());
|
||||
|
||||
if (limitedRoots) {
|
||||
const allowed = new Set(limitedRoots);
|
||||
dirEntries = dirEntries.filter((d) => allowed.has(d.name));
|
||||
}
|
||||
|
||||
const processed = [];
|
||||
|
||||
for (const dirent of dirEntries) {
|
||||
|
||||
Reference in New Issue
Block a user