/upload/complete was failing (non-JSON response, missing fileId, or
post-processing timeout) after all bytes were already on the server,
causing upload-manager to retry the entire multi-GB upload — which
corrupts the server-side file since two uploads end up interleaved.
Now /complete failures are swallowed and sessionId is used as the
file_code fallback. Upload is considered done once all chunks are in.
Upload completes on server but file is still being processed, so
share-link fails. Retry up to 6x with backoff; on final failure, use
fileId-based fallback URL instead of throwing — prevents upload-manager
from retrying the entire multi-GB upload.
Previously, clicking 'Ausgewählte starten' on 'Wartet' jobs during an
active upload just showed a toast. But the jobs might NOT actually be
in the batch (skipped during task building).
Now: ALL selected queued/error/aborted jobs are sent to addJobsToBatch.
The upload-manager has duplicate protection (checks jobAbortControllers)
so jobs already in the batch are skipped. Jobs NOT in the batch get
added and start uploading immediately.
Toast now shows exact counts: "X hinzugefügt, Y waren schon im Batch"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously addJobs() was fire-and-forget — added jobs ran as orphaned
promises. When the original batch completed, batch-done fired and
uploadManager was set to null while added jobs were still running.
Now: added job promises are tracked in _additionalPromises array.
startBatch drains this array after the original tasks complete,
ensuring batch-done only fires when ALL jobs (original + added) finish.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, 'Erneut versuchen' and 'Ausgewählte starten' did nothing
when a batch was already running (uploading=true). Failed jobs were
set to 'Wartet' but never actually uploaded because they couldn't be
added to the running batch.
New: upload-manager.addJobs() allows adding tasks to a running batch.
When a batch is active and user retries/starts jobs, they're injected
into the running batch via IPC 'add-jobs-to-batch'. The upload manager
starts processing them immediately using the existing semaphores.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When Account A failed, only the first file got the fallback to Account B.
All subsequent files in the same batch still tried Account A (wasting
all retries), then skipped fallback because _failedAccounts already
had the key.
Now: before the retry loop, each job checks if its account is already
known-failed and immediately switches to the fallback account, avoiding
wasted retries on a known-bad account.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- hosters.js apiGet(): fixed regression from v2.3.8 where res.json()
consumed the body, making res.text() return empty on parse failure.
Now reads as text first, then parses JSON (matching VOE fix pattern).
- updater.js fetchJson(): same fix — read text first, parse JSON,
show actual server response in error message on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- apiGet(): wrap res.json() in try-catch with descriptive error
message when server returns HTML instead of JSON
- URL-encode apiKey in upload server lookup URL template
(prevents broken URLs if key contains +, &, = chars)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, both missing files (fs.statSync throws) and 0-byte files
produced the same error "Datei ist leer (0 Bytes)". Now:
- Missing files: "Datei nicht gefunden"
- Empty files: "Datei ist leer (0 Bytes)"
Also adds 3 edge case tests (throttle consume(0), unlimited rate,
semaphore release-without-acquire). All 66 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix 3 failing config-store tests: update expectations to match
multi-account array format (tests passed with old single-object format)
- backup-crypto: reject empty/null passwords on encrypt+decrypt
instead of producing weak keys silently
- updater: validate assetUrl and assetName before downloading
to prevent crash on incomplete update metadata
All 59 tests now passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- scaleParallelUploads used Math.max instead of Math.min, causing MORE
concurrent uploads instead of limiting them to the global count
- Settings debounce (350ms) was not flushed on app close — user changes
made right before closing were lost
- onRemoteClientCount IPC listener was re-registered on every
renderSettings() call, causing listener accumulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Folder monitor: clear _seenFiles entry on file unlink so re-added
files (e.g. re-encoded) are detected again
- Sync IPC save (beforeunload): use atomic write pattern with backup
(.bak) creation, matching the async _atomicWrite behavior
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
- batch-done handler: appendHistory failure no longer prevents the
upload-batch-done event from reaching the renderer (UI would get stuck)
- remote:input-event: validate x/y as finite numbers before passing
to sendInputEvent (prevents NaN/Infinity crash)
- VOE upload server: wrap JSON.parse in try-catch with clear error
message instead of raw stack trace
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Upload manager now rejects empty files (0 bytes) with clear error
message instead of sending useless uploads to the server
- Fix drag-drop zone highlight flickering caused by dragleave firing
on child elements (classic browser bug, fixed with enter/leave counter)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The error handler was missing clearTimeout for the auth timeout timer
and didn't clean up authenticated client state (signaling disconnect,
destroying capture window when last client drops).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Upload button no longer gets permanently stuck if startUpload()
throws after health check (try-catch with uploading=false reset)
- Wait for running health check instead of silently blocking upload
- Add abort signal check in VOE/Vidmoly upload generators
- Escape filenames with quotes/backslashes in multipart form headers
(all 4 uploaders: doodstream, voe, vidmoly, byse)
- Validate backup import structure before overwriting config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Config write serialization via _writeQueue prevents concurrent
read-modify-write races between settings/queue/history saves
- Cancel active uploads on app quit (prevents zombie processes)
- Persist queue before update install (prevents queue loss)
- Sync IPC save in beforeunload (guarantees save before close)
- Fix double configStore.load() call
- Guard against status regression in handleProgress (done→uploading)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When Doodstream requires 2FA, the account modal now dynamically
shows an OTP input field so the user can enter the code from
their email and complete the login without restarting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove restrictive resolution constraints, capture at native res
- Account for window frame/title bar when mapping click coordinates
(capture includes title bar but sendInputEvent is content-relative)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If getCaptureStream fails, send error back through WebSocket so it
appears in proxy logs for diagnosis.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Capture window logs now forwarded to main process via IPC to diagnose
why video tracks are missing from the WebRTC answer SDP.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RTCSessionDescription and RTCIceCandidate objects lose their properties
when sent through Electron's contextBridge IPC. Convert to plain objects
with explicit property extraction before sending.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- desktopCapturer now searches window+screen types with fallbacks
- Partial title match and screen fallback if exact match fails
- Error messages sent back from capture window via IPC
- Detailed logging for capture source selection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Multiple accounts per hoster with drag-sortable priority (primary + fallbacks)
- Separate account types: Web Login and API selectable per hoster
- Account fallback: after all retries fail, automatically switches to next fallback account
- Fix: Byse health check returning [Fehler] OK when API responds with msg "OK"
- Fix: retry during active upload sets status to "Wartet" instead of "Bereit"
- Config migration from single-object to multi-account array format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Small always-on-top drop target window (toggle in Settings > Allgemein)
- Files dropped on it get added to the queue with hoster modal
- Auto-shows on app start if previously enabled
- Column headers now in English (Filename, Uploaded/Size, Progress)
- Statusbar labels in English (Connections, Total)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hoster pre-selection in Ordnerüberwachung settings (only configured accounts shown)
- With preset hosters: files go directly to queue without modal
- Without preset: hoster modal opens as before
- Fix: Aktiv badge now green on initial render
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New FolderMonitor class with chokidar for watching folders
- Settings UI panel with all options (extensions filter, recursive, auto-start, skip duplicates)
- Auto-queue and auto-upload when files appear in monitored folder
- Fix statusbar to show uploaded/remaining instead of cumulative session bytes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New "Neues Log pro Session" checkbox in settings. When enabled,
each app session creates a separate log file with timestamp
(e.g. fileuploader-2026-03-11_20-30-15.log). File is only created
when an upload actually completes. When disabled, behaves as before
(single appending log file).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Status bar shows uploaded/total bytes (e.g. "16 GB / 281 GB")
Total is sum of all queue jobs (100GB x 4 hosters = 400GB)
- Fix semaphore acquisition order: hoster-first then global prevents
jobs waiting on a hoster slot from wasting global semaphore slots,
significantly increasing active connection utilization
- Context menu: dynamic count on all labels, singular/plural for
single selection, user-adjusted grouping with separators
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Single atomic write instead of two-phase (prevents split state on crash)
- Timestamped pre-import backup (multiple imports don't overwrite safety net)
- Fix UI refresh: correct function names + refresh globalSettings/alwaysOnTop
- Zero sensitive buffers (key, plaintext, decrypted) after use
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AES-256-GCM + PBKDF2 encrypted config backup (.mhu files).
Export/import all accounts, settings, and history.
Pre-import safety backup of current config.
Password modal with confirmation for export.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Guard startBatch against null uploadManager in nextTick (race on fast cancel)
- Fix updateSettings not creating globalThrottle when none existed at start
- Fix updateSettings not updating globalSemaphore limit live
- Fix retry pause: 2500ms → 3000ms as intended
- Remove dead isError code in history (was always false after continue)
- Add signal.aborted check in API upload generator (hosters.js)
- Add extra signal check in throttle consume loop for faster abort
- Fix doodstream debug log path (process.cwd → __dirname)
- Fix updater fetchJson signal listener leak
- Make progress column sortable in queue table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- All config writes now go through _atomicWrite() (write to .tmp, backup
to .bak, rename .tmp to main config)
- load() falls back to .bak if main config is empty or corrupt
- Prevents 0KB config files caused by process termination during write
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Checks alternate AppData folder names and portable exe directory
to find existing config when current path has no config file.
Prevents losing accounts, settings, and queue after updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously all jobs read the same lastStartTime simultaneously,
causing them all to start at once. Now uses a per-hoster promise
chain to ensure each job waits its turn.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>