Commit Graph

97 Commits

Author SHA1 Message Date
Administrator
126b1e569a feat(account-rotation): dedicated logging + live toast notifications
To trace whether the fallback chain actually engages during real uploads,
every rotation decision now emits a structured 'rot-log' event from the
upload-manager. main.js persists each event to a new account-rotation.log
(same directory as fileuploader.log; falls back to Desktop then userData)
and also mirrors it into the main debug log with a [ROT] prefix for
single-file grepping.

Logged events:
  - batch-start (clears _failedAccounts / _accountOverrides)
  - pre-job-swap / pre-job-swap-blocked (job picks override before first try)
  - retries-exhausted / mark-failed (enters rotation loop)
  - rotate (switched to new account, retry starting)
  - rotation-end (no override / override already failed)
  - final-error (all accounts exhausted)
  - switchAccount (main resolved the next fallback)

The renderer shows a toast on 'rotate', 'rotation-end' and 'final-error'
so fallback behavior is visible live instead of buried in logs.
2026-04-19 22:57:19 +02:00
Administrator
9c679bd442 perf(accounts): event delegation + in-place card updates
The Accounts view rebuilt the whole list on every enable/disable/
check/reorder. Each render destroyed and recreated four click
listeners plus five drag listeners per card (20 accounts = 180
listeners cycled per click), then ran an IPC getConfig round-trip
on top. Typing-fast enable/disable toggles felt sludgy.

  - Single delegated click handler on the accounts container.
  - Single delegated set of drag/drop handlers (one per event type,
    not per card).
  - Listeners are bound once on first render, never rebound.
  - updateAccountCard(accountId) swaps just the one affected card's
    DOM node when its state changes. toggleAccount / checkSingleAccount
    use that instead of calling renderAccounts.
  - Drag-and-drop reorder moves the DOM node in place and re-renders
    only the priority badges of the affected group — no container
    rebuild, no getConfig refetch.
2026-04-19 22:49:11 +02:00
Administrator
2dc94084ab fix: vidmoly login verification + retry stale-uploadId + faster account toggle
Three fixes bundled:

  - Vidmoly redesign broke login: the old check required either the
    'login' or 'xfsts' cookie, but the new site sets different cookie
    names. Now we verify by fetching /?op=my_account and looking for
    logged-in markers (Logout / My Account / My Files) in the body
    instead of relying on specific cookie names.

  - retrySelectedJobs left the stale uploadId in _jobIndexByUploadId
    when resetting a job. A late 'aborted'/'error' event from the
    original (cancelled) upload could route back to the reset job
    and overwrite its 'preview' state. Now the old uploadId is
    removed from the index and marked in _deletedJobIds so those
    stragglers get dropped.

  - toggleAccount did two IPC round-trips (saveConfig + getConfig) on
    every enable/disable click, plus four re-renders (Accounts,
    HosterSummary, HosterModal, Settings). Rapid clicks felt laggy.
    The getConfig refetch is redundant since we mutated the flag in
    place, and HosterModal/Settings don't depend on account enabled
    state. Click now renders immediately and the save runs async.
2026-04-19 22:08:22 +02:00
Administrator
880537dcfb fix: multi-level account rotation + clear failed-accounts per batch + size-sort staleness
Three state bugs found during audit:

  1. _failedAccounts / _accountOverrides survived across batches. A
     rate-limited account from batch 1 stayed permanently blacklisted
     for the rest of the app session, so batch 2 skipped straight to
     the fallback even after the original recovered. Now cleared in
     startBatch so each run evaluates accounts fresh.

  2. Account rotation was one level deep. With three accounts [A,B,C]
     on the same hoster and A + B both failing, the job errored out
     — C was never tried. The fallback-retry was a single if-block.
     Replaced with a while-loop that keeps asking main for the next
     override and rotating until every account is exhausted.

  3. Queue sort cache included 'size' as a static key, but bytesTotal
     goes 0 → actual when previews resolve. A queue sorted by size
     during preview would cache the all-zeros order and never update.
     Removed size from _STATIC_SORT_KEYS — it now re-sorts per render
     like status/speed/progress.
