Compare commits

...

2 Commits

Author SHA1 Message Date
Administrator
f6d4a7de3d release: v2.2.8 2026-03-21 13:19:46 +01:00
Administrator
27905d66de 🐛 fix: shutdown countdown ignores mode change, timer leaks
Critical: handleShutdownAfterFinish() captured shutdown mode in a
closure at scheduling time — changing mode during countdown was ignored,
causing unexpected system shutdown/restart/sleep.

Now reads shutdownMode at execution time, clears timer when mode
changes to 'nothing', clears orphaned timers before creating new ones,
and adds error handling on exec() calls.

Also: guard stats timer against double-start in upload-manager.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:19:07 +01:00
3 changed files with 19 additions and 9 deletions

View File

@ -550,6 +550,7 @@ class UploadManager extends EventEmitter {
} }
_startStatsTimer() { _startStatsTimer() {
if (this.statsInterval) clearInterval(this.statsInterval);
this.statsInterval = setInterval(() => { this.statsInterval = setInterval(() => {
let globalSpeedKbs = 0; let globalSpeedKbs = 0;
let activeCount = 0; let activeCount = 0;

25
main.js
View File

@ -1258,6 +1258,11 @@ let shutdownTimer = null;
ipcMain.handle('set-shutdown-after-finish', (_event, mode) => { ipcMain.handle('set-shutdown-after-finish', (_event, mode) => {
shutdownMode = mode || 'nothing'; shutdownMode = mode || 'nothing';
// Cancel active countdown if mode changed to 'nothing'
if (shutdownMode === 'nothing' && shutdownTimer) {
clearTimeout(shutdownTimer);
shutdownTimer = null;
}
return true; return true;
}); });
@ -1278,21 +1283,25 @@ function handleShutdownAfterFinish() {
if (shutdownMode === 'nothing') return; if (shutdownMode === 'nothing') return;
const { exec } = require('child_process'); const { exec } = require('child_process');
const mode = shutdownMode;
// Notify renderer // Notify renderer
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('shutdown-countdown', { mode, seconds: 60 }); mainWindow.webContents.send('shutdown-countdown', { mode: shutdownMode, seconds: 60 });
} }
// Clear any previous countdown to prevent orphaned timers
if (shutdownTimer) clearTimeout(shutdownTimer);
shutdownTimer = setTimeout(() => { shutdownTimer = setTimeout(() => {
if (mode === 'shutdown') { // Read current mode at execution time (not captured at scheduling time)
exec('shutdown /s /t 0'); if (shutdownMode === 'shutdown') {
} else if (mode === 'restart') { exec('shutdown /s /t 0', (err) => { if (err) debugLog(`shutdown failed: ${err.message}`); });
exec('shutdown /r /t 0'); } else if (shutdownMode === 'restart') {
} else if (mode === 'sleep') { exec('shutdown /r /t 0', (err) => { if (err) debugLog(`restart failed: ${err.message}`); });
exec('rundll32.exe powrprof.dll,SetSuspendState 0,1,0'); } else if (shutdownMode === 'sleep') {
exec('rundll32.exe powrprof.dll,SetSuspendState 0,1,0', (err) => { if (err) debugLog(`sleep failed: ${err.message}`); });
} }
// else: mode was changed to 'nothing' during countdown — do nothing
}, 60000); }, 60000);
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "2.2.7", "version": "2.2.8",
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {