feat: résolution Premium → Debrid → Free et plugins Real-Debrid / AllDebrid (MAT-142)#183
feat: résolution Premium → Debrid → Free et plugins Real-Debrid / AllDebrid (MAT-142)#183mpiton wants to merge 4 commits into
Conversation
…ster Link resolution used to pick the first plugin that claimed a URL. It now walks the resolution tiers in the order set under Settings → Downloads and records why each rung declined, so an exhausted cascade names every tier it tried instead of failing as a bare "no source". A hand-edited config can no longer disable a rung: an unknown tier fails the load loudly, and a partial or duplicated list is normalised back to the full three-tier order. Refs MAT-142
Checksums come from each plugin's CI release SHA256SUMS, not a local build: the WASM artefacts are not byte-reproducible off the runner. Both entries require Vortex 0.3.0, the release that introduces the Premium → Debrid → Free cascade. On an older host a debrid plugin is just another URL claimant and would outrank the hoster plugin that owns the domain. Refs MAT-142
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
📝 WalkthroughWalkthroughAdds a configurable Premium → Debrid → Free link-resolution cascade, persists and exposes its ordering, discovers eligible Debrid plugins and accounts, reports exhausted tiers, supports Debrid-to-free fallback, and provides a sortable localized settings control. ChangesResolution cascade
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant SettingsView
participant TauriIPC
participant ConfigStore
participant CommandBus
participant PluginLoader
participant AccountRotator
User->>SettingsView: reorder resolution tiers
SettingsView->>TauriIPC: submit resolution_order
TauriIPC->>ConfigStore: validate and persist configuration
CommandBus->>ConfigStore: read resolution_order
CommandBus->>PluginLoader: check eligible Debrid plugins
CommandBus->>AccountRotator: select account for chosen tier
AccountRotator-->>CommandBus: return module and account_id
CommandBus-->>User: return resolved link or tier exhaustion details
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src-tauri/src/application/commands/resolve_links_tiers.rs (1)
62-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPremium exhaustion aborts the cascade — add a test for this path.
premium_tierreturnsErr(AppError::Domain(...))onAllExhausted(line 72), which?propagates immediately, aborting the entire cascade. This is the documented design ("the caller waits for the cooldown rather than silently downgrading"), but no test exercises this path. The existing tests only coverNoneAvailable(no accounts) andQuotaExhaustedfor debrid. A premium account in cooldown should error out rather than fall through to Debrid/Free — this behavior should be explicitly tested to prevent regressions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src-tauri/src/application/commands/resolve_links_tiers.rs` around lines 62 - 78, Add a test covering the `premium_tier` `NextAccountOutcome::AllExhausted` path, configuring a premium account in cooldown and invoking the cascade. Assert that it returns `Err(AppError::Domain(...))` and does not fall through to Debrid or Free tiers, preserving the documented cooldown behavior.src-tauri/src/application/commands/resolve_links_tiers_tests.rs (1)
118-128: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
AccountType::Debridis used for all accounts including the hoster premium account.The
account()helper hardcodesAccountType::Debridfor every account, including the premium hoster account at line 156 (account("premium-1", HOSTER, ...)). If the account rotator ever filters byAccountType, these tests would silently produce wrong results. Consider parameterizingAccountTypeor at least using a more appropriate type for hoster accounts.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src-tauri/src/application/commands/resolve_links_tiers_tests.rs` around lines 118 - 128, Update the account helper to accept an AccountType parameter and pass it into Account::new, then provide the appropriate type at each call site, including AccountType::Debrid for debrid accounts and the hoster account’s correct type for premium-1. Preserve the existing status setup and test behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src-tauri/src/adapters/driven/plugin/extism_loader.rs`:
- Around line 473-476: Update the plugin selection logic around the infos
sorting and resolve_url flow to exclude PluginCategory::Debrid entries from URL
ownership candidates entirely, rather than merely sorting them last. Preserve
Debrid discovery through debrid_plugins_covering for its fallback tier, allowing
unclaimed HTTP URLs to reach HttpModule without requiring a Debrid account.
---
Nitpick comments:
In `@src-tauri/src/application/commands/resolve_links_tiers_tests.rs`:
- Around line 118-128: Update the account helper to accept an AccountType
parameter and pass it into Account::new, then provide the appropriate type at
each call site, including AccountType::Debrid for debrid accounts and the hoster
account’s correct type for premium-1. Preserve the existing status setup and
test behavior.
In `@src-tauri/src/application/commands/resolve_links_tiers.rs`:
- Around line 62-78: Add a test covering the `premium_tier`
`NextAccountOutcome::AllExhausted` path, configuring a premium account in
cooldown and invoking the cascade. Assert that it returns
`Err(AppError::Domain(...))` and does not fall through to Debrid or Free tiers,
preserving the documented cooldown behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c73772e1-839d-4f00-83ae-210ca9d92394
📒 Files selected for processing (26)
CHANGELOG.mdregistry/registry.tomlsrc-tauri/src/adapters/driven/config/toml_config_store.rssrc-tauri/src/adapters/driven/plugin/extism_loader.rssrc-tauri/src/adapters/driving/tauri_ipc.rssrc-tauri/src/application/commands/mod.rssrc-tauri/src/application/commands/resolve_links.rssrc-tauri/src/application/commands/resolve_links_tiers.rssrc-tauri/src/application/commands/resolve_links_tiers_tests.rssrc-tauri/src/application/commands/tests_support.rssrc-tauri/src/domain/error.rssrc-tauri/src/domain/model/config.rssrc-tauri/src/domain/ports/driven/plugin_loader.rssrc/components/__tests__/ClipboardIndicator.test.tsxsrc/hooks/__tests__/useAppEffects.test.tssrc/i18n/locales/en.jsonsrc/i18n/locales/fr.jsonsrc/layouts/__tests__/AppLayout.test.tsxsrc/stores/__tests__/settingsStore.test.tssrc/types/settings.tssrc/views/LinkGrabberView/__tests__/LinkGrabberView.test.tsxsrc/views/SettingsView/DownloadsSection.tsxsrc/views/SettingsView/ResolutionOrderSetting.tsxsrc/views/SettingsView/__tests__/ResolutionOrderSetting.test.tsxsrc/views/SettingsView/__tests__/Sections.test.tsxsrc/views/SettingsView/__tests__/SettingsView.test.tsx
Merging this PR will degrade performance by 7.84%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | from_status_code_500 |
125.6 ns | 154.7 ns | -18.85% |
| ❌ | apply_patch_many_fields |
4.7 µs | 5.7 µs | -17.35% |
| ❌ | from_status_code_404 |
154.7 ns | 183.9 ns | -15.86% |
| ❌ | reject_invalid |
522.8 ns | 581.1 ns | -10.04% |
| ⚡ | from_status_code_200 |
123.3 ns | 94.2 ns | +30.97% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/mat-142-debrid-services (f9f0cec) with main (e4f01c9)
There was a problem hiding this comment.
cubic analysis
All reported issues were addressed across 26 files
Linked issue analysis
Linked issue: MAT-142: [Lot 4] Intégrer les services debrid Real-Debrid et AllDebrid
| Status | Acceptance criteria | Notes |
|---|---|---|
| R-01 — An added Real‑Debrid or AllDebrid account is validated against the real service and shows traffic remaining + expiration. | Account validation and expiry are implemented and surfaced. The PR explicitly documents a scoped deviation: services do not expose a reliable scalar bytes quota, so only expiration is reported and 'remaining traffic' degrades to “unknown”. | |
| ✅ | R-02 — A link covered by a configured debrid is unrestrict’ed and downloaded from the direct CDN URL (never from the page origin). | Debrid plugins return direct CDN URLs and the host uses that module/account for the download; tests exercise the cascade and the extractor returns direct_url. Plugin repos include smoke/native tests and CI for their WASM. |
| ✅ | R-03 — The Premium → Debrid → Free order is respected and reconfigurable; hoster-specific premium accounts outrank debrid by default. | A domain-level ResolutionTier and default order were added, normalization and persistence are implemented, a Settings UI to reorder tiers was added, and unit/integration tests validate default precedence and reordering behaviour. |
| ✅ | R-04 — Debrid failures (quota/hoster not covered/panic) fall through cleanly to the next tier with explicit reasons; no false “success”. | Cascade accumulates per‑tier decline reasons and returns a ResolutionExhausted error; host surfaces the composed explanation rather than a bare ‘no source’. Tests assert the exhausted cascade message includes the rungs tried. |
| ✅ | R-05 — Tokens live in the keyring only; no secrets persisted in SQLite, config or logs. | Plugins and host are using the credential/keyring API (get_credential) and the PR states tokens are not logged or persisted. Tests and test harness use credential stores that exercise the keyring path. |
| R-06 — Tests: handlers with in‑memory mocks for both services, native plugin tests, CI green, registry with checksums in CI. | The PR adds extensive domain and UI tests and the plugin repos are reported as having native/smoke tests and green CI. One explicit gap is noted: there is no automated test for the specific resolver ordering change that forces Debrid plugins to be considered last in resolve_url (an uncovered, low‑risk sorting fixture). |
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
The link check picks a debrid while it is healthy and persists the module and account, but resolution at download time only rotates within that module and HosterNoFile is not rotatable. A quota spent since the check, a hoster dropped from coverage, or an outage therefore left the download in error instead of trying the next configured tier (MAT-142 R-04). Only the free rung is retried, and only for Debrid modules. Premium is deliberately not: any premium account for the hoster was already offered this link at check time and lost to the debrid. CaptchaRequired stays unwrapped so the engine can still answer the challenge; anything else reports both reasons through ResolutionExhausted. Also bounds resolution tier identifiers arriving over IPC, since the parse error quotes what it rejected, and drops the per-call Vec allocation in normalize_resolution_order.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src-tauri/src/application/commands/hoster_download_source.rs`:
- Around line 111-136: Update free_tier_source to require the plugin returned by
plugins.resolve_url for the URL to be in the Hoster category before calling
extract_hoster_link. Preserve the existing same-module exclusion and
debrid_error fallback, and ensure other Debrid-category plugins return the
original debrid error without being invoked; add coverage for two distinct
Debrid plugins if the existing test setup permits it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 076faa04-f37b-4cb7-9325-2748c18588b2
📒 Files selected for processing (7)
CHANGELOG.mdsrc-tauri/src/adapters/driving/tauri_ipc.rssrc-tauri/src/application/commands/hoster_download_source.rssrc-tauri/src/application/commands/hoster_download_source_tests.rssrc-tauri/src/application/commands/resolve_links_tiers_tests.rssrc-tauri/src/domain/error.rssrc-tauri/src/domain/model/config.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- CHANGELOG.md
- src-tauri/src/domain/error.rs
- src-tauri/src/adapters/driving/tauri_ipc.rs
- src-tauri/src/application/commands/resolve_links_tiers_tests.rs
- src-tauri/src/domain/model/config.rs
There was a problem hiding this comment.
All reported issues were addressed across 7 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
resolve_url keeps debrid candidates and merely orders them last, so for a URL no hoster claims the runner-up is another debrid. The same-module guard let that through: the fall-through called it without credentials and labelled the refusal "free", naming a rung that was never tried. Requires PluginCategory::Hoster on the resolved plugin, so the debrid's own error stands when no free rung covers the URL.
CodSpeed: what is real and what is notTwo separate things in that report.
I already took the one free win here: The only way to get the rest back is making the field a fixed The three
|
Ferme MAT-142. Deux plugins debrid, une cascade de résolution configurable, et les entrées registry correspondantes.
Ce qui change côté hôte
La résolution d'un lien ne prenait plus le premier plugin qui revendiquait l'URL. Elle parcourt maintenant les niveaux dans l'ordre défini dans Settings → Downloads (
Premium → Debrid → Freepar défaut) et retient, pour chaque lien, le plugin et le compte à utiliser. Chaque niveau qui décline enregistre pourquoi, donc une cascade épuisée nomme les rungs essayés au lieu d'échouer sur un « aucune source » nu (R-04).resolve_urltrie désormais les pluginsDebriden dernier : sur l'ordre alphabétique seul,vortex-mod-alldebridvolait les URLs quevortex-mod-mediafirepossède.Un
config.tomlédité à la main ne peut plus casser la cascade : un niveau inconnu fait échouer le chargement bruyamment, une liste partielle ou dupliquée est renormalisée vers les trois niveaux.Les deux plugins
vortex-mod-realdebridv1.0.0 — 58 testsvortex-mod-alldebridv1.0.0 — 48 testsLes deux passent le lien à leur API, renvoient l'URL CDN directe, et ne retournent jamais vers la page d'origine (R-02). Le token vit dans le keyring, lu via
get_credential; il n'est ni loggé, ni persisté, ni recopié dans un message d'erreur — y compris quand serde échoue sur le payload de credential, où l'erreur d'origine est volontairement jetée parce qu'elle cite la valeur fautive (R-05).Les codes d'erreur de chaque service sont mappés sur les codes machine de l'hôte pour que la cascade sache pourquoi un niveau décline. Le sens du mapping compte : un code
ACCOUNT_*fait cesser à l'hôte d'utiliser le compte pour tous les hosters, donc une panne qui appartient au hoster ou au service debrid n'est jamais classée comme telle. Deux codes AllDebrid étaient du mauvais côté et ont été corrigés —LINK_HOST_UNAVAILABLE(hoster HS) etNO_SERVER(AllDebrid saturé) désactivaient un abonnement payant valide pour une panne tierce.AllDebrid répond HTTP 200 sur presque tous ses échecs, donc l'enveloppe est parsée avant que quoi que ce soit soit traité comme un succès.
Registry
Les checksums viennent des
SHA256SUMSproduits par la CI de chaque plugin, pas d'un build local : les artefacts WASM ne sont pas reproductibles bit à bit hors runner. Vérifiés en retéléchargeant les assets publiés.Les deux entrées exigent Vortex 0.3.0 — c'est la version qui apporte la cascade. Sur un hôte plus ancien, un plugin debrid n'est qu'un revendiquant d'URL de plus et passerait devant le plugin hoster qui possède le domaine.
Écart assumé sur R-01
R-01 demande « trafic restant + expiration ». Seule l'expiration est remontée. Aucun des deux services n'expose un quota scalaire en octets :
limitedHostersQuotasd'AllDebrid compte des téléchargements par hoster et par jour, et les limites Real-Debrid sont une map par hôte derrière/traffic. L'UI formate ces champs avecformatBytes, donc les remonter afficherait un chiffre faux. La vue Accounts dégrade proprement sur « inconnu ». Documenté dans les deux READMEs.Tests
1817 tests Rust et 747 tests front passent. Chaque plugin a des tests de parsing sur payloads capturés plus un smoke test qui charge le vrai artefact WASM release et appelle chaque export à travers l'ABI Extism réelle, host functions stubbées comprises. Les deux CI plugin sont vertes et les releases v1.0.0 publiées.
Ce que les tests ne couvrent pas : l'ordonnancement
Debriden dernier dansresolve_urln'a pas de test automatisé — le dépôt hôte n'a aucune fixture WASM et en ajouter une pour une ligne de tri coûterait plus que ce qu'elle protège. La cascade elle-même, qui est le vrai mécanisme de R-03, est testée avec des mocks in-memory des deux services.Summary by cubic
Adds debrid support with a configurable Premium → Debrid → Free resolution order and safer fallbacks with clear reasons. Addresses MAT-142 by integrating
vortex-mod-realdebridandvortex-mod-alldebridto expand host coverage.New Features
vortex-mod-realdebridandvortex-mod-alldebridreturn direct CDN links via keyring-held tokens, map service errors to host codes, and parse AllDebrid’s 200-error envelope.resolution_orderadded to IPC and config with validation and normalization.Bug Fixes
ResolutionExhaustedlists all reasons. Captcha challenges pass through, and premium never downgrades to free.Written for commit f9f0cec. Summary will update on new commits.
Summary by CodeRabbit