2026-04-19 22:01:20 +02:00
Administrator
4f2d462754 perf: single-pass escapeHtml/escapeAttr
Hot path on large table rebuilds — every text cell runs through one
of these. Switching from 4 chained .replace() calls to a single regex
with a lookup map is ~3× faster. At 5000 rows × 4 fields per rebuild,
80k → 20k regex operations.
2026-04-19 14:06:52 +02:00
Administrator
2d8b3f1bf9 perf: final sweep — hot-path allocation, cached log target, sort-header skip
Last round of targeted wins:

  - upload-manager progress callback was allocating a fresh
    { jobId, speedKbs, bytesUploaded } object on every fs stream chunk
    (hundreds of times per second per active job). Now a single entry
    is created at job start and mutated in place — zero allocations
    on the steady-state progress tick.

  - upload-manager stats timer's two separate activeJobs.values()
    scans (globalSpeedKbs + inProgressBytes) merged into one pass.

  - clouddrop-upload.js reuses a single Buffer.allocUnsafe(chunkSize)
    across all chunks, taking subarray() only for the tail chunk.
    A 1 GB upload no longer allocates 64× 16 MB = 1 GB of short-lived
    buffers — real GC relief during many-file batches.

  - _resolveUploadLogTarget is now cached; the fallback ladder runs
    once per session (or when the user changes the log path / daily-log
    date rolls), not on every 500ms flush.

  - renderRecentUploadsPanel skips updateRecentSortHeaders on the
    append-only fast path — sort state hasn't changed, headers don't
    need recomputing.
2026-04-19 14:02:34 +02:00
Administrator
f16dd9ffa6 perf: lazy history refresh + append-only recent panel + queue-cleanup merge
Three more targeted wins:

  - loadHistory() was called unconditionally on every handleBatchDone,
    doing an IPC roundtrip + full history-table rebuild even when the
    user is on the Upload tab and can't see it. Now it sets a dirty
    flag and the actual refresh is deferred until the user switches
    to the Verlauf tab. On a fresh tab click it always runs.

  - renderRecentUploadsPanel append-only fast path: when the sort is
    'date desc' (the default) and the dataset only grew, the panel
    inserts the new rows at the top via insertAdjacentHTML instead
    of rebuilding the 5000-row tbody from scratch. Length shrinks or
    sort-change still trigger a full rebuild.

  - handleBatchDone's removeFromQueueOnDone cleanup now does one pass
    (build keep-list + detach from index together) instead of two
    separate filter() scans over queueJobs.
2026-04-19 13:55:37 +02:00
Administrator
879f6ade0e perf: O(1) lookups for selection buttons, applySummaryResults, file-drop dedup; batched upload log
Four more wins targeting batch-heavy paths:

  - updateQueueActionButtons replaced three O(n) queueJobs.some() scans
    with a single O(|selection|) pass over selectedJobIds, using the
    existing _jobIndexById map. Selection change cost on a 1000-job
    queue drops from ~3000 comparisons to |selection|.

  - applySummaryResults built a (fileName+hoster)→job Map once per call
    instead of running queueJobs.find() per result. Big batches
    (hundreds of files × multiple hosters) no longer scale O(n²).

  - addPathsToQueue and the folder-monitor auto-queue path built their
    dedup Set up front instead of running .find() per incoming path.
    Picking a folder with thousands of files now dedups in O(n+m)
    instead of O(n×m).

  - appendUploadLog became async + buffered like debugLog. A burst of
    20 files completing within a second becomes one fs.appendFile
    instead of 20 fs.appendFileSync that each blocked the main event
    loop. Fallback ladder (primary → Desktop → userData) is preserved;
    pending buffer flushes synchronously on before-quit.
2026-04-19 13:38:39 +02:00
Administrator
8f304f91d8 perf: buffered debug-log writer, scroll rAF-throttle, Set dedup for recent panel
Three more rounds of lag removal aimed at heavy upload sessions:

  - main-process debugLog() was doing fs.appendFileSync on every call
    and was firing hundreds of times per second during busy uploads
    (progress transitions, unhandled rejection traces, folder-monitor
    events). Replaced with an in-memory buffer flushed every 500ms via
    async appendFile — the main event loop is no longer blocked per
    line. Buffered entries flush synchronously on before-quit.

  - the renderer's 'RX upload-progress' / 'RX upload-stats' listeners
    were emitting one IPC roundtrip per event. For 20 concurrent jobs
    that's 80 IPC messages/sec just for logging. They now skip the
    debug call on the hot 'uploading' tick and only log transitions.

  - _onQueueScroll now coalesces scroll events via requestAnimationFrame
    so a fast trackpad fling triggers one virtual render per frame
    instead of one per wheel event.

  - maybeAddSessionFile switched from O(n) sessionFilesData.some() dedup
    to an O(1) Set lookup keyed on (link, filename, host). Adding 1000
    results to an already-populated panel drops from ~500ms to <5ms.
