Implemented virutal scrolling for epg, optimized database batching, moved sync timer to server

This commit is contained in:
Trevor Mears
2026-01-04 00:46:51 -08:00
parent ca87a5f143
commit a00082b0ef
7 changed files with 293 additions and 131 deletions
+4 -2
View File
@@ -79,7 +79,9 @@ app.listen(PORT, async () => {
console.log(`NodeCast TV server running on http://localhost:${PORT}`);
// Trigger background sync with delay to allow server to settle
setTimeout(() => {
syncService.syncAll().catch(console.error);
setTimeout(async () => {
await syncService.syncAll().catch(console.error);
// Start the server-side sync timer after initial sync
await syncService.startSyncTimer().catch(console.error);
}, 5000);
});
+19
View File
@@ -1,6 +1,7 @@
const express = require('express');
const router = express.Router();
const { settings, getDefaultSettings } = require('../db');
const syncService = require('../services/syncService');
/**
* Get all settings
@@ -24,6 +25,12 @@ router.put('/', async (req, res) => {
try {
const updates = req.body;
const updatedSettings = await settings.update(updates);
// If sync interval changed, restart the server-side sync timer
if (updates.epgRefreshInterval !== undefined) {
syncService.restartSyncTimer().catch(console.error);
}
res.json(updatedSettings);
} catch (err) {
console.error('Error updating settings:', err);
@@ -53,4 +60,16 @@ router.get('/defaults', (req, res) => {
res.json(getDefaultSettings());
});
/**
* Get sync status (last sync time)
* GET /api/settings/sync-status
*/
router.get('/sync-status', (req, res) => {
const lastSyncTime = syncService.getLastSyncTime();
res.json({
lastSyncTime: lastSyncTime ? lastSyncTime.toISOString() : null
});
});
module.exports = router;
+13
View File
@@ -185,4 +185,17 @@ router.post('/:id/test', async (req, res) => {
}
});
// Global Sync - sync all enabled sources
router.post('/sync-all', async (req, res) => {
try {
// Trigger global sync (async - don't wait for completion)
syncService.syncAll().catch(console.error);
res.json({ success: true, message: 'Global sync started' });
} catch (err) {
console.error('Error starting global sync:', err);
res.status(500).json({ error: 'Failed to start global sync' });
}
});
module.exports = router;
+77 -4
View File
@@ -1,5 +1,5 @@
const { getDb } = require('../db/sqlite');
const { sources } = require('../db'); // For source config
const { sources, settings } = require('../db'); // For source config and settings
const xtreamApi = require('./xtreamApi');
const m3uParser = require('./m3uParser');
const epgParser = require('./epgParser');
@@ -8,6 +8,74 @@ const epgParser = require('./epgParser');
const activeSyncs = new Set(); // sourceId
class SyncService {
constructor() {
this.lastSyncTime = null; // Track when global sync last completed
this._syncTimer = null; // Server-side sync timer
this._currentInterval = null;
}
/**
* Get when the last global sync completed
*/
getLastSyncTime() {
return this.lastSyncTime;
}
/**
* Start the server-side sync timer based on settings
* Should be called once on server startup after initial sync
*/
async startSyncTimer() {
// Get interval from settings
const currentSettings = await settings.get();
const intervalHours = parseInt(currentSettings.epgRefreshInterval) || 24;
// If interval is 0, don't start timer (manual only mode)
if (intervalHours <= 0) {
console.log('[Sync] Auto-sync disabled (manual only mode)');
this.stopSyncTimer();
this._currentInterval = 0;
return;
}
const intervalMs = intervalHours * 60 * 60 * 1000;
// Don't restart if interval hasn't changed
if (this._currentInterval === intervalHours && this._syncTimer) {
return;
}
// Clear existing timer
this.stopSyncTimer();
console.log(`[Sync] Starting server-side sync timer: every ${intervalHours} hours`);
this._syncTimer = setInterval(async () => {
console.log('[Sync] Scheduled sync triggered');
await this.syncAll();
}, intervalMs);
this._currentInterval = intervalHours;
}
/**
* Stop the server-side sync timer
*/
stopSyncTimer() {
if (this._syncTimer) {
clearInterval(this._syncTimer);
this._syncTimer = null;
}
}
/**
* Restart the sync timer with updated settings
* Called when sync interval setting changes
*/
async restartSyncTimer() {
await this.startSyncTimer();
}
/**
* Sync all enabled sources
*/
@@ -21,7 +89,8 @@ class SyncService {
await this.syncSource(source.id);
}
}
console.log('[Sync] Global sync completed');
this.lastSyncTime = new Date();
console.log('[Sync] Global sync completed at', this.lastSyncTime.toISOString());
} catch (err) {
console.error('[Sync] Global sync failed:', err);
}
@@ -164,9 +233,11 @@ class SyncService {
}
});
const BATCH_SIZE = 500;
// Reduced batch size for better event loop interleaving
const BATCH_SIZE = 100;
for (let i = 0; i < categories.length; i += BATCH_SIZE) {
insertBatch(categories.slice(i, i + BATCH_SIZE));
// Yield to event loop between batches to allow other requests
await new Promise(resolve => setImmediate(resolve));
}
@@ -244,9 +315,11 @@ class SyncService {
}
});
const BATCH_SIZE = 500;
// Reduced batch size for better event loop interleaving
const BATCH_SIZE = 100;
for (let i = 0; i < items.length; i += BATCH_SIZE) {
insertBatch(items.slice(i, i + BATCH_SIZE));
// Yield to event loop between batches to allow other requests
await new Promise(resolve => setImmediate(resolve));
}