feat(tv): Kanonik TVDB anahtarları ve çoklu kök klasör desteği eklendi
Birden fazla kök klasör arasında veri birleştirmeyi sağlamak için TVDB kimliklerini kullanan TV dizileri için kanonik anahtar sistemi uygulandı. Kullanıcı arayüzünde reaktif yükleme eklendi ve eski yollardan otomatik geçişle meta veri yönetimi geliştirildi. Önemli Değişiklikler: - TV dizisi veri yapısı artık dizi başına birden fazla kök klasörü destekliyor - Eski klasör anahtarları otomatik olarak kanonik TVDB anahtarlarına taşınıyor - Veritabanı şeması, rootFolders dizisi için yeni indekslerle güncellendi
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
import { connectMongo } from "./db.js";
|
||||
|
||||
const COLLECTION = "tv_data";
|
||||
const TVDB_KEY_PREFIX = "tvdb-";
|
||||
|
||||
function canonicalTvdbKey(tvdbId) {
|
||||
if (tvdbId === null || tvdbId === undefined) return null;
|
||||
return `${TVDB_KEY_PREFIX}${tvdbId}`;
|
||||
}
|
||||
|
||||
async function getCollection() {
|
||||
const { db } = await connectMongo();
|
||||
const col = db.collection(COLLECTION);
|
||||
await col.createIndex({ rootFolder: 1 });
|
||||
await col.createIndex({ tvdbId: 1 });
|
||||
await col.createIndex({ rootFolders: 1 });
|
||||
await col.createIndex({ tvdbId: 1 }, { sparse: true });
|
||||
await col.createIndex({ updatedAt: -1 });
|
||||
return col;
|
||||
}
|
||||
@@ -17,6 +24,7 @@ function buildDocument(key, rootFolder, seriesData) {
|
||||
_id: key,
|
||||
key,
|
||||
rootFolder,
|
||||
rootFolders: rootFolder ? [rootFolder] : [],
|
||||
tvdbId,
|
||||
name: seriesData?.name || null,
|
||||
data: seriesData || {},
|
||||
@@ -26,8 +34,40 @@ function buildDocument(key, rootFolder, seriesData) {
|
||||
|
||||
export async function upsertTvSeries(key, rootFolder, seriesData) {
|
||||
const col = await getCollection();
|
||||
const doc = buildDocument(key, rootFolder, seriesData);
|
||||
await col.updateOne({ _id: key }, { $set: doc }, { upsert: true });
|
||||
const tvdbId = seriesData?.id ?? seriesData?.tvdbId ?? null;
|
||||
const canonicalKey = tvdbId !== null ? canonicalTvdbKey(tvdbId) : null;
|
||||
const targetKey = canonicalKey || key;
|
||||
|
||||
const existingByTvdb =
|
||||
tvdbId !== null ? await col.findOne({ tvdbId }) : null;
|
||||
const existing =
|
||||
existingByTvdb ||
|
||||
(await col.findOne({ _id: targetKey })) ||
|
||||
(await col.findOne({ _id: key }));
|
||||
|
||||
const desiredKey = canonicalKey || existingByTvdb?._id || targetKey;
|
||||
const doc = buildDocument(
|
||||
desiredKey,
|
||||
rootFolder || existing?.rootFolder,
|
||||
seriesData
|
||||
);
|
||||
|
||||
const rootSet = new Set(existing?.rootFolders || []);
|
||||
if (existing?.rootFolder) rootSet.add(existing.rootFolder);
|
||||
if (rootFolder) rootSet.add(rootFolder);
|
||||
doc.rootFolders = Array.from(rootSet);
|
||||
doc.rootFolder = doc.rootFolder || doc.rootFolders[0] || null;
|
||||
doc.tvdbId = tvdbId;
|
||||
doc.key = canonicalKey || doc._id;
|
||||
doc._id = doc.key;
|
||||
|
||||
await col.updateOne({ _id: doc._id }, { $set: doc }, { upsert: true });
|
||||
|
||||
// Eğer eski bir anahtar farklıysa temizle
|
||||
if (existing && existing._id !== doc._id) {
|
||||
await col.deleteOne({ _id: existing._id }).catch(() => {});
|
||||
}
|
||||
|
||||
return doc.data;
|
||||
}
|
||||
|
||||
@@ -39,10 +79,15 @@ export async function getTvSeriesByKey(key) {
|
||||
|
||||
export async function getTvSeriesByRoot(rootFolder) {
|
||||
const col = await getCollection();
|
||||
const docs = await col.find({ rootFolder }).toArray();
|
||||
const docs = await col
|
||||
.find({
|
||||
$or: [{ rootFolder }, { rootFolders: rootFolder }]
|
||||
})
|
||||
.toArray();
|
||||
return docs.map((doc) => ({
|
||||
key: doc.key,
|
||||
rootFolder: doc.rootFolder,
|
||||
rootFolders: doc.rootFolders || [],
|
||||
data: doc.data
|
||||
}));
|
||||
}
|
||||
@@ -53,13 +98,19 @@ export async function listAllTvSeries() {
|
||||
return docs.map((doc) => ({
|
||||
key: doc.key,
|
||||
rootFolder: doc.rootFolder,
|
||||
rootFolders: doc.rootFolders || [],
|
||||
data: doc.data
|
||||
}));
|
||||
}
|
||||
|
||||
export async function listTvSeriesKeysForRoot(rootFolder) {
|
||||
const col = await getCollection();
|
||||
const docs = await col.find({ rootFolder }).project({ key: 1 }).toArray();
|
||||
const docs = await col
|
||||
.find({
|
||||
$or: [{ rootFolder }, { rootFolders: rootFolder }]
|
||||
})
|
||||
.project({ key: 1 })
|
||||
.toArray();
|
||||
return docs.map((d) => d.key).filter(Boolean);
|
||||
}
|
||||
|
||||
@@ -70,5 +121,34 @@ export async function removeTvSeriesByKey(key) {
|
||||
|
||||
export async function removeTvSeriesByRoot(rootFolder) {
|
||||
const col = await getCollection();
|
||||
await col.deleteMany({ rootFolder });
|
||||
const cursor = col.find({
|
||||
$or: [{ rootFolder }, { rootFolders: rootFolder }]
|
||||
});
|
||||
|
||||
// Silmek yerine root'u listeden çıkar; boş kalırsa kaydı kaldır
|
||||
// Not: cursor.forEach async callback desteklemez, manual loop
|
||||
while (await cursor.hasNext()) {
|
||||
const doc = await cursor.next();
|
||||
const roots = new Set(doc.rootFolders || []);
|
||||
if (doc.rootFolder) roots.add(doc.rootFolder);
|
||||
roots.delete(rootFolder);
|
||||
|
||||
if (roots.size === 0) {
|
||||
await col.deleteOne({ _id: doc._id });
|
||||
} else {
|
||||
const nextRootFolder = Array.from(roots)[0];
|
||||
await col.updateOne(
|
||||
{ _id: doc._id },
|
||||
{
|
||||
$set: {
|
||||
rootFolder: nextRootFolder,
|
||||
rootFolders: Array.from(roots),
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { canonicalTvdbKey };
|
||||
|
||||
Reference in New Issue
Block a user