• v3.3.57 b93617ace9

    Administrator released this 2026-06-09 05:03:05 +02:00 | 115 commits to master since this release

    Right-click 'Ausgewählte starten' / queue-toolbar 'Selected starten' now actually works during an active upload, not just on idle queue.

    Bug: the in-batch add path filtered selected jobs by a hardcoded ['queued','error','aborted','skipped'] — missing 'preview'. Newly added files (and existing untouched rows) have status='preview', so during an active batch the addable list was empty and the action silently did nothing. The idle-start path used the broader STARTABLE_QUEUE_STATUSES set which DOES include 'preview', so it worked when no upload was running.

    Fix:

    • in-batch add path now uses the same isStartableQueueStatus() helper as the idle path. 'preview' is included consistently.
    • If the selection has no startable jobs (e.g. everything is already running or done), the action surfaces a toast 'Keine startbaren Jobs ausgewählt' instead of failing silently.
    Downloads
  • v3.3.55 f0fb5f881f

    Administrator released this 2026-06-08 23:04:14 +02:00 | 119 commits to master since this release

    ETA in the status bar now factors in queued/waiting jobs, not just the ones currently uploading.

    Cause: files added via Datei/Ordner-Picker or via folder drag-drop arrived in the renderer with size=null because walkFolderAsync only returned paths. Their queue entries got bytesTotal=0. The ETA computation correctly walked bytesRemaining, but each waiting job contributed 0, so the ETA only reflected the bytes left in the actively-uploading rows.

    Fix:

    • walkFolderAsync now fs.stat each file as it walks (the cost is amortized into the existing setImmediate yield pacing — still non-blocking on the main thread).
    • New select-folder-with-sizes IPC + get-file-sizes IPC for paths-only legacy callers to hydrate.
    • pickFolder() prefers select-folder-with-sizes; addPathsToQueue() handles both string[] and {path,name,size}[] shapes and fires a fire-and-forget get-file-sizes for any size==0 entries (drag-drop folder path or stale legacy returns).
    • When sizes arrive late, queueJobs.bytesTotal is patched in place and the queue + status bar re-render. ETA jumps to the realistic 'all-queue' estimate.

    This is why your 4 active uploads showed ETA 00:53 with 432 more waiting — only the active 4 had bytesTotal, the remaining 432 contributed 0 bytes to the calc. Now the ETA reflects the full queue.

    Downloads
  • v3.3.54 127807d62a

    Administrator released this 2026-06-08 22:03:41 +02:00 | 121 commits to master since this release

    CRITICAL hotfix for 3.3.53 + 7 bugs from the latest audit (42 confirmed findings).

    CRITICAL:

    1. safeSend() in 3.3.53 called itself instead of webContents.send — infinite recursion every IPC send. Bulk-replace script accidentally matched the safeSend implementation itself. This blocked all main→renderer IPC (progress, stats, rotation events, batch-done, …) and triggered RangeError stack overflows that could crash the main process. Fixed by restoring the inner call to mainWindow.webContents.send.

    2. queueMicrotask() reference in renderer/app.js _computeQueueStats — not defined in all Chromium contexts and threw ReferenceError on every stats compute (during active uploads), bubbling up through the rAF queue. Replaced with feature-detect fallback: queueMicrotask || Promise.resolve().then.

    Other bugs fixed in this release:
    3. Account edit silently lost when the account was removed between modal open and save (findIndex returned -1, code skipped without error). Now shows 'Account nicht mehr in der Config' and aborts.
    4. deleteAccount saveConfig was fire-and-forget with empty .catch() — failures swallowed. Now logs + toasts.
    5. Vidmoly file-list recovery fallback 'one new file → take it' could claim job A's file as job B's result during parallel uploads. Removed; exact title match only (same as the byse fix in 3.3.x).
    6. config-store .bak refresh wrote any non-empty file as backup, including a freshly-corrupted live file. Now JSON.parse + structure validation before refreshing .bak — a corrupt live file no longer poisons the backup.
    7. Chunked startBatch loops (3.3.48) didn't check signal.aborted between chunks. Cancel during a 5000-job batch kept spawning 100 more _runJobs per chunk gap. Added abort check at the top of each chunk.
    8. before-quit: tray destruction was missing — Windows could leave a dead tray icon visible after the app process exited. Added tray.destroy() in before-quit. Also wrapped destroyDropTargetWindow() in try/catch.
    9. Renderer init() failure now shows a top-of-page banner with the error + asks for diagnose-paket instead of leaving the user on a blank window.

    Downloads
  • v3.3.53 ddf2710fc6

    Administrator released this 2026-06-08 21:28:34 +02:00 | 123 commits to master since this release

    Stability hardening from the 4-agent crash audit (19 HIGH + 9 MEDIUM findings, 35 confirmed). Implements the highest-impact ones:

    1. Hot-path timer callbacks now wrapped in try/catch. setInterval bodies (speedMonitor, statsInterval) and the per-byte progressCb closure used to let any throw escape into Node's unhandled-exception path → silent process death. Each is now bounded; a throw is rotLog'd and the timer keeps running.

    2. Main process event listeners (uploadManager 'progress', 'stats', 'rot-log') wrapped in try/catch with object-shape validation. JSON.stringify in the rot-log entries → pairs encoder now per-key try/catch to survive a circular ref or non-serializable value.

    3. New safeSend(channel, data) wrapper used for all 15 channels of main → renderer IPC. Guards mainWindow.isDestroyed() + try/catch so a racing window-close during a webContents.send() doesn't bubble.

    4. Renderer: handleProgress and handleStats wrapped in try/catch — any throw inside the IPC-driven path is logged via window.api.debugLog and the renderer event loop survives.

    5. Auto-updater no longer calls app.quit() unconditionally 900ms after launching the installer. It now polls globalThis._mhuUploadManagerRef.getActiveJobCount() every 3s and only quits when 0 active uploads (or after a 30 min safety timeout). Prevents the 'app vanished mid-upload' scenario if the user clicked install and a background batch was still running.

    6. crash.log is now collected in the Support Bundle next to debug.log + account-rotation.log + doodstream-debug.log + fileuploader.log.

    Downloads
  • v3.3.52 f0608dcda1

    Administrator released this 2026-06-08 21:19:19 +02:00 | 125 commits to master since this release

    Crash instrumentation. The app was silently exiting during long 5-hoster batches and leaving no breadcrumb. Five gaps closed:

    1. process.on('uncaughtException', ...) added — synchronous throws in the main process are now logged to debug.log AND a dedicated crash.log instead of terminating the process silently. Same for any signal received (SIGINT/SIGTERM/SIGHUP/SIGBREAK) and process exit.

    2. mainWindow.webContents.on('render-process-gone', ...) added — if the renderer dies (OOM at thousands-of-queue-rows × hours of upload, GPU crash, native crash), the reason + exitCode are written to crash.log and a dialog asks whether to reload the UI (uploads in main keep running) or quit. Without this, render-process-gone → window-all-closed → silent app.quit().

    3. webContents.on('unresponsive' / 'responsive' / 'did-fail-load') added — captures hung renderer and load failures.

    4. app.on('child-process-gone', ...) added — captures GPU/utility process death.

    5. Renderer: window.addEventListener('error', …) + 'unhandledrejection' forward every uncaught throw + rejection back to main's debug.log via the existing debugLog IPC, so renderer-side bugs are now visible in the support bundle.

    Plus app.on('window-all-closed', …) now logs activeJobs count + uploadManager presence to crash.log before quitting — so the next time the app dies, we'll see exactly which subsystem state it was in. New 'crash.log' lands next to debug.log and is included in the support bundle.

    Downloads
  • v3.3.51 d159ac484a

    Administrator released this 2026-06-08 19:22:54 +02:00 | 127 commits to master since this release

    Files panel + history table sort headers: the first click on any column header now sets the natural default direction for that column (desc for date, asc for text) instead of toggling away from it.

    Why: the initial sort state was already 'date|desc' (the intuitive 'newest first' order for the Files panel and history table). The click handler treated this as 'user clicked the already-selected column → toggle direction', so the first time the user clicked Datum they actually flipped to ascending — new uploads then landed at the bottom, out of view. The user then had to click Datum a second time to flip back to descending and see them at the top.

    Fix: track whether the user has explicitly clicked a sort header during this session. On the first click on any header (or any click that changes the column), set the column's natural default direction. Only on a subsequent click on the same already-active column does the direction toggle. The arrow indicator already reflects the state, so the second click for explicit toggle still works as expected.

    Applied to both the Files panel (lower 'recent uploads' panel) and the Verlauf tab's history table.

    Downloads
  • v3.3.49 8d33141294

    Administrator released this 2026-06-08 03:04:25 +02:00 | 131 commits to master since this release

    Accounts prüfen button no longer silently fails. Two changes to the runHealthCheck guard:

    1. The 'uploading && mode === manual' block is removed — health checks already use their own per-hoster clients (VoeUploader, DoodstreamUploader, …) with independent cookie jars and don't share state with the upload manager, so running both concurrently is safe. The 3.3.39 per-hoster serialization for VOE still applies, so concurrent health checks won't trigger the CSRF burst that originally motivated the guard.

    2. When a health check is already in flight and the user clicks the button again, the function now surfaces a 'Account-Check läuft bereits.' toast instead of returning silently — the previous behavior left the user clicking with no visible feedback.

    Downloads
  • v3.3.48 f9aa7f4168

    Administrator released this 2026-06-08 01:30:19 +02:00 | 133 commits to master since this release

    Third + most targeted fix for the Start-button freeze. Two changes:

    1. startBatch was synchronous from rotLog('batch-start') through the entire 5365-task dedup loop AND the 5365 _runJob promise spawn loop before hitting the first 'await Promise.allSettled'. With no event-loop yield, the main process couldn't service the IPC reply to the renderer's await window.api.startUpload() — so the renderer sat 'Not Responding' for as long as it took main to plow through the whole synchronous prelude (30-60 seconds at this scale). Both loops now yield via 'await new Promise(setImmediate)' every 200 tasks (dedup) and every 100 tasks (spawn), letting the IPC reply, OS messages, and incoming progress events flow during the spawn.

    2. rotLog was a synchronous fs.appendFileSync per call. Each rot-log event (~30-50 in the startup burst: batch-start + per-job upload-start + pre-resolved fallback + …) blocked the main thread 5-20ms. Now it pushes into the existing _rotLogBuffer and reuses the same async flush path that debugLog already uses — same on-disk format, same file targets, just unblocking.

    Downloads
  • v3.3.47 ba4642e09a

    Administrator released this 2026-06-07 21:11:53 +02:00 | 135 commits to master since this release

    Four HIGH-impact perf fixes from the audit:

    1. Per-batch baseline pre-fetch for byse + doodstream. Until now uploadFile fetched a 100-item file list per JOB to seed its post-upload poller — at 600 byse jobs that was 600 HTTPS roundtrips to api.byse.sx instead of 1 per (hoster, apiKey). Upload-manager now resolves the baseline once via prefetchBaseline, caches the Promise per (hoster, apiKey) for the batch, and passes the Set into uploadFile via opts.{byseBaseline,doodBaseline}. Concurrent jobs share the in-flight Promise — no duplicate fetches even if they start in the same tick.

    2. Async folder walk. select-folder + resolve-folder-files used to do fs.readdirSync recursively on the main process, blocking the entire IPC bus on deep folder trees. Now walks asynchronously with fs.promises.readdir + an explicit yield via setImmediate every 8 directories so the event loop stays responsive even on tens of thousands of files.

    3. renderHistoryTable fast path. The forEach + 4× escapeHtml + template-string per row was the dominant cost when switching to Verlauf with thousands of entries. Replaced with a single-array string-builder that pushes pre-computed escaped values — same DOM, no behavior change, just less GC pressure and no V8 deopt from the template-literal compose.

    4. Progress IPC batching at main. Per-job progress emits (30 active jobs × 4Hz = 120 IPC msgs/sec) are now coalesced at main into 'upload-progress-batch' events flushed every 100ms — deduped by jobId, terminal events (done/error/aborted/skipped) preserved in order. Renderer handles both the new batch event and the legacy single event, so older preload + renderer combinations keep working. Effective IPC volume drops from ~120/s to ~10/s during active uploads.

    Downloads
  • v3.3.46 4bb18f7abc

    Administrator released this 2026-06-07 20:59:34 +02:00 | 137 commits to master since this release

    Second hotfix for the Start-button freeze with 2000+ queued jobs. Three changes that compound to remove the freeze entirely:

    1. Renderer no longer fires window.api.debugLog (an IPC roundtrip back to main) on every received upload-progress / upload-stats / upload-batch-done event. With 3000 initial queued events, that was 3000 sync-ish IPC invokes flooding main's debug-log handler queue and blocking the renderer event loop for ~30 seconds (visible as 'Not Responding' in the title bar).

    2. Upload-manager no longer emits the redundant initial 'queued' progress event per job. The renderer already holds each job in 'queued'/'preview' state from its own queueJobs array; the first event it actually needs from main is the 'getting-server' / 'uploading' transition for the jobs the semaphore lets through. With 3000 tasks this was 3000 main → renderer IPC sends back-to-back at startBatch.

    3. fileSize is now reused from the per-file cache populated in startBatch (line 315) instead of re-statting in every _runJob call. That removes a redundant fs.statSync per task on the startBatch hot path.

    Downloads