From c48f2e070edc211d80c3bd6c138de621f2c16dd0 Mon Sep 17 00:00:00 2001 From: Trevor Mears Date: Fri, 2 Jan 2026 15:00:11 -0800 Subject: [PATCH] Refactored EPG refresh logic --- public/index.html | 25 ++++++---- public/js/components/EpgGuide.js | 79 +++++++++++++++++++++++++++++++- public/js/pages/Settings.js | 33 +++++++------ 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/public/index.html b/public/index.html index 3558bd2..e2f11e9 100644 --- a/public/index.html +++ b/public/index.html @@ -3,7 +3,8 @@ - + @@ -124,27 +125,29 @@
- +
@@ -322,7 +325,11 @@
-

Use the 🔄 button next to EPG sources for manual refresh

+

Use the + + button next to EPG sources for manual refresh

@@ -482,7 +489,7 @@

User Management

Manage user accounts and permissions

- +
@@ -500,7 +507,7 @@
- +

Add New User

diff --git a/public/js/components/EpgGuide.js b/public/js/components/EpgGuide.js index d539966..e6df7f2 100644 --- a/public/js/components/EpgGuide.js +++ b/public/js/components/EpgGuide.js @@ -122,11 +122,83 @@ class EpgGuide { getRefreshInterval() { // Read from server-side player settings (synced via Settings page) if (window.app?.player?.settings?.epgRefreshInterval) { - return parseInt(window.app.player.settings.epgRefreshInterval); + return parseFloat(window.app.player.settings.epgRefreshInterval); } return 24; // Default 24 hours } + /** + * Start background EPG refresh timer + * Automatically fetches fresh EPG data at the configured interval + */ + startBackgroundRefresh() { + // Clear any existing timer + this.stopBackgroundRefresh(); + + const intervalHours = this.getRefreshInterval(); + + // If interval is 0 or invalid, don't start timer (manual refresh only) + if (!intervalHours || intervalHours <= 0) { + console.log('[EPG] Background refresh disabled (manual only mode)'); + this._currentRefreshInterval = 0; + return; + } + + const intervalMs = intervalHours * 60 * 60 * 1000; // Convert hours to milliseconds + + console.log(`[EPG] Starting background refresh timer: every ${intervalHours} hours (${Math.round(intervalMs / 1000)}s)`); + + this._backgroundRefreshTimer = setInterval(async () => { + console.log('[EPG] Background refresh triggered'); + try { + await this.fetchEpgData(true); // Force refresh + this.lastRefreshTime = new Date(); + console.log('[EPG] Background refresh complete'); + + // Update channel list program info if visible + if (window.app?.channelList) { + window.app.channelList.clearProgramInfoCache(); + window.app.channelList.updateVisibleEpgInfo?.(); + } + } catch (err) { + console.error('[EPG] Background refresh failed:', err); + } + }, intervalMs); + + // Store current interval so we can detect changes + this._currentRefreshInterval = intervalHours; + } + + /** + * Stop background EPG refresh timer + */ + stopBackgroundRefresh() { + if (this._backgroundRefreshTimer) { + clearInterval(this._backgroundRefreshTimer); + this._backgroundRefreshTimer = null; + console.log('[EPG] Background refresh timer stopped'); + } + } + + /** + * Restart background refresh if interval has changed + * Called when settings change + */ + restartBackgroundRefreshIfNeeded() { + const newInterval = this.getRefreshInterval(); + if (this._currentRefreshInterval !== newInterval) { + console.log(`[EPG] Refresh interval changed: ${this._currentRefreshInterval}h -> ${newInterval}h`); + this.startBackgroundRefresh(); + } + } + + /** + * Get last refresh time for display + */ + getLastRefreshTime() { + return this.lastRefreshTime || null; + } + /** * Load EPG data (server-side caching) */ @@ -134,7 +206,12 @@ class EpgGuide { try { this.container.innerHTML = '
'; await this.fetchEpgData(forceRefresh); + this.lastRefreshTime = new Date(); this.render(); + + // Start background refresh timer after initial load + // This ensures EPG data stays fresh while the app is open + this.startBackgroundRefresh(); } catch (err) { console.error('Error loading EPG:', err); this.container.innerHTML = ` diff --git a/public/js/pages/Settings.js b/public/js/pages/Settings.js index bb2f21c..6c99b3b 100644 --- a/public/js/pages/Settings.js +++ b/public/js/pages/Settings.js @@ -19,7 +19,7 @@ class SettingsPage { // Player settings this.initPlayerSettings(); - + // User management (admin only) this.initUserManagement(); } @@ -112,10 +112,15 @@ class SettingsPage { // Load saved value from player settings epgRefreshSelect.value = this.app.player.settings.epgRefreshInterval || '24'; - // Save on change + // Save on change and restart background timer epgRefreshSelect.addEventListener('change', () => { this.app.player.settings.epgRefreshInterval = epgRefreshSelect.value; this.app.player.saveSettings(); + + // Restart EPG background refresh with new interval + if (window.app?.epgGuide) { + window.app.epgGuide.restartBackgroundRefreshIfNeeded(); + } }); } @@ -136,17 +141,17 @@ class SettingsPage { initUserManagement() { // User tab visibility is handled in show() method // when currentUser is available - + // Handle add user form const addUserForm = document.getElementById('add-user-form'); if (addUserForm) { addUserForm.addEventListener('submit', async (e) => { e.preventDefault(); - + const username = document.getElementById('new-username').value; const password = document.getElementById('new-password').value; const role = document.getElementById('new-role').value; - + try { await API.users.create({ username, password, role }); alert('User created successfully!'); @@ -162,15 +167,15 @@ class SettingsPage { async loadUsers() { const userList = document.getElementById('user-list'); if (!userList) return; - + try { const users = await API.users.getAll(); - + if (users.length === 0) { userList.innerHTML = 'No users found'; return; } - + userList.innerHTML = users.map(user => ` ${user.username} @@ -192,17 +197,17 @@ class SettingsPage { const username = prompt('Enter new username (leave blank to keep current):'); const password = prompt('Enter new password (leave blank to keep current):'); const role = prompt('Enter role (admin or viewer, leave blank to keep current):'); - + const updates = {}; if (username) updates.username = username; if (password) updates.password = password; if (role) updates.role = role; - + if (Object.keys(updates).length === 0) { alert('No changes made'); return; } - + try { await API.users.update(userId, updates); alert('User updated successfully!'); @@ -216,7 +221,7 @@ class SettingsPage { if (!confirm(`Are you sure you want to delete user "${username}"?`)) { return; } - + try { await API.users.delete(userId); alert('User deleted successfully!'); @@ -234,7 +239,7 @@ class SettingsPage { if (tabName === 'content') { this.app.sourceManager.loadContentSources(); } - + // Load users when switching to users tab if (tabName === 'users') { this.loadUsers(); @@ -249,7 +254,7 @@ class SettingsPage { usersTab.style.display = 'block'; } } - + // Load sources when page is shown await this.app.sourceManager.loadSources();