feat: run full refresh with EPG rematching

Run full live catalog refreshes through live source sync, complete channel analysis, EPG source refresh, and persisted EPG rematching while keeping check-new incremental.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
blomios
2026-10-08 00:05:00 +02:00
parent b81bc10d49
commit b3dbb3cfba
14 changed files with 404 additions and 39 deletions

View File

@ -115,6 +115,24 @@ class SyncService {
}
}
async refreshEnabledLiveSources({ rebuild = false } = {}) {
const allSources = await sources.getAll();
for (const source of allSources) {
if (!source.enabled || !['xtream', 'm3u'].includes(source.type)) continue;
if (source.type === 'xtream') {
const newIds = await this.syncXtreamLive(source);
if (rebuild) await rebuildLiveProjection(newIds);
} else {
await this.syncM3u(source, { rebuild });
}
}
}
async refreshEnabledEpgSources() {
const epgSources = await sources.getByType('epg');
for (const source of epgSources) await this.syncEpg(source);
}
/**
* Start sync for a source
*/
@ -185,17 +203,7 @@ class SyncService {
*/
async syncXtream(source) {
const api = xtreamApi.createFromSource(source);
const db = getDb();
// 1. Live Categories
console.log(`[Sync] Fetching Live Categories for ${source.name}`);
const liveCats = await api.getLiveCategories();
await this.saveCategories(source.id, 'live', liveCats);
// 2. Live Streams
console.log(`[Sync] Fetching Live Streams for ${source.name}`);
const liveStreams = await api.getLiveStreams();
const { newIds } = await this.saveStreams(source.id, 'live', liveStreams);
const newIds = await this.syncXtreamLive(source, api);
await rebuildLiveProjection(newIds);
// 3. VOD Categories
@ -229,6 +237,16 @@ class SyncService {
}
}
async syncXtreamLive(source, api = xtreamApi.createFromSource(source)) {
console.log(`[Sync] Fetching Live Categories for ${source.name}`);
const liveCats = await api.getLiveCategories();
await this.saveCategories(source.id, 'live', liveCats);
console.log(`[Sync] Fetching Live Streams for ${source.name}`);
const liveStreams = await api.getLiveStreams();
const { newIds } = await this.saveStreams(source.id, 'live', liveStreams);
return newIds;
}
/**
* Batch save categories
*/
@ -513,7 +531,7 @@ class SyncService {
* M3U Sync Logic (Streaming - Memory Efficient)
* Processes M3U files in batches to avoid OOM on large playlists
*/
async syncM3u(source) {
async syncM3u(source, { rebuild = true } = {}) {
console.log(`[Sync] Fetching M3U playlist for ${source.name}`);
// Temporary memory logging for verification
@ -578,8 +596,9 @@ class SyncService {
}));
await this.saveCategories(source.id, 'live', categories);
await rebuildLiveProjection(allNewIds);
if (rebuild) await rebuildLiveProjection(allNewIds);
console.log(`[Sync] M3U sync complete for ${source.name}`);
return allNewIds;
}
/**