Three deeper optimizations focused on hot allocations:
1. cloneSession(): items/packages references shared instead of per-item
shallow clone. The IPC layer runs structuredClone() in the same tick
so the renderer always gets an isolated copy; in-process consumers
read snapshots synchronously without mutating. Eliminates ~5000
object allocations per emit on a 5000-item queue.
2. findNextQueuedItem(): single-pass priority scan instead of 3 separate
passes (high → normal → low). Returns immediately on high-priority
match; collects best normal/low candidate while iterating. Saves up
to 2x O(n) iterations per scheduler tick.
3. packageSpeedBps: direct loop assembly instead of
Object.fromEntries([...Map].map(...)) (3 allocs per entry → 1).
Idle case now returns a stable EMPTY_PACKAGE_SPEED_BPS reference
so the renderer's useMemo on it doesn't recompute on every snapshot
while the queue is paused/stopped.
All 565 tests green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major optimizations to reduce UI lag with large queues (5000+ items):
1. ItemRow extracted to its own memoized component (renderer)
Previously every package re-render mapped all its items inline,
producing N×M re-renders per state update. Now each item-row only
re-renders when ITS specific data changes, with custom equality on
the visible fields (status, progress, speed, fullStatus, etc.).
Also adds stable useCallback handlers per item.
2. PackageCard stats consolidated into single useMemo (renderer)
Replaces 5 separate filter()/some() + 2 reduce() calls (O(7N)) with
one O(N) pass collecting all aggregates (done/failed/cancelled/
extracted/extracting/activeProgress/extractingProgress).
3. selectedIds memo comparator fixed (renderer)
Custom equality now checks if selection state changed for items in
THIS package only. Previously any selection anywhere broke memo on
all 200+ visible PackageCards.
4. Scheduler single-pass queue presence (main)
New getQueuePresence() returns hasImmediate + hasDelayed in one
iteration. Replaces hasQueuedItems() + hasDelayedQueuedItems() that
each scanned packages independently. Saves one full O(n) iteration
per scheduler tick.
No functional changes. All 565 tests green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three low-risk optimizations that reduce CPU/memory footprint without
changing user-visible behavior:
1. Periodic cleanup of unbounded module-level Maps (24/7 stability):
- debridLinkKeyCooldowns, debridLinkKeyCooldownDetails,
debridLinkKeyRuntimeStatuses (debrid.ts)
- megaDebridAccountCooldowns (debrid.ts)
- allDebridHostInfoCache (download-manager.ts)
- All pruned every 10 min via existing resetStaleRetryState() with a
1h grace window for debugging
- Without this, modules accumulated entries indefinitely over days
of continuous server operation
2. Hoist regex literals in resolveArchiveItemsFromList() to module scope.
Avoids 5 RegExp constructions per call. The function is called per
archive set during extraction discovery — adds up on packages with
many archives.
3. BandwidthChart: skip the 250ms redraw interval while the session is
stopped or paused. The chart renders once on state change to show the
latest history, then sleeps until downloads resume. Saves 4 canvas
redraws per second when idle (renderer CPU).
No functional changes. All 565 tests green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three improvements for users running on servers where the Windows account
is not "Administrator" or where the environment is headless (Service, RDP-
disconnected, no interactive desktop):
1. readSettingsFile / readSessionFile: distinguish ENOENT (normal first run)
from EACCES/EPERM (permission problem). The latter logs an explicit
message including the current Windows username so the user can spot
misconfigured ACLs immediately.
2. ensureBaseDir: log EACCES/EPERM with the current username before re-
throwing. Previously the error bubbled up without any hint why the
AppData directory creation failed.
3. createTray: log a warning when Tray creation fails (typical on Windows
Service / headless servers / RDP disconnected sessions). Previously the
error was silently swallowed and minimize-to-tray would just not work
without explanation.
These errors were silently swallowed before, making it impossible to
diagnose problems on servers with restricted user accounts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The v1.7.130 BONUS_DIR_PATTERNS used substring matching with space-separated
patterns like "making of" and "behind the scenes", but real-world subfolder
names use dot/dash/underscore separators (e.g. "Breaking.Bad.S05.Making.Of").
These were NOT detected as bonus dirs, causing the safety net in v1.7.131 to
apply the source filename's episode token to the package name, producing
mislabeled bonus files like "Breaking.Bad.S05E10.GERMAN.BluRay.720p.TSCC".
Fix: normalize folder segments by stripping all separators ([._-\s]+) before
matching against BONUS_DIR_NORMALIZED_PATTERNS. "Breaking.Bad.S05.Making.Of"
normalizes to "breakingbads05makingof" which matches "makingof".
Also extend BONUS_FILENAME_RE with "inside-e\d+" and "making-of-e\d+" to
catch more filename variants from Breaking Bad BluRay extras.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Real-world scenario from user logs: package "Drei.Meter.ueber.dem.Himmel.
S01GERMAN.DL.720P.WEB.X264-WAYNE" (note malformed S01GERMAN with no
separator) caused the auto-renamer to strip the source's S01E01..S01E08
episode tokens because SCENE_SEASON_ONLY_RE doesn't match a season followed
by an immediate letter (no separator).
Result: all 8 episodes in the season pack collapsed to the same target name
and collided in the MKV library with (2)(3)(4)(5)(6)(7)(8) suffixes.
Fix: After buildAutoRenameBaseNameFromFoldersWithOptions, check if the
source filename has a valid episode token. If yes:
1. If target has NO episode token: try to insert it via regex replacement
(Sxx<garbage> -> SxxExx.<garbage>), then via applyEpisodeTokenToFolderName.
If both fail, skip the rename entirely (preserve source name).
2. If target has a DIFFERENT episode token: skip the rename (mislabel risk).
This guard is the last line of defense against the helper's regex
limitations on malformed package names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bonus content (Featurettes, Behind-The-Scenes, Making-Of, Deleted Scenes,
etc.) was being moved into the flat MKV library with generic names like
"Schrotflinte.mkv" or "White.House.mkv", losing all show context. Auto-rename
also touched these files and would mislabel them with episode tokens.
Real-world impact: 397 bonus files from Breaking Bad S03/S04/S05 BluRay
extras subdirectories landed in the user's main library with nonsense names.
Fix:
- Add isInsideBonusDir() that walks the path from file to package root,
checking each directory segment for bonus indicators (Extras, Bonus,
Featurettes, Specials, Behind-The-Scenes, Making-Of, Deleted-Scenes, etc.)
- Add BONUS_FILENAME_RE to catch bonus indicators in filenames (making-of-e02,
deleted-scene, alternate-ending, gag-reel, behind-the-scenes, etc.)
- Auto-rename: skip files matching either pattern
- MKV collection: skip files matching either pattern, log skipped count
Bonus files now stay in the package output directory with their original
names; only the actual episodes get moved to the flat library.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stem extraction regex for matching companion metadata files (.sfv,
.nfo) to their archives only handled RAR patterns. Now also covers
ZIP, 7z, tar, generic splits, and recovery volumes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When all archive parts were already extracted in a prior hybrid round and
a companion file (.sfv, .nfo, .md5) downloads later, result.extracted is
0 so the companion's status was never updated from "Entpacken - Ausstehend"
or "Entpacken - Warten auf Parts".
Now companion metadata files are explicitly marked as "Entpackt (Metadaten)"
when no archives need extraction and no failures occurred, preventing them
from blocking package completion indefinitely.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
collectArchiveCleanupTargets() now includes companion files (.sfv, .nfo,
.md5, .sha1, .sha256, .crc, .srr) that share the same base stem as the
archive parts. Previously these were left as orphans after archive cleanup.
Applies to all archive types: multipart RAR, single RAR, ZIP, split ZIP,
7z, and split 7z.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SFV files belong to the same archive set as the RAR parts but were not
included in hybridFileNames, causing them to stay stuck on "Entpacken -
Ausstehend" after the RAR parts were successfully extracted. This blocked
package completion in hybrid extraction mode.
Fix: collect archive base stems from extracted parts and match companion
metadata files (.sfv, .nfo, etc.) by stem, adding them to hybridFileNames
so they get marked as "Entpackt" together with their archive parts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SFV checksum verification files are legitimately tiny (~128 bytes) but were
rejected by the "suspicious small download" detection, causing infinite
"Direktlink erneuern" retry loops that blocked package extraction.
- Add KNOWN_SMALL_FILE_RE for .sfv, .nfo, .nzb, .md5, .sha1, .sha256, .crc,
.txt, .url, .lnk, .srr file extensions
- Skip suspicious-small-download rejection for known small files when they
match their expected size (or have no size expectation)
- Skip tiny-download error detection for known small metadata files
- Add test: verifies .sfv file downloads without retries and completes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add safeJsonReplacer to all JSON.stringify calls in storage.ts to prevent
NaN/Infinity values from corrupting state files and causing queue loss
- Fix LinkSnappy and 1Fichier retry loops: use sleepWithSignal() instead of
sleep() so abort signals are respected during retry delays
- Fix Debrid-Link polling: replace raw setTimeout with sleepWithSignal() so
URL generation polling can be cancelled
- Fix Mega-Debrid doConnectApi: clear token cache on 401/403 responses
instead of caching invalid credentials for 20 minutes
- Add logging when normalizeLoadedSession removes orphaned items so data
loss during startup is visible in logs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Increase quit timeout from 900ms to 5000ms to ensure pending saves complete
- Add persistNowSync() called before update install to flush queue to disk
- Remove blockAllPersistence from shutdown save condition — shutdown must
always persist to prevent data loss across restarts
- Add temp file recovery as last resort when both primary and backup
session files are corrupted
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Some hosters/debrid services obfuscate downloaded archive filenames by
mutating characters and changing extensions (e.g. .part06.rar → .part06.mov,
star_crossed → star_crossfed). This breaks extraction since the extractor
relies on filename patterns to discover archive parts.
New deobfuscateArchiveFiles() method runs after download, before extraction:
- Reads magic bytes of non-archive files via detectArchiveSignature()
- If RAR/7z/ZIP signature found: corrects the extension
- Uses correctly-named sibling .rar files as reference to reconstruct
the full correct filename including part number
- Updates item.fileName and item.targetPath after rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- notDebrid (host-level) no longer burns all keys: stops rotation immediately
with 5min cooldown instead of cycling through all 9 keys pointlessly
- Remove double provider-blockade: debrid_link_cooldown no longer stacks
recordProviderFailure + applyProviderBusyBackoff on top of key cooldowns
- Detect timeout cascades: 2+ consecutive transport failures trigger 3min
cooldown instead of burning remaining keys
- Case-sensitive rename: files with different casing (e.g. lowercase scene
names) now get properly renamed instead of being skipped as "already matching"
- Extended sample filter: detect -s.mkv suffix and \Sample\ subdirectories
in auto-rename (already worked in MKV-move)
- Add key status display with state pills in Debrid-Link key stats popup
- Add parseDebridLinkTerminalFailure for fast-fail on exhausted keys
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add polling loop (5x 2s) in resolveDownloaderEntry when /add returns
no downloadUrl — Debrid-Link sometimes needs seconds to generate links
- Classify missing/expired downloadUrl as temporary instead of fatal so
key rotation kicks in before giving up
- Change notDebrid from fatal to temporary — "host may be down" is
transient, all keys should be tried before failing
- Raise parseRetryAfterMs cap from 2min to 1h — floodDetected mandates
"retry after 1 hour" per API docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove daily-log module entirely (caused UI freezes due to sync I/O
even after async rewrite). Revert queue-scope stop() change (was for
a different project). All source files now match v1.7.112.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rewrite daily-log from synchronous fs.writeSync to async buffered
writes (500ms flush interval), matching the main logger's pattern.
The sync writes blocked the event loop on every log line.
- Revert the stop() queue scope change from v1.7.114 which was
intended for a different project.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When startItems() was used with a subset of items (e.g. 2000 of 6020),
stopping and restarting would pick up ALL 6020 queued items instead of
just the original 2000. Now stop() marks items outside the run set as
"Gestoppt" so they are not automatically included in the next start().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All log output is now additionally written to daily log files:
daily-logs/YYYY-MM/YYYY-MM-DD.log (main log)
daily-logs/YYYY-MM/YYYY-MM-DD-rename.log (rename log)
Automatic cleanup of daily logs older than 30 days. The existing
rd_downloader.log and rename.log continue to work as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The post-MKV-move rename pass added in v1.7.107 ran on the shared
mkvLibraryDir (Entpackt/), causing files from OTHER packages to be
renamed to the current package's name. For example, Orange.Is.The.New.Black
files were renamed to Ted.S02E13...SAUERKRAUT.mkv.
Remove the post-MKV-move rename entirely. The original hybrid race
condition (1 file per season not renamed) is far less damaging than
cross-package corruption.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multiple Mega-Debrid accounts can now be configured as login:password
pairs (one per line). When an account hits Fair-Use limits or errors,
the next account is tried automatically.
- New parser module mega-debrid-accounts.ts (parse, ID generation,
masking, serialization)
- Per-account daily limits, usage tracking, enable/disable
- Account rotation with per-mode cooldowns (API failures don't
block Web attempts)
- Backward compatible: existing single megaLogin/megaPassword
is auto-migrated to the new format
- UI: textarea for credentials, account list with masked logins
Follows the existing Debrid-Link multi-key pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scene releases occasionally use SxxSxx (e.g. s05s01) instead of
SxxExx — the second S is a typo for E. Add a fallback regex to
detect this pattern and correctly interpret it as S05E01.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sample files like wayne-sample.mkv were renamed by auto-rename which
stripped the -sample suffix. After rename they were indistinguishable
from the main MKV, causing MKV collection to create (2) copies
(e.g. Messiah.Superstar.S01E01...WAYNE (2).mkv at 17 MB alongside
the real 470 MB episode).
Auto-rename now skips files with a "sample" token in their name,
matching the same detection used by MKV collection's sample filter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
During hybrid extraction, files can finish extracting between the
auto-rename scan and MKV-move, causing them to be moved to the MKV
library dir with their original scene names (e.g. awa-diethundermans03e21hd.mkv).
Add a post-MKV-move auto-rename pass on the MKV library directory to
catch these stragglers and rename them to the proper folder-based name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Folder names with episode ranges like S01E01-E08 (common in season
packs from debrid servers) were returned unchanged as target name,
causing all episodes to get the same filename and producing (2)(3)(4)
suffixes during MKV collection.
- Detect episode ranges (S01E01-E08, S01E01-08) in folder names and
replace them with the source file's specific episode token
- Extend applyEpisodeTokenToFolderName regex to match and replace
full episode ranges instead of only single episode tokens
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix episode-token pollution: packageExtraCandidates included ALL item
filenames, causing resolveEpisodeTokenForAutoRename to pick up episode
tokens from unrelated files (e.g. S01E07 from 4sf-...-s01e07 applied
to all hrs-...-101/102/103 files). This also caused (2)(3) MKV
suffixes when multiple files were renamed to the same wrong episode.
Now only the package name (outputDir) is used as extra candidate.
- Fix deferred nested extraction missing abort signal: the nested
extractPackageArchives call in runDeferredPostExtraction did not
receive deferredController.signal, making it unabortable on
stop/cancel/restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Dynamically compute JVM -Xmx based on system RAM instead of hardcoded 32g
- Reduce peak memory during session loading (inline JSON string, skip redundant clone)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Always force re-download once when both JVM and legacy extractors fail
(suggestRedownload=true), regardless of valid archive signature
- Add autoRecoveredForRedownload Set for loop protection (one attempt per archive)
- Clear loop protection on package reset (clearHybridArchiveState)
- Previous sibling-items check failed when other episodes were already
cleaned up after hybrid extraction
- Lower mini-download retry threshold from 100KB to 5KB
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Retry extraction with 2.5s delay on CRC/password errors (Windows file handle race)
- Improve auto-recovery: force re-download when known password fails (content corruption)
- Expand auto-recovery to wrong_password category for encrypted RAR5
- Add fsync after download for pre-allocated files
- Fix permanent extraction failure loop for archives with valid headers but corrupt content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Switch JVM GC from SerialGC to G1GC with MaxGCPauseMillis=50
for shorter pause times in long-lived daemon
- Increase JVM heap from 512m/8g to 1g/32g to reduce GC pressure
on systems with plenty of RAM
- Raise hybrid thread cap from floor(cpuCount/2) capped 8
to ceil(cpuCount*0.75) capped 12 — downloads are I/O-bound
and don't need much CPU headroom
- Refresh daemon process priority before each request so hybrid
extraction gets correct CPU priority even if daemon was started
with a different priority level
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Early-exit now also uses raw Content-Length as fallback when
totalBytes is unknown (provider returned fileSize=0)
- Stall handler checks if file is already complete on disk before
deleting and retrying — prevents re-download loop for files that
finished but server delayed closing the connection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add scheduler generation counter to prevent stale scheduler from
continuing after stop/start cycle
- Guard processItem stop-abort handler: skip status overwrite when a
new start() has already re-activated the session
- Yield in start() after recoverRetryableItems to let pending abort
handlers complete before evaluating item states
- Add test: rapid stop → disable provider → start must resolve
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Files with valid RAR/7z/ZIP signature are not corrupt (wrong password),
only files with invalid signature get force-redownloaded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Trust extractor CRC verdict over file size checks
- Re-queue incomplete downloads instead of just warning
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If the primary extractor (7-Zip) fails with wrong_password/checksum
error on a .rar file, automatically try the alternative extractor
(UnRAR/WinRAR) which handles RAR format natively and more reliably.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Items incorrectly marked as "completed" by the old 50% recovery threshold
persist in the session file across updates. On startup, check all completed
items: if the file on disk is smaller than expected totalBytes, reset to
"queued" so it gets re-downloaded. Also reset items whose files are missing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs in findReadyArchiveSets disk-fallback:
1. Failed items were explicitly excluded from the blocking check
(status !== "failed"), so partial downloads with "failed" status
would not block extraction. Now ANY non-completed item blocks.
2. The disk size check was only > 10 KB, allowing 627 MB partial
files of 1001 MB archives to pass. Now requires the file to be
within one allocation unit of the item's expected totalBytes.
Added findItemByDiskPath helper to look up the owning item for a
file on disk and get its expected size.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Item-Recovery incorrectly marked partially downloaded files as "completed"
when the file size was >= 50% of expected size. A 627 MB partial download
of a 1001 MB file (62.7%) would pass the check and trigger hybrid
extraction on incomplete RAR archives.
Fix: require file to be within one allocation unit (4 KB) of the expected
size instead of 50%. Also add a pre-allocation guard: if the file appears
to be at the expected size but downloadedBytes is significantly behind
(< 95%), skip recovery (likely a pre-allocated sparse file from a crash).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detect items whose targetPath has a " (N)" suffix from the previous
duplicate filename bug and rename them back to the original filename.
This fixes extraction failures for RAR split archives that were
downloaded with (1) suffix before v1.7.19.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace shared currentKeyIndex round-robin with sequential scan from
first key. All parallel items now consistently use the same key (e.g.
Key 3) until it hits a quota/error, then all move to Key 4, etc.
Previously, currentKeyIndex was shared across parallel unrestrict calls,
causing items to scatter across keys (3, 5, 7) even when Key 3 still
had capacity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After restart, reservedTargetPaths (in-memory) was empty so claimTargetPath
could not distinguish between "file belongs to this item" and "file belongs
to another item". The naive fix (allow overwrite if unclaimed) would have
risked overwriting completed files from other items.
Proper fix: restore reservedTargetPaths from persisted session data on init.
This way each item's targetPath is correctly claimed, and claimTargetPath
can safely reuse the item's own file while protecting other items' files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the app restarts (or updates) while downloads are in progress,
partial files remain on disk but reservedTargetPaths (in-memory) is empty.
claimTargetPath treated the unclaimed-but-existing file as a conflict and
appended (1) to the filename, breaking RAR split archive extraction.
Fix: if a file exists on disk but no other item has reserved the path,
allow overwriting instead of creating a duplicate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "notDebrid", "disabledServerHost", "notFree" as immediate-skip errors
(no 3x retry per key, break to next key instantly like quota errors)
- Add per-key cooldown cache (2 min) so parallel items skip recently-failed keys
instead of all starting at the same broken key
- Set cooldown on quota errors too, preventing repeated checks on exhausted keys
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shelve (15+ failures) now mimics manual stop/start behavior:
- Clears item.provider for fresh provider selection on retry
- Resets provider circuit breaker (providerFailures) for the old provider
- Reduces shelve duration from 5 min to 90s since the issue is stale
provider state, not a timing problem (manual restart works instantly)
Also adds comprehensive session-load logging:
- Logs package/item count on every session file read
- Logs errors when session file parsing fails (was silent before)
- Safety net: if primary session is empty but backup has packages,
automatically restores from backup
- Logs shutdown save with package/item counts
- Logs DownloadManager init state and cleanup policy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The inner unrestrict error handler still called recordProviderFailure()
for hosterNotAvailable errors, causing provider-level cooldown escalation
(up to 180s) even though the issue is hoster-side, not provider-side.
This made auto-retry stall while manual reset worked instantly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
hosterNotAvailable was added to isTemporaryUnrestrictError which
triggered aggressive provider-level cooldowns (up to 180s) that
blocked ALL items for the affected provider. Since items kept
failing, the cooldown never expired (15-min reset threshold never
reached), causing retries to effectively stall.
Fix: remove hosterNotAvailable from isTemporaryUnrestrictError.
It still gets normal unrestrict retry with item-level backoff
(5s -> 120s) via isUnrestrictFailure, but without provider-wide
cooldown blocking other items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move hosterNotAvailable from isPermanentLinkError to
isTemporaryUnrestrictError — hoster being unavailable is usually
transient (overload, maintenance) and should be retried with backoff
instead of immediately failing as "Link ungültig".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backup redesign (JDownloader 2 style):
- AES-256-GCM encrypted .mdd format with fixed app key
- All credentials exported (no more ***-masking), works on any PC
- Includes settings, session AND history in backup
- Backward-compatible: auto-detects legacy JSON backups
- Normalize history entries on import
- Added debridLinkApiKeys, linkSnappy credentials to sensitive keys
Hide extracted items:
- New setting: hide completed/extracted items from package item list
- Items still count in progress stats (done/total), only hidden in UI
- Default: enabled
- Toggle in settings: "Entpackte Items in Paketliste ausblenden"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace plaintext JSON export with encrypted binary format (JDownloader 2 style)
- Fixed app-internal key, works on any machine without password
- Export now includes ALL credentials (no more ***-masking), session AND history
- Add debridLinkApiKeys, linkSnappy credentials to sensitive keys list
- Backward-compatible import: auto-detects legacy JSON backups
- File extension changed from .json to .mdd
- MDD1 magic bytes + random IV + GCM auth tag for integrity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Archives with absolute internal paths (e.g. scene groups storing full
Windows paths) fail all password attempts in normal mode at ~98%, then
succeed only after the flat-mode fallback kicks in. Previously every
archive in such a package wasted all password cycles before discovering
flat mode was needed again.
Now the first successful flat-mode extraction sets a package-level flag
so subsequent archives skip the normal loop entirely and go straight to
flat-mode extraction, saving ~4x password attempts per archive.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SCENE_EPISODE_RE/JOINED_RE: use (?!\d) lookahead instead of requiring
separator after episode number, so filenames like s09e06rrp now match
- MKV-Sammelordner: skip 0-byte files from failed/partial extractions
- MKV-Sammelordner: detect same-name same-size duplicates in target dir
and skip instead of creating (2) copies; remove duplicate source file
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Retry failed wrong_password archives serially after parallel extraction
to recover from CRC mismatches caused by concurrent UnRAR I/O contention
- Stop resetting sessionDownloadedBytes on start/resume so the session
total accurately reflects all bytes downloaded since app launch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Daily traffic limits:
- Per-provider daily download limit (configurable in GB per provider)
- Per Debrid-Link API key daily limit (individual limits per key)
- Usage tracking with automatic daily reset at midnight
- Provider is skipped when daily limit reached, falls back to next provider
- Reset button per provider and per Debrid-Link key in account settings
- Hoster routing skips daily-limited providers gracefully
Debrid-Link multi-key improvements:
- Keys now display with labels (#1, #2...) and masked tokens in account list
- Option to show detailed per-key view with individual usage stats
- Keys that hit their daily limit are automatically skipped
- providerAccountId/providerAccountLabel stored per download item
Auto-sort packages by progress:
- Active packages automatically sorted to top during downloads
- Sorted by completion ratio, then downloaded bytes
- Toggle in settings (autoSortPackagesByProgress)
UI polish:
- Package column headers: flatter, more transparent design
- LinkSnappy mode label: "Login" renamed to "Web"
- Account list: new toggle for detailed Debrid-Link key display
- Account usage stats section with warning styling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- extractor: detect UnRAR "Cannot create...\..." error (archive with
leading-backslash internal paths) and retry in flat mode (-e) which
strips all paths and avoids the invalid double-separator on Windows
- types/download-manager: add providerLabel field to DownloadItem,
store full label (e.g. "Debrid-Link #1") set at unrestrict time
- App: display providerLabel in Service column (falls back to generic
provider name if label not yet set)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sanitizeFilename() is now applied before constructing outputDir and
extractDir, so names like "TMSF/4SF" no longer produce broken Windows paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a pending item has neither targetPath nor fileName (e.g. after a
reset before re-unrestrict), it is invisible to pendingItemStatus and
the disk-fallback could incorrectly start extraction with a partial file.
Add a guard that skips disk-fallback for any archive set if the package
contains such an untracked pending item.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Clear item.provider on stop/restart so provider order/routing changes
are respected on next download attempt
- Reset item.provider for all non-completed items when providerOrder or
hosterRouting changes in settings
- Cancel scheduled-start timer when queue is started manually
- Track disk-fallback hybrid-extract archives per session to prevent
infinite post-processing loop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace fixed primary/secondary/tertiary slots with unlimited ordered
providerOrder: DebridProvider[] list; supports as many accounts as needed
- Provider list reorderable via up/down buttons in Accounts settings tab
- Migration: derives order from legacy primary/secondary/tertiary if empty
- Mega-Debrid split into megadebrid-api and megadebrid-web as separate providers
- Add per-hoster routing (hosterRouting) to assign specific debrid provider per hoster
- Fix duplicate MKV files in library: filter out sample files from Sample subfolders
- Remove legacy provider-selection dropdowns from hidden settings section
- Add CSS for provider-order-list/row/num/label/actions classes
- Update debrid tests: add providerOrder: [] to use legacy fallback path
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MKV library collection now filters out sample files before moving.
Files in "sample"/"samples" directories and files with "sample" in
their name are excluded. This prevents duplicate "(2)" entries in
the library folder caused by samples having the same base name as
the real episodes (just different casing from the archive).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New settings field hosterRouting maps file hosters to specific debrid providers
- 27 known hosters predefined (Rapidgator, Uploaded, Turbobit, Nitroflare, etc.)
- Custom hoster support via prompt dialog
- Routing takes priority over default provider chain
- Falls back to normal chain on error when autoProviderFallback is enabled
- Logs routing decisions: "Hoster-Zuordnung: rapidgator → Debrid-Link"
- Full UI section in settings with add/remove/change provider per hoster
- Storage validation and normalization for hosterRouting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add LinkSnappy provider with cookie-based session auth and /api/linkgen
- Upgrade LinkSnappy download URLs from http to https (fix 425 errors)
- Add account deactivation toggle (disabledProviders in settings)
- Show account type (API/Web/Login) in provider dropdowns
- Show API key count for Debrid-Link in status label
- Fix all missing German umlauts throughout the UI
- Wider modal for textarea, compact action buttons in one row
- Debrid-Link: log which API key (#1/#2) is used for unrestrict
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New DebridLinkClient with automatic API key rotation on quota errors
(maxLink, maxLinkHost, maxData, maxDataHost, maxAttempts, maxTransfer)
- Multi-account support: comma or newline-separated API keys
- Full UI integration: account settings, provider dropdowns, summary display
- Safe fallback for undefined debridLinkApiKeys on settings upgrade
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace BrowserWindow-based login flow with Netscape cookie file import
for BestDebrid authentication. Cloudflare Turnstile captcha cannot be
solved in Electron's embedded browser, so users export cookies from
their real browser and import them here.
- Rewrite bestdebrid-web.ts: remove BrowserWindow/CDP code, add
parseNetscapeCookieFile() and importCookiesFromFile()
- Add file picker dialog for .txt cookie files in main IPC handler
- Update IPC channel from OPEN_BESTDEBRID_LOGIN to IMPORT_BESTDEBRID_COOKIES
- Update preload bridge and renderer UI with cookie import button
- Fix pLabel scope in downloadToFile (pass as parameter from processItem)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detaching the debugger before loadURL removed the registered
anti-fingerprint scripts, causing blank pages. Now keep the
debugger attached through page load and detach on window close.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use Chrome DevTools Protocol to inject scripts before page load that:
- Set navigator.webdriver to false
- Add fake navigator.plugins
- Set navigator.languages to realistic values
- Add window.chrome runtime stub
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cloudflare Turnstile blocks Electron's default user agent. Set a
Chrome user agent on both the session and webContents level so the
login captcha passes correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass cdn: 0 to 1Fichier get_token API to force direct server
downloads instead of CDN, avoiding unnecessary CDN credit usage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The running download status (e.g. "Download läuft", "Warte auf
Festplatte") now shows the full provider label including the source
mode, e.g. "Mega-Debrid (API)" instead of just "Mega-Debrid".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>