mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
perf(database): skip the per-launch duplicate scan and keep playlist payloads last (#1883)
deduplicateXtreamCache no longer scans the whole catalog on every launch once both unique indexes exist (7.8 s cold on a 3.9M-title library). The playlists table keeps payload as its last column (playlists-table.ts); ensurePlaylistsPayloadLast rebuilds an existing table once, keeping its indexes and triggers, foreign keys off, committing only if references referencing playlists gain no violations. On a full copy of that profile initDatabase went from 5.2-8.1 s to 39 ms and the playlist metadata read from 1.5-2 s to 22-34 ms; the one-time rebuild costs about 4 s. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
05fc8f9a2e
commit
37df7aceff
9 files changed
+741
-34
No files matched your search
@@ -37,6 +37,38 @@ function seed(sqlite: Database.Database) {
|
||||
if (columns.some(({ name }) => name === 'epg_channel_id')) {
|
||||
sqlite.exec("UPDATE content SET epg_channel_id = 'retained-epg-id'");
|
||||
}
|
||||
// An M3U source with a channel payload and every playlist column the
|
||||
// schema has: the payload-last rebuild of `playlists` must carry them all
|
||||
// across unchanged.
|
||||
const playlistColumns = new Set(
|
||||
(sqlite.pragma('table_info(playlists)') as { name: string }[]).map(
|
||||
({ name }) => name
|
||||
)
|
||||
);
|
||||
if (playlistColumns.has('payload')) {
|
||||
const values: Record<string, string | number> = {
|
||||
id: 'm3u',
|
||||
name: 'Saved list',
|
||||
type: 'm3u-url',
|
||||
url: 'https://list.invalid/a.m3u',
|
||||
payload: '{"channels":[{"name":"One"}]}',
|
||||
epg_urls: '["https://epg.invalid/a.xml"]',
|
||||
detected_epg_urls: '["https://epg.invalid/b.xml"]',
|
||||
favorites: '["one"]',
|
||||
recently_viewed: '["one"]',
|
||||
last_usage: '2026-01-01T00:00:00.000Z',
|
||||
count: 1,
|
||||
};
|
||||
const present = Object.keys(values).filter((name) =>
|
||||
playlistColumns.has(name)
|
||||
);
|
||||
sqlite
|
||||
.prepare(
|
||||
`INSERT INTO playlists (${present.join(', ')})
|
||||
VALUES (${present.map(() => '?').join(', ')})`
|
||||
)
|
||||
.run(...present.map((name) => values[name]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,6 +177,15 @@ async function main() {
|
||||
const sqlite = new Database(databasePath, { readonly: true });
|
||||
try {
|
||||
verifyCurrentSchema(sqlite);
|
||||
assert.equal(
|
||||
(
|
||||
sqlite.pragma('table_info(playlists)') as {
|
||||
name: string;
|
||||
}[]
|
||||
).at(-1)?.name,
|
||||
'payload',
|
||||
'payload must be the last playlists column'
|
||||
);
|
||||
for (const { query, rows } of before) {
|
||||
assert.deepEqual(sqlite.prepare(query).all(), rows, query);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user