diff --git a/server/index.js b/server/index.js index b285ce1..7231319 100644 --- a/server/index.js +++ b/server/index.js @@ -54,7 +54,7 @@ const radarrRoutes = require('./routes/radarr'); const embyRoutes = require('./routes/emby'); const dashboardRoutes = require('./routes/dashboard'); const authRoutes = require('./routes/auth'); -const { startPoller, POLL_INTERVAL } = require('./utils/poller'); +const { startPoller, POLL_INTERVAL, POLLING_ENABLED } = require('./utils/poller'); const app = express(); const PORT = process.env.PORT || 3001; @@ -80,7 +80,7 @@ app.listen(PORT, () => { console.log(` sofarr - Your Downloads Dashboard`); console.log(` Server running on port ${PORT}`); console.log(` Log level: ${process.env.LOG_LEVEL || 'info'}`); - console.log(` Poll interval: ${POLL_INTERVAL}ms`); + console.log(` Polling: ${POLLING_ENABLED ? POLL_INTERVAL + 'ms' : 'disabled (on-demand)'}`); console.log(`=================================`); startPoller(); }); diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js index c1141ba..1e1d82c 100644 --- a/server/routes/dashboard.js +++ b/server/routes/dashboard.js @@ -3,6 +3,7 @@ const router = express.Router(); const { mapTorrentToDownload } = require('../utils/qbittorrent'); const cache = require('../utils/cache'); +const { pollAllServices, POLLING_ENABLED } = require('../utils/poller'); const EMBY_URL = process.env.EMBY_URL; const EMBY_API_KEY = process.env.EMBY_API_KEY; @@ -105,7 +106,14 @@ router.get('/user-downloads', async (req, res) => { const showAll = isAdmin && req.query.showAll === 'true'; console.log(`[Dashboard] Serving downloads for user: ${user.name} (${username}), isAdmin: ${isAdmin}, showAll: ${showAll}`); - // Read all data from poller cache + // When polling is disabled, fetch on-demand if cache has expired + // The fetched data is cached (30s TTL) so subsequent requests from any user reuse it + if (!POLLING_ENABLED && !cache.get('poll:sab-queue')) { + console.log(`[Dashboard] Cache expired and polling disabled, fetching on-demand...`); + await pollAllServices(); + } + + // Read all data from cache const sabQueueData = cache.get('poll:sab-queue') || { slots: [] }; const sabHistoryData = cache.get('poll:sab-history') || { slots: [] }; const sonarrTagsResults = cache.get('poll:sonarr-tags') || []; diff --git a/server/utils/poller.js b/server/utils/poller.js index 32c506e..205ad03 100644 --- a/server/utils/poller.js +++ b/server/utils/poller.js @@ -7,7 +7,11 @@ const { getRadarrInstances } = require('./config'); -const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL, 10) || 5000; +const rawPollInterval = (process.env.POLL_INTERVAL || '').toLowerCase(); +const POLL_INTERVAL = (rawPollInterval === 'off' || rawPollInterval === 'false' || rawPollInterval === 'disabled') + ? 0 + : (parseInt(process.env.POLL_INTERVAL, 10) || 5000); +const POLLING_ENABLED = POLL_INTERVAL > 0; let polling = false; @@ -125,8 +129,9 @@ async function pollAllServices() { }) ]); - // Aggregate and store in cache (TTL slightly longer than poll interval to avoid gaps) - const cacheTTL = POLL_INTERVAL * 3; + // When polling is active, TTL is 3x interval to avoid gaps between polls + // When polling is disabled (on-demand), use 30s so data refreshes on next request after expiry + const cacheTTL = POLLING_ENABLED ? POLL_INTERVAL * 3 : 30000; // SABnzbd const firstSabQueue = sabQueues[0] && sabQueues[0].data && sabQueues[0].data.queue; @@ -182,6 +187,10 @@ async function pollAllServices() { let intervalHandle = null; function startPoller() { + if (!POLLING_ENABLED) { + console.log(`[Poller] Background polling disabled (POLL_INTERVAL=${process.env.POLL_INTERVAL || 'not set'}). Data will be fetched on-demand.`); + return; + } console.log(`[Poller] Starting background poller (interval: ${POLL_INTERVAL}ms)`); // Run immediately, then on interval pollAllServices(); @@ -196,4 +205,4 @@ function stopPoller() { } } -module.exports = { startPoller, stopPoller, POLL_INTERVAL }; +module.exports = { startPoller, stopPoller, pollAllServices, POLL_INTERVAL, POLLING_ENABLED };