fix: m3u sync stale purge bug
This commit is contained in:
@@ -252,11 +252,17 @@ class SyncService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch save streams (channels, vod, series)
|
* Batch save streams (channels, vod, series)
|
||||||
* Also purges stale entries that no longer exist in the source
|
* Also purges stale entries that no longer exist in the source (unless skipPurge is true)
|
||||||
|
* @param {number} sourceId - Source ID
|
||||||
|
* @param {string} type - Type of items (live, movie, series)
|
||||||
|
* @param {Array} items - Items to save
|
||||||
|
* @param {Object} options - Options { skipPurge: boolean }
|
||||||
|
* @returns {Set} Set of synced IDs (for external purge if skipPurge was true)
|
||||||
*/
|
*/
|
||||||
async saveStreams(sourceId, type, items) {
|
async saveStreams(sourceId, type, items, options = {}) {
|
||||||
if (!items || items.length === 0) return;
|
if (!items || items.length === 0) return new Set();
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
|
const { skipPurge = false } = options;
|
||||||
|
|
||||||
// Collect all IDs we're syncing
|
// Collect all IDs we're syncing
|
||||||
const syncedIds = new Set();
|
const syncedIds = new Set();
|
||||||
@@ -335,33 +341,46 @@ class SyncService {
|
|||||||
await new Promise(resolve => setImmediate(resolve));
|
await new Promise(resolve => setImmediate(resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge stale entries that no longer exist in the source
|
// Purge stale entries (skip if doing batch sync like M3U)
|
||||||
// Use temp table approach to avoid SQL parameter limits with large playlists
|
if (!skipPurge && syncedIds.size > 0) {
|
||||||
if (syncedIds.size > 0) {
|
await this.purgeStaleItems(sourceId, type, syncedIds);
|
||||||
db.exec('CREATE TEMP TABLE IF NOT EXISTS synced_ids (id TEXT PRIMARY KEY)');
|
|
||||||
db.exec('DELETE FROM synced_ids');
|
|
||||||
|
|
||||||
const insertTemp = db.prepare('INSERT OR IGNORE INTO synced_ids (id) VALUES (?)');
|
|
||||||
const insertTempBatch = db.transaction((ids) => {
|
|
||||||
for (const id of ids) {
|
|
||||||
insertTemp.run(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
insertTempBatch([...syncedIds]);
|
|
||||||
|
|
||||||
const deleteStmt = db.prepare(`
|
|
||||||
DELETE FROM playlist_items
|
|
||||||
WHERE source_id = ? AND type = ?
|
|
||||||
AND id NOT IN (SELECT id FROM synced_ids)
|
|
||||||
`);
|
|
||||||
const deleted = deleteStmt.run(sourceId, type);
|
|
||||||
|
|
||||||
if (deleted.changes > 0) {
|
|
||||||
console.log(`[Sync] Purged ${deleted.changes} stale ${type} items`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[Sync] Saved ${items.length} ${type} items`);
|
console.log(`[Sync] Saved ${items.length} ${type} items`);
|
||||||
|
return syncedIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purge stale items that are no longer in the source
|
||||||
|
* @param {number} sourceId - Source ID
|
||||||
|
* @param {string} type - Type of items (live, movie, series)
|
||||||
|
* @param {Set} syncedIds - Set of IDs that should be kept
|
||||||
|
*/
|
||||||
|
async purgeStaleItems(sourceId, type, syncedIds) {
|
||||||
|
if (!syncedIds || syncedIds.size === 0) return;
|
||||||
|
|
||||||
|
const db = getDb();
|
||||||
|
db.exec('CREATE TEMP TABLE IF NOT EXISTS synced_ids (id TEXT PRIMARY KEY)');
|
||||||
|
db.exec('DELETE FROM synced_ids');
|
||||||
|
|
||||||
|
const insertTemp = db.prepare('INSERT OR IGNORE INTO synced_ids (id) VALUES (?)');
|
||||||
|
const insertTempBatch = db.transaction((ids) => {
|
||||||
|
for (const id of ids) {
|
||||||
|
insertTemp.run(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
insertTempBatch([...syncedIds]);
|
||||||
|
|
||||||
|
const deleteStmt = db.prepare(`
|
||||||
|
DELETE FROM playlist_items
|
||||||
|
WHERE source_id = ? AND type = ?
|
||||||
|
AND id NOT IN (SELECT id FROM synced_ids)
|
||||||
|
`);
|
||||||
|
const deleted = deleteStmt.run(sourceId, type);
|
||||||
|
|
||||||
|
if (deleted.changes > 0) {
|
||||||
|
console.log(`[Sync] Purged ${deleted.changes} stale ${type} items`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -489,6 +508,7 @@ class SyncService {
|
|||||||
logMemory();
|
logMemory();
|
||||||
|
|
||||||
const allGroups = new Set();
|
const allGroups = new Set();
|
||||||
|
const allSyncedIds = new Set(); // Collect IDs across all batches
|
||||||
let totalChannels = 0;
|
let totalChannels = 0;
|
||||||
let batchCount = 0;
|
let batchCount = 0;
|
||||||
|
|
||||||
@@ -506,9 +526,10 @@ class SyncService {
|
|||||||
tvgId: ch.tvgId || null,
|
tvgId: ch.tvgId || null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Save this batch immediately
|
// Save this batch immediately (skip purge - we'll do it at the end)
|
||||||
if (playlistItems.length > 0) {
|
if (playlistItems.length > 0) {
|
||||||
await this.saveStreams(source.id, 'live', playlistItems);
|
const batchIds = await this.saveStreams(source.id, 'live', playlistItems, { skipPurge: true });
|
||||||
|
batchIds.forEach(id => allSyncedIds.add(id));
|
||||||
totalChannels += playlistItems.length;
|
totalChannels += playlistItems.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,6 +546,11 @@ class SyncService {
|
|||||||
console.log(`[Sync] M3U Parsed: ${totalChannels} channels, ${allGroups.size} groups`);
|
console.log(`[Sync] M3U Parsed: ${totalChannels} channels, ${allGroups.size} groups`);
|
||||||
logMemory();
|
logMemory();
|
||||||
|
|
||||||
|
// Purge stale items after all batches are complete
|
||||||
|
if (allSyncedIds.size > 0) {
|
||||||
|
await this.purgeStaleItems(source.id, 'live', allSyncedIds);
|
||||||
|
}
|
||||||
|
|
||||||
// Save Categories (Groups) at the end
|
// Save Categories (Groups) at the end
|
||||||
const categories = Array.from(allGroups).map(name => ({
|
const categories = Array.from(allGroups).map(name => ({
|
||||||
category_id: name,
|
category_id: name,
|
||||||
|
|||||||
Reference in New Issue
Block a user