2026-04-19 13:19:04 +02:00
Administrator
9158949480 perf: memoize queue sort, dedup stats scan per tick, skip no-op DOM writes
Three more wins on top of the previous pass:
  - sortQueueJobs memoizes the result for static sort keys
    (filename, host, size) — these don't change during upload, so
    every 200ms progress render now reuses the same sorted array
    instead of running an O(n log n) Collator compare.
  - _computeQueueStats caches within a single tick via queueMicrotask.
    updateStatusBar + updateStatsPanel are always called back-to-back
    and now share one queue scan instead of running two.
  - _updateRowInPlace writes DOM values only when they actually
    changed. Idle/queued/done rows (the majority) incur zero DOM
    mutations per progress tick.
2026-04-19 12:59:10 +02:00
Administrator
85287aa620 perf: kill lag with 1000s of rows during upload
The two worst hot paths were:
  - clicking a row triggered a full table rebuild with sort+innerHTML
    (queue AND recent panel), and the opposite panel got cleared with
    another full rebuild
  - every upload progress tick (4/sec) scanned queueJobs twice and
    filtered sessionFilesData twice just to update the status bar

Fixes:
  - applyQueueSelectionClasses / applyRecentSelectionClasses toggle the
    .selected class on existing rows instead of rebuilding the tbody.
    Click selection is now O(rendered rows) instead of O(total × sort).
  - maybeAddSessionFile schedules renderRecentUploadsPanel via rAF so
    a batch of 1000 successful uploads coalesces into one render.
  - sortRecentFiles memoizes its result per (sortKey, direction, len)
    — unchanged sort state + unchanged length returns the cached array
    instead of re-sorting thousands of entries.
  - _computeQueueStats now also returns inProgressBytes, dropping the
    second queueJobs scan in updateStatusBar.
  - session done/error counts are maintained incrementally, replacing
    two sessionFilesData.filter().length calls every status-bar tick.
  - handleRowClick uses the _jobIndexById map instead of Array.find.
2026-04-19 12:27:16 +02:00
Administrator
9a7354fc55 feat(recent): export all recent uploads (name+link+hoster+time)
Adds an 'Exportieren' button next to 'Alle entfernen' that writes a
pipe-delimited log of every row currently shown in the recent-uploads
panel — so session data doesn't get lost if the log file path is wrong.

Also fixes appendUploadLog silently failing: if the configured path is
unwritable (e.g. C:/Users/<nonexistent>/...), entries now go to
<userData>/fileuploader-fallback.log and the renderer warns once.
2026-04-19 11:35:41 +02:00
Administrator
161357522e fix(backup): don't pass click event as legacy password
addEventListener('click', doBackupImport) was passing the MouseEvent
as the first argument, which got forwarded to pbkdf2 as an Object.
2026-04-17 16:54:53 +02:00
Administrator
edb614f985 feat(backup): import legacy password-encrypted backups
Try app-internal key first (new format); on failure, signal the
renderer to prompt for the old password and retry. Lets users import
.mhu files that were exported with a custom password in v2.7.6 or
earlier without downgrading.
2026-04-17 11:22:33 +02:00
Administrator
3e9483e222 feat(backup): drop password prompt on export/import
File stays AES-GCM encrypted with a fixed app-internal key — opaque
without the app, which is the only protection we actually need for
locally-stored API keys. Removes the modal and both password dialogs.
2026-04-17 11:17:21 +02:00
Administrator
1164da37ea feat: add Clouddrop.cc as upload hoster (API key auth, chunked uploads)
- New lib/clouddrop-upload.js with chunked upload support (16 MB chunks)
- Auth via Bearer token (cd_XXX format)
- Files < 16 MB: simple multipart POST /api/cloud/upload
- Files > 16 MB: chunked protocol (init → PUT chunks → complete)
- After upload: auto-creates permanent share link via /api/cloud/share-link
- Health check verifies API key by listing root files

Registered in:
- lib/config-store.js (HOSTER_NAMES, templates, DEFAULTS)
- main.js (hosterAccountHasCreds, checkClouddropHealth, runHosterHealthCheck)
- lib/upload-manager.js (_executeUpload dispatch)
- renderer/app.js (HOSTERS, HOSTER_ADD_OPTIONS, getHosterLabel)

