feat(webdav): info.json tabanlı dizi indeksleme ekle
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.
This commit is contained in:
106
server/server.js
106
server/server.js
@@ -2790,10 +2790,14 @@ function rebuildWebdavIndex() {
|
|||||||
console.warn(`⚠️ WebDAV movie index oluşturulamadı: ${err.message}`);
|
console.warn(`⚠️ WebDAV movie index oluşturulamadı: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildSeriesCategory = (dataRoot, categoryLabel) => {
|
const buildSeriesCategory = (
|
||||||
|
dataRoot,
|
||||||
|
categoryLabel,
|
||||||
|
usedShowNames,
|
||||||
|
coveredRoots
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const dirs = fs.readdirSync(dataRoot, { withFileTypes: true });
|
const dirs = fs.readdirSync(dataRoot, { withFileTypes: true });
|
||||||
const usedShowNames = new Set();
|
|
||||||
for (const dirent of dirs) {
|
for (const dirent of dirs) {
|
||||||
if (!dirent.isDirectory()) continue;
|
if (!dirent.isDirectory()) continue;
|
||||||
const seriesDir = path.join(dataRoot, dirent.name);
|
const seriesDir = path.join(dataRoot, dirent.name);
|
||||||
@@ -2806,6 +2810,7 @@ function rebuildWebdavIndex() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const { rootFolder } = parseTvSeriesKey(dirent.name);
|
const { rootFolder } = parseTvSeriesKey(dirent.name);
|
||||||
|
if (coveredRoots) coveredRoots.add(rootFolder);
|
||||||
const showTitle = sanitizeWebdavSegment(
|
const showTitle = sanitizeWebdavSegment(
|
||||||
seriesData?.name || seriesData?.title || dirent.name
|
seriesData?.name || seriesData?.title || dirent.name
|
||||||
);
|
);
|
||||||
@@ -2863,8 +2868,101 @@ function rebuildWebdavIndex() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
buildSeriesCategory(TV_DATA_ROOT, "TV Shows");
|
const tvUsedShowNames = new Set();
|
||||||
buildSeriesCategory(ANIME_DATA_ROOT, "Anime");
|
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;
|
let webdavIndexLast = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user