From 66aa99f0f78e098e589b946211e2f6f9cfccdda7 Mon Sep 17 00:00:00 2001 From: wisecolt Date: Sun, 1 Feb 2026 13:16:46 +0300 Subject: [PATCH] =?UTF-8?q?feat(webdav):=20info.json=20tabanl=C4=B1=20dizi?= =?UTF-8?q?=20indeksleme=20ekle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit info.json dosyalarını okuyarak WebDAV dizin yapısını oluşturma özelliği eklendi. Bu özellik, mevcut TV ve Anime veri köklerinde olmayan ancak indirme dizininde bulunan dizileri indeksler. --- server/server.js | 106 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/server/server.js b/server/server.js index ec13cf7..667c786 100644 --- a/server/server.js +++ b/server/server.js @@ -2790,10 +2790,14 @@ function rebuildWebdavIndex() { console.warn(`⚠️ WebDAV movie index oluşturulamadı: ${err.message}`); } - const buildSeriesCategory = (dataRoot, categoryLabel) => { + const buildSeriesCategory = ( + dataRoot, + categoryLabel, + usedShowNames, + coveredRoots + ) => { try { const dirs = fs.readdirSync(dataRoot, { withFileTypes: true }); - const usedShowNames = new Set(); for (const dirent of dirs) { if (!dirent.isDirectory()) continue; const seriesDir = path.join(dataRoot, dirent.name); @@ -2806,6 +2810,7 @@ function rebuildWebdavIndex() { continue; } const { rootFolder } = parseTvSeriesKey(dirent.name); + if (coveredRoots) coveredRoots.add(rootFolder); const showTitle = sanitizeWebdavSegment( seriesData?.name || seriesData?.title || dirent.name ); @@ -2863,8 +2868,101 @@ function rebuildWebdavIndex() { } }; - buildSeriesCategory(TV_DATA_ROOT, "TV Shows"); - buildSeriesCategory(ANIME_DATA_ROOT, "Anime"); + const tvUsedShowNames = new Set(); + const animeUsedShowNames = new Set(); + const coveredTvRoots = new Set(); + buildSeriesCategory(TV_DATA_ROOT, "TV Shows", tvUsedShowNames, coveredTvRoots); + buildSeriesCategory(ANIME_DATA_ROOT, "Anime", animeUsedShowNames, null); + + const buildSeriesFromInfoJson = (categoryLabel, usedShowNames, coveredRoots) => { + try { + const roots = fs.readdirSync(DOWNLOAD_DIR, { withFileTypes: true }); + for (const dirent of roots) { + if (!dirent.isDirectory()) continue; + const rootFolder = dirent.name; + if (coveredRoots?.has(rootFolder)) continue; + const infoPath = path.join(DOWNLOAD_DIR, rootFolder, "info.json"); + if (!fs.existsSync(infoPath)) continue; + let info = null; + try { + info = JSON.parse(fs.readFileSync(infoPath, "utf-8")); + } catch (err) { + continue; + } + const episodes = info?.seriesEpisodes; + if (!episodes || typeof episodes !== "object") continue; + + const showBuckets = new Map(); + for (const [relPath, episode] of Object.entries(episodes)) { + if (!episode) continue; + if (shouldSkip(relPath)) continue; + const absVideo = path.join(DOWNLOAD_DIR, rootFolder, relPath); + if (!fs.existsSync(absVideo)) continue; + const ext = path.extname(absVideo).toLowerCase(); + if (!isVideoExt(ext)) continue; + + const showTitleRaw = + episode.showTitle || info?.name || rootFolder || "Unknown"; + const showKey = `${episode.showId || ""}__${showTitleRaw}`; + if (!showBuckets.has(showKey)) { + showBuckets.set(showKey, { + title: showTitleRaw, + episodes: [] + }); + } + showBuckets.get(showKey).episodes.push({ + absVideo, + relPath, + season: episode.season, + episode: episode.episode, + key: episode.key + }); + } + + for (const bucket of showBuckets.values()) { + const showTitle = sanitizeWebdavSegment(bucket.title); + const uniqueShow = makeUniqueWebdavName( + showTitle, + usedShowNames, + bucket.title + ); + const showDir = path.join(WEBDAV_ROOT, categoryLabel, uniqueShow); + let createdSeasonCount = 0; + for (const entry of bucket.episodes) { + const seasonNumber = Number(entry.season); + const episodeNumber = Number(entry.episode); + const seasonLabel = Number.isFinite(seasonNumber) + ? `Season ${String(seasonNumber).padStart(2, "0")}` + : "Season"; + const seasonDir = path.join(showDir, seasonLabel); + if (createdSeasonCount === 0) { + ensureDirSync(showDir); + } + if (!fs.existsSync(seasonDir)) { + ensureDirSync(seasonDir); + createdSeasonCount += 1; + } + const ext = path.extname(entry.absVideo); + const code = + entry.key || + makeEpisodeCode(seasonNumber, episodeNumber) || + `S${String(seasonNumber || 0).padStart(2, "0")}E${String( + Number(episodeNumber) || 0 + ).padStart(2, "0")}`; + const fileName = sanitizeWebdavSegment( + `${uniqueShow} - ${code}${ext}` + ); + const linkPath = path.join(seasonDir, fileName); + createSymlinkSafe(entry.absVideo, linkPath); + } + } + } + } catch (err) { + console.warn(`⚠️ WebDAV ${categoryLabel} info.json index oluşturulamadı: ${err.message}`); + } + }; + + buildSeriesFromInfoJson("TV Shows", tvUsedShowNames, coveredTvRoots); } let webdavIndexLast = 0;