Tests: 74/74 pass. ESLint: 0/0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 06:55:21 +02:00
Administrator
849b1e340b feat: 'Alle entfernen' button for recent files panel
Adds a red 'Alle entfernen' button next to the 'Zuletzt erzeugte
Upload-Links' label that clears all entries from the recent files
panel after confirmation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:25:42 +02:00
Administrator
e7dd91ae59 feat: resizable queue table columns (JDownloader-style)
Drag the right edge of any queue column header to resize it. Cursor
changes to col-resize on hover. Widths are saved to localStorage and
restored on next launch.

- Resizer handles in all 8 queue table columns
- Resize state visible via dragging class + body cursor override
- Min width 40px, no max (table can scroll horizontally)
- Click on resizer doesn't trigger column sort
- Persisted across sessions via localStorage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:13:40 +02:00
Administrator
cb6d61a406 🐛 fix: files added during upload now actually get uploaded
When user added new files during an active upload (drag-drop, picker
or folder monitor with pre-selected hosters), the files were pushed to
selectedFiles but NO queue jobs were created (because updateUploadView
skips buildQueuePreview during uploading=true).

The files briefly showed up via folder monitor's direct buildQueuePreview
call, but then handleBatchDone → syncSelectedFilesFromQueue removed them
from selectedFiles because they had no queue jobs.

Now: applyHosterSelection() and folder monitor both detect added files
during upload and:
1. Build preview jobs for the new files
2. Reset them to 'queued' status
3. Inject them into the running batch via addJobsToBatch IPC

The upload-manager has duplicate protection so re-injection is safe.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 22:46:31 +02:00
Administrator
c197a004c8 Add full upload history export and keep complete history 2026-03-28 19:48:28 +01:00
Administrator
b75930cb29 refactor: unify queue start status handling 2026-03-26 11:08:01 +01:00
Administrator
f288ced84b fix: allow global start to retry failed queue items 2026-03-26 11:00:46 +01:00
Administrator
8b68a7a07e fix: prevent retry jobs from getting stuck in waiting state 2026-03-26 10:17:15 +01:00
Administrator
a5b07c0f73 🐛 fix: 'Ausgewählte starten' on queued jobs now force-adds to batch
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>
2026-03-25 00:14:53 +01:00
Administrator
f642122726 🐛 fix: show feedback when 'Wartet' jobs are already in batch
- 'Ausgewählte starten' on already-queued jobs during upload now shows
  toast: "X Jobs warten bereits auf ihren Upload-Slot"
- Only error/aborted/skipped jobs are added to the running batch
  (prevents duplicate task creation for already-queued jobs)
- Toast confirms when error jobs are added to batch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:07:12 +01:00
Administrator
bf7f35d06c feat: auto-deduplicate queue against upload log on startup
When the app restarts with a restored queue, it now automatically
reads all fileuploader.log files and removes jobs that were already
successfully uploaded in a previous session.

This prevents re-uploading files that completed before a crash/close.
The dedup runs silently before the UI renders — no user action needed.

Also adds 'read-own-upload-log' IPC that reads all log variants
(base + daily logs) without file picker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:36:31 +01:00
Administrator
24cb096ba9 🐛 fix: log import now permanently removes jobs from queue
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>
2026-03-24 10:02:29 +01:00
Administrator
e07db0532a feat: import upload log to remove already-uploaded jobs from queue
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>
2026-03-24 09:57:09 +01:00
Administrator
e1b03605fa feat: retry/start selected jobs while upload batch is running
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>
2026-03-23 18:15:31 +01:00
Administrator
17fbb98c13 🐛 fix: skipped jobs now show error instead of stuck 'Wartet' forever
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>
2026-03-23 18:08:41 +01:00
Administrator
94c3c5e4ac 🔧 chore: let→const for never-reassigned Sets/Maps/objects
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>
2026-03-22 20:29:34 +01:00
Administrator
d02d6944d3 feat: 'Ausgewählte starten' works for failed/aborted jobs too
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>
2026-03-22 20:19:42 +01:00
Administrator
68fc064999 🐛 fix: Ctrl+A selects correct panel (queue vs recent files)
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>
2026-03-22 19:44:15 +01:00
Administrator
d7f9cd510f feat: upload starts immediately, no blocking health check alert
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>
2026-03-22 16:34:12 +01:00
Administrator
26fabaa5c1 🔧 chore: ESLint clean — 0 errors, 0 warnings
- Disable detect-object-injection (78 false positives from config lookups)
- Suppress 2 safe regex warnings in vidmoly HTML parser with comments
- Suppress 2 async loop condition warnings (modified between awaits)

ESLint: 0 errors, 0 warnings. Tests: 70/70 pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:58:09 +01:00
Administrator
c82edc8d9e 🔧 chore: add ESLint + security plugin, fix all errors
ESLint with eslint-plugin-security configured and all 6 errors fixed:
- Remove unused 'self' variable (doodstream-upload.js)
- Remove unused 'statusCode' destructure (voe-upload.js)
- Remove unused 'powerSaveBlocker' import (main.js)
- Remove dead 'setHealthCheckStatus' function (app.js)
- Add URLSearchParams to ESLint globals
- Rename unused 'mode' param to '_mode'

82 remaining warnings are all security/detect-object-injection
false positives (normal config object access patterns).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:53:26 +01:00
Administrator
cb70b47242 ♻️ refactor: remove redundant 'X abbrechen' context menu items
'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>
2026-03-22 14:48:51 +01:00
Administrator
1d35f024f2 🐛 fix: re-uploading same file after deleting completed job was blocked
_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>
2026-03-22 14:44:50 +01:00
Administrator
5dabd44b53 🐛 fix: add missing escapeAttr on remote token input value
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>
2026-03-21 14:58:14 +01:00
Administrator
a4a2eaa736 🐛 fix: scaleParallelUploads inverted, settings lost on close, IPC leak
- 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>
2026-03-21 14:24:14 +01:00
Administrator
ada3b31ad1 🐛 fix: health check wait timeout, _deletedJobIds memory cleanup
- Add 30-second timeout to health check wait loop in startUpload/
  startSelectedUpload to prevent infinite spin if healthCheckRunning
  gets stuck
- Clear _deletedJobIds Set when batch completes to prevent unbounded
  memory growth over long sessions with many deletions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 14:16:59 +01:00
Administrator
f6c9979ac5 🐛 fix: job index rebuild after restore, drop-target visibility, XSS
- Rebuild _jobIndexById after restoring queue from config on startup
  (prevented progress updates from finding restored jobs)
- Show and focus mainWindow when files are dropped on floating
  drop-target while window is minimized/hidden
- Escape status text in queue table HTML to prevent XSS from
  unexpected status values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:52:22 +01:00
Administrator
61e458b8ea 🐛 fix: skip 0-byte files, fix drag-drop highlight flicker
- 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>
2026-03-21 11:46:04 +01:00
Administrator
6d3b2d3a86 🐛 fix: upload button stuck, abort handling, filename escaping
- 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>
2026-03-21 11:21:09 +01:00
Administrator
7ba2c63d51 🐛 fix: config race conditions, quit safety, update data loss
- 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>
2026-03-21 10:20:07 +01:00
Administrator
a64ebd1587 feat(queue): add "Hoster entfernen" submenu to context menu
Right-click on queue now shows a "Hoster entfernen ▸" submenu listing
all hosters with job count (e.g. "Vidmoly (3)"). Clicking removes all
jobs for that hoster, cancels active uploads, and saves immediately.

Also fixes submenu viewport flip measurement (was reading offsetWidth
on display:none elements).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 09:03:13 +01:00
Administrator
176cadc2dd 🐛 fix(queue): deleted jobs reappear after restart
Three root causes fixed:
- handleProgress() re-created deleted jobs from stale progress callbacks
- Queue save was debounced (10s during uploads), deletion lost on app close
- Delete was blocked during active uploads (removed !uploading guard)

Now: deletions save immediately, deleted IDs are tracked to prevent
re-creation, and active uploads are cancelled when their jobs are deleted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 08:46:19 +01:00
Administrator
beba96c21b feat(doodstream): add OTP input support for web login
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>
2026-03-20 16:09:33 +01:00
Administrator
f13bf7f5bc feat(remote): add Fernsteuerung settings panel with token management and status display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 06:58:29 +01:00
Administrator
0c945e21b8 fix: prevent double-click race condition in upload start
Move `uploading = true` guard to immediately after the check in both
startUpload() and startSelectedUpload(), before any async calls.
Previously the flag was set after await executeHealthCheck(), allowing
a fast double-click to bypass the guard and start duplicate batches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:02:30 +01:00