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>
Replace the raw login:password textarea with a proper form-based UI:
- Individual Login + Password input fields with "Hinzufügen" button
- List of configured accounts with masked logins and "Entfernen" button
- Duplicate login detection
- Storage format unchanged (megaCredentials stays login:password pairs)
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>