buildQueuePreview() was re-creating removed jobs because they weren't
in _completedUploadKeys. Now log-imported file+hoster combos are added
to _completedUploadKeys so they stay removed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New 'Log importieren' button in queue actions. Opens file picker for
.log/.txt files, parses the fileuploader.log format:
date|hoster|link||filename|
Matches each log entry against queue jobs by filename+hoster (case-
insensitive). Removes matching jobs that are already uploaded,
shows toast with count.
Use case: after a crash/restart, import the log from a previous
session to skip files that were already successfully uploaded.
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 buildUploadTasksFromJobs skips jobs (e.g. no valid account for
a hoster), the main process now returns their IDs + reason. The
renderer marks them as 'error' with a descriptive message instead of
leaving them stuck in 'Wartet' (queued) status with no feedback.
Previously: jobs silently stayed at 'Wartet' forever if their hoster
had no configured/enabled account. User had no idea why they weren't
uploading.
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>
ESLint prefer-const auto-fix: 12 variables changed from let to const
where the reference is never reassigned (Maps, Sets, sort state objects).
All tools clean:
- ESLint: 0 errors, 0 warnings
- Tests: 70/70 pass
- npm audit (runtime): 0 vulnerabilities
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, 'Ausgewählte starten' only picked up jobs with status
'preview' or 'queued', silently ignoring failed/aborted/skipped jobs.
Users had to click 'Erneut versuchen' separately first.
Now it resets error/aborted/skipped jobs to 'queued' and starts them
in one click — combining retry + start into a single action.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ctrl+A now properly respects which panel the user last clicked:
- Click in queue table → Ctrl+A selects all queue jobs
- Click in recent files panel → Ctrl+A selects all recent files
- Clicking one panel clears the other panel's selection
Previously, if any recent file was ever selected, Ctrl+A would
always select recent files even when the user was working in the queue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, the auto health check before upload would block with an
alert dialog if any hoster check failed (e.g. "byse.sx: fetch failed"),
preventing the upload from starting entirely.
Now the upload starts immediately regardless of health check results.
The startup account check still runs in the background on app launch.
Failed hosters will naturally retry during the actual upload via the
existing retry/fallback mechanism in upload-manager.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
'Hoster entfernen' already cancels active uploads AND removes jobs.
The separate 'doodstream.com abbrechen' etc. items were redundant
and confused users with two ways to do the same thing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_completedUploadKeys tracked done uploads to prevent phantom preview
jobs when removeFromQueueOnDone auto-removes them. But when user
EXPLICITLY deleted a completed job from queue, the key remained —
silently blocking re-upload of the same file+hoster combination.
Now clears the completed key in removeJobFromIndex so deleted files
can be re-added. Safe with removeFromQueueOnDone because
syncSelectedFilesFromQueue runs before next buildQueuePreview.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clearHistory() was the only configStore write call not awaited in its
IPC handler. The renderer received 'success' before the file write
completed — closing the app immediately after could leave history intact.
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>
- throttle: consume(0) resolves immediately
- throttle: updateRate(0) makes consume instant (unlimited)
- semaphore: release without acquire clamps active to 0
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>
Consistent with all other user-data HTML attribute insertions
in the codebase that use escapeAttr().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>