Desktop-Anwendung zum gleichzeitigen Hochladen auf mehrere File-Hoster
Go to file
Administrator 7f636258d4 perf(uploads): make the batch-start file-stat non-blocking — kill the 336ms main stall (v3.3.103)
The v3.3.102 log (real 224-job batch) confirmed History virtualization works and
steady-state uploads are pristine (fps=32, event-loop mean 11.8ms). The remaining
residual was a ~6s spin-up burst at batch start: the main event loop blocked for
336ms with cpu=0%core (i.e. blocked on I/O, not computing) and the renderer janked
196-391ms, then everything settled clean.

A multi-agent investigation plus an adversarial review corrected the obvious-looking
hypothesis. The renderer's uncapped progress-batch drain is NOT the cause:
handleProgress only mutates plain JS state and schedules already-coalesced renders
(one per frame), and main coalesces progress to ~50 latest-per-job entries per
100ms. Chunking that drain would fix nothing — and the reviewer showed it would
REGRESS correctness: requestAnimationFrame throttles to ~0 when the window is
minimized (the common state for a background uploader), so a rAF-chunked drain would
grow an unbounded backlog and defer persistQueueStateSoon for every buffered item,
losing terminal 'done' events on close (the queue-persistence ghost-fix class). So
that path is deliberately not taken.

The real cause (cpu=0%core = blocked on I/O) is a synchronous fs.statSync storm in
UploadManager.startBatch: the dedup loop ran up to DEDUP_CHUNK=200 synchronous
fs.statSync calls in a single tick before yielding (200 x ~1.68ms on the user's VM
= the exact 336ms), on a disk already saturated by the 1MB read-ahead.

Fix — make the batch-start stats non-blocking:
- The dedup loop now dedupes synchronously (cheap Map work) and then stats the
  unique files in parallel via await Promise.all(fs.promises.stat ...) per chunk, so
  the stat I/O runs on the libuv threadpool and the main thread never blocks. The
  results-Map shape ({name,size,results:[]}) and dedup semantics (size 0 on failure)
  are unchanged.
- The per-job statSync fallback is converted to await fs.promises.stat for
  consistency (it sits in an async function before the first real await; the cached
  size from dedup already lets nearly every job skip it).

Tests: the upload-manager mocks override fs.statSync; they now also override
fs.promises.stat with the same fake sizes (upload-manager.test.js x2,
suspect-reject-alternates.test.js). 407 tests pass; clean Electron boot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:53:48 +02:00
assets feat: custom app icon (arrows-up design) 2026-03-11 23:30:46 +01:00
docs feat(diagnostics): network bind + fail-closed IP allowlist + host-in-code (Tailscale, like rd-diagnostics-mcp) 2026-06-19 19:32:29 +02:00
gateway feat(diagnostics): network bind + fail-closed IP allowlist + host-in-code (Tailscale, like rd-diagnostics-mcp) 2026-06-19 19:32:29 +02:00
lib perf(uploads): make the batch-start file-stat non-blocking — kill the 336ms main stall (v3.3.103) 2026-06-21 20:53:48 +02:00
renderer perf(history): virtualize the History table — kill the last tab-switch layout cost (v3.3.102) 2026-06-21 20:04:10 +02:00
scripts fix: rcedit import in afterPack - icon was not being embedded in exe 2026-03-12 01:22:56 +01:00
tasks perf(uploads): make the batch-start file-stat non-blocking — kill the 336ms main stall (v3.3.103) 2026-06-21 20:53:48 +02:00
tests perf(uploads): make the batch-start file-stat non-blocking — kill the 336ms main stall (v3.3.103) 2026-06-21 20:53:48 +02:00
.gitignore perf(config): split history out of the hot config file — the real 38.5MB main-thread thrash (v3.3.99) 2026-06-21 18:37:12 +02:00
app.py chore: initial commit - Electron multi-hoster uploader 2026-03-10 02:32:06 +01:00
eslint.config.mjs perf(config): cache parsed config + lean serialize + drop load() from log flush (long-run lag) 2026-06-21 00:49:55 +02:00
hosters.py chore: initial commit - Electron multi-hoster uploader 2026-03-10 02:32:06 +01:00
main.js perf(history): gate the unconditional History-tab reload + fix the diagnostics history regression (v3.3.101) 2026-06-21 19:45:50 +02:00
package-lock.json chore(deps): npm audit fix --force — closes 12 deferred vulnerabilities 2026-04-28 11:53:53 +02:00
package.json perf(uploads): make the batch-start file-stat non-blocking — kill the 336ms main stall (v3.3.103) 2026-06-21 20:53:48 +02:00
preload-drop-target.js feat: floating drop target window and English column labels 2026-03-12 04:11:01 +01:00
preload.js feat(diagnostics): read-only remote diagnostics agent over the existing WS transport (app side) 2026-06-19 17:24:32 +02:00
README.md docs: update README for Electron version 2026-03-10 02:35:43 +01:00
requirements.txt chore: initial commit - Electron multi-hoster uploader 2026-03-10 02:32:06 +01:00

Multi-Hoster-Upload

Desktop-Anwendung zum gleichzeitigen Hochladen von Dateien auf mehrere File-Hosting-Dienste.

Unterstuetzte Hoster

  • doodstream.com - API-Key basiert
  • voe.sx - API-Key basiert
  • vidmoly.me - Login basiert (Username/Passwort)
  • byse.sx - API-Key basiert

Installation

Setup (empfohlen)

Lade die Multi-Hoster-Upload Setup X.X.X.exe aus den Releases herunter und installiere.

Portable

Lade die Multi-Hoster-Upload X.X.X.exe herunter. Keine Installation noetig.

Entwicklung

npm install
npm start

Build

npm run release:win

Erzeugt Setup + Portable EXE im release/ Ordner.

Release

GITEA_TOKEN=dein_token npm run release:gitea -- 1.0.1 "Release notes"

Features

  • Drag & Drop oder Dateiauswahl
  • Gleichzeitiger Upload zu mehreren Hostern
  • Echtzeit-Fortschrittsanzeige
  • Ergebnis-Tabelle mit Sortierung und Kopier-Funktion
  • Upload-Verlauf (letzte 100 Eintraege)
  • Health-Check fuer Hoster-Verbindungen
  • Auto-Updater (prueft git.24-music.de)
  • Log-Datei kompatibel mit File & Image Uploader Format