Fix/premiumize progress status - #1014
Open
jvvilar wants to merge 2 commits into
Open
Conversation
Premiumize reports no seeders or speed, so seeder-based stall logic falsely marked every provider download as stalled. Track progress over time instead, expose synthetic qBittorrent bytes for Cleanuparr, and show Premiumize-specific status text in the web UI.
ClientKind was set during provider polls but not written in UpdateRdData, so UI and qBittorrent logic could not detect Premiumize torrents after reloading from the database.
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Fixes false stalled reporting for Premiumize torrents during the provider download phase.
Premiumize's
/api/transfer/listonly returnsstatus,progress(0.0–1.0), andmessage— no seeders, speed, or byte counts. rdt-client previously mappedSeeders = 0, which triggered seeder-based stall logic shared with Real-Debrid and other providers. Every active Premiumize download appeared stalled in the web UI (Torrent stalled) and in the fake qBittorrent API (state: stalledDL), causing tools like Cleanuparr to treat healthy downloads as stuck and remove them after repeated stall strikes.This PR tracks Premiumize progress over time instead, uses that for stall detection (20-minute threshold), exposes synthetic byte counts for the qBittorrent API, and shows Premiumize-specific status text (
~/min) in the web UI. A follow-up fix persistsClientKindto the database so provider-specific logic actually applies after torrents are reloaded from SQLite.How I found this
I use rdt-client's qBittorrent API with Cleanuparr. Premiumize torrents in the provider download phase were consistently reported as
stalledDL, accumulating stall strikes and getting removed even while Premiumize showed them actively downloading.Investigating the code path:
PremiumizeDebridClient.Map()hardcodedSeeders = 0,Speed = 0,Bytes = 0QBittorrent.csandTorrentDtoMapper.cstreatRdSeeders < 1as stalled for non-NZB torrentsstate, so every Premiumize provider-phase download looked stuckPremiumize's API docs confirm there is no per-transfer detail endpoint with bytes or peers — only
progressas a float andstatus. There is also no way to derive real total size from progress alone during an in-flight transfer.Ruling out other causes
stalledDLfrom rdt-clientstatusTextand qBittorrent state both used the same seeder heuristicTesting the fix locally
After deploying a local build, provider-phase torrents showed as actively downloading instead of stalled. Initially the UI still displayed synthetic KB/s (derived speed formatted like a real transfer rate). That led to a second discovery:
ClientKindwas set in memory during provider polls but never persisted inUpdateRdData, so torrents reloaded from the database hadClientKind = nulland fell back to the generic status format. PersistingClientKindfixed the UI to show the intendedTorrent downloading (60% - ~0.4%/min)format.Root cause
Two related problems:
Wrong stall heuristic for Premiumize — stall detection assumed all providers expose seeder health. Premiumize does not;
Seeders = 0meant "unknown", not "dead".ClientKindnot saved —PremiumizeDebridClient.UpdateDatasettorrent.ClientKind = Provider.Premiumizein memory, butTorrentData.UpdateRdDatadid not write it to the database. Any code path that reloads torrents from SQLite (torrents.Get()for websockets, API list, qBittorrent) sawClientKind = null, missing Premiumize-specific branches for status text, synthetic qBittorrent bytes, and tracker-based stall detection.Solution
Track Premiumize
progressover time in an in-memory singleton (PremiumizeProgressTracker):RdSpeed(bytes/s) from progress deltas using a 1 GB synthetic base (for qBittorrent/Cleanuparr compatibility only — not real bytes)~/minfor web UI status text (honest estimate from the only data Premiumize provides)Real-Debrid and other providers are unchanged — they continue to use seeder-based stall logic.
Why a synthetic 1 GB total for qBittorrent
Premiumize provides no byte counts during
running. Cleanuparr and other qBittorrent consumers expectsizeanddownloadedto move with progress. A fixed 1 GB synthetic total letsdownloaded = size × progress%increment as Premiumize progress advances, without pretending we know the real release size.Why
~/mininstead of KB/s in the UIThe derived KB/s is computed from the same synthetic base (
deltaProgress × 1 GB / elapsed) and looks like a real transfer speed. Showing~/minmakes it clear we are estimating from Premiumize's done-% over time, not reporting actual bandwidth.What this PR changes
Premiumize progress tracker
IPremiumizeProgressTracker/PremiumizeProgressTracker— singleton, in-memory perTorrentIdSyntheticSizeBytes = 1_000_000_000(1 GB)StallThreshold = 20 minutesUpdate,IsStalled,GetSpeedBytesPerSec,GetProgressPerMin,RemovePremiumize debrid client
Map():Seeders = null(unknown, not zero), store rawProgressFractionrunning: update tracker, setRdSpeedfrom derived bytes/sfinished/error: remove tracker entry"error"→TorrentStatus.ErrorqBittorrent API (Cleanuparr / Sonarr / Radarr)
RdSize: synthetic 1 GBsize/downloadedRdSeeders < 1stall check withIsProviderStalled()— tracker for Premiumize, seeders for othersWeb UI status text
TorrentDtoMapperacceptsIPremiumizeProgressTracker(threaded throughTorrentsController,RemoteService)Torrent downloading (60% - ~0.2%/min)Torrent downloading (60%)Torrent stalled (60%)statusTextis preferred overtorrent-status.pipe.tsClientKind persistence (second commit)
TorrentData.UpdateRdData— persistClientKindTorrents.CaptureRdState— includeClientKindso first provider poll triggers a saveUpdateRdData_ShouldPersistClientKindNo database migration —
ClientKindcolumn already exists; existing rows populate on the next provider poll.Expected behaviour
staterunning, progress movingdownloadingTorrent downloading (60% - ~0.2%/min)running, no progress 20+ minstalledDLTorrent stalled (60%)Out of scope
messagefield in UI~/minis the current honest metric)Test plan
PremiumizeProgressTrackerTest— speed on progress increase, stall after 20 min, stall reset, removePremiumizeDebridClientTest— tracker integration onUpdateDataQBittorrentTest— Premiumize moving progress →downloading+ synthetic size; stalled tracker →stalledDLTorrentPayloadDataTests.UpdateRdData_ShouldPersistClientKinddotnet test server/RdtClient.Service.Test/RdtClient.Service.Test.csproj~/min(not fake KB/s) and are not falsely stalledstalledDLafter 20+ minutes without progress