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

@ -108,8 +108,8 @@ class SettingsPage {
initLiveAnalysis() {
document.getElementById('live-check-new-button')?.addEventListener('click', () => void this.runLiveAnalysis('check-new'));
document.getElementById('live-rebuild-catalog-button')?.addEventListener('click', () => {
if (confirm('Rebuild the entire channel catalog? This can take a while.')) void this.runLiveAnalysis('rebuild');
document.getElementById('live-rebuild-catalog-button')?.addEventListener('click', async () => {
if (await this.confirmFullRefresh()) void this.runLiveAnalysis('rebuild');
});
// Compatibility with pages cached before the actions were split.
document.getElementById('live-analysis-button')?.addEventListener('click', () => void this.runLiveAnalysis('rebuild'));
@ -153,6 +153,14 @@ class SettingsPage {
const presentation = LiveAnalysisPresentation.present(status);
buttons.forEach(button => { button.disabled = presentation.busy; });
const rebuildButton = document.getElementById('live-rebuild-catalog-button');
const fullRefreshPhases = new Set([
'refreshing_live_sources', 'refreshing_epg_sources', 'matching_epg',
'laya_epg_matching', 'writing_epg_matches'
]);
const fullRefreshBusy = presentation.busy &&
(this.currentLiveAnalysisAction === 'rebuild' || fullRefreshPhases.has(presentation.phase));
if (rebuildButton) rebuildButton.textContent = fullRefreshBusy ? 'Refreshing…' : 'Run full refresh';
statusElement.textContent = presentation.statusLabel;
progress.hidden = !presentation.busy;
track.classList.toggle('indeterminate', presentation.progressIndeterminate);
@ -181,6 +189,7 @@ class SettingsPage {
const buttons = ['live-check-new-button', 'live-rebuild-catalog-button', 'live-analysis-button']
.map(id => document.getElementById(id)).filter(Boolean);
if (buttons.some(button => button.disabled)) return;
this.currentLiveAnalysisAction = action;
this.liveAnalysisStatus = { ...(this.liveAnalysisStatus || {}), status: 'queued', processed: 0 };
this.renderLiveAnalysisStatus(this.liveAnalysisStatus);
try {
@ -194,10 +203,39 @@ class SettingsPage {
this.startLiveAnalysisPolling();
} catch (err) {
this.liveAnalysisStatus = { status: 'error', error: err.message };
this.currentLiveAnalysisAction = '';
this.renderLiveAnalysisStatus(this.liveAnalysisStatus);
}
}
confirmFullRefresh() {
const titleText = 'Run a full refresh?';
const bodyText = 'Nodecast will refresh IPTV sources, re-analyze every channel and language, refresh EPG data, and rebuild all EPG matches. This may take several minutes.';
const modal = document.getElementById('modal');
const title = document.getElementById('modal-title');
const body = document.getElementById('modal-body');
const footer = document.getElementById('modal-footer');
const close = modal?.querySelector('.modal-close');
if (!modal || !title || !body || !footer || !close) {
return Promise.resolve(confirm(`${titleText}\n\n${bodyText}`));
}
return new Promise(resolve => {
title.textContent = titleText;
body.textContent = bodyText;
footer.innerHTML = '<button type="button" class="btn btn-secondary" id="full-refresh-cancel">Cancel</button>' +
'<button type="button" class="btn btn-primary" id="full-refresh-confirm">Start full refresh</button>';
modal.classList.add('active');
const finish = confirmed => {
modal.classList.remove('active');
close.onclick = null;
resolve(confirmed);
};
close.onclick = () => finish(false);
document.getElementById('full-refresh-cancel').onclick = () => finish(false);
document.getElementById('full-refresh-confirm').onclick = () => finish(true);
});
}
startLiveAnalysisPolling() {
if (this.liveAnalysisPollTimer) return;
this.liveAnalysisPollTimer = setInterval(async () => {
@ -211,6 +249,7 @@ class SettingsPage {
this.liveAnalysisPollTimer = null;
if (presentation.complete) {
await this.app.channelList?.loadChannels();
this.currentLiveAnalysisAction = '';
this.liveAnalysisStatus = status;
this.renderLiveAnalysisStatus(status);
}