Skip to content

Conversation

@Gaisberg
Copy link
Collaborator

@Gaisberg Gaisberg commented Oct 29, 2025

Pull Request Check List

Resolves: #issue-number-here

  • Added tests for changed code.
  • Updated documentation for changed code.

Description:

Summary by CodeRabbit

Release Notes

  • New Features
    • Integrated Debrid-Link as a new downloader service, allowing users to leverage it alongside existing providers for torrent downloads with comprehensive lifecycle management.
    • Added Debrid-Link configuration settings enabling users to input API credentials for service authentication.
    • Premium membership status validation ensures seamless access to all Debrid-Link features.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

A new Debrid-Link downloader service is integrated into the application. The changes include a new API client (DebridLinkAPI), a downloader implementation (DebridLinkDownloader) with torrent and file management, configuration models, and updates to data models and router endpoints to support the new service.

Changes

Cohort / File(s) Summary
Downloader Registration
src/program/services/downloaders/__init__.py
Imports DebridLinkDownloader and registers it in the initialized services mapping.
New Debrid-Link Downloader Implementation
src/program/services/downloaders/debridlink.py
Introduces DebridLinkAPI (SmartSession-based client with base URL, rate limits, retries, proxy support, and Authorization header) and DebridLinkDownloader (torrent lifecycle management, file selection, link resolution, user info retrieval, error handling, and premium validation). Includes DebridLinkError exception type.
Data Model Updates
src/program/services/downloaders/models.py, src/routers/secure/default.py
Expands UserInfo.service and DownloaderUserInfo.service Literal types to include "debridlink".
Settings Configuration
src/program/settings/models.py
Adds DebridLinkModel with enabled and api_key fields, and debrid_link field to DownloadersModel.

Sequence Diagram

sequenceDiagram
    participant Client
    participant DebridLinkDownloader
    participant DebridLinkAPI
    participant DebridLink_Service as Debrid-Link<br/>API

    Client->>DebridLinkDownloader: validate()
    DebridLinkDownloader->>DebridLinkDownloader: _validate_settings()
    DebridLinkDownloader->>DebridLink_Service: get_user_info()
    DebridLink_Service-->>DebridLinkDownloader: user info
    DebridLinkDownloader->>DebridLinkDownloader: _validate_premium()
    DebridLinkDownloader-->>Client: validation result

    Client->>DebridLinkDownloader: add_torrent(infohash)
    DebridLinkDownloader->>DebridLink_Service: POST /torrents/add
    DebridLink_Service-->>DebridLinkDownloader: torrent_id
    DebridLinkDownloader-->>Client: torrent_id

    Client->>DebridLinkDownloader: get_instant_availability(infohash)
    DebridLinkDownloader->>DebridLinkDownloader: _process_torrent()
    DebridLinkDownloader->>DebridLink_Service: add & check availability
    DebridLink_Service-->>DebridLinkDownloader: availability data
    DebridLinkDownloader->>DebridLink_Service: delete_torrent()
    DebridLinkDownloader-->>Client: TorrentContainer or None

    Client->>DebridLinkDownloader: resolve_link(link)
    DebridLinkDownloader->>DebridLink_Service: GET /links/check
    DebridLink_Service-->>DebridLinkDownloader: resolved link info
    DebridLinkDownloader-->>Client: link details
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • debridlink.py requires careful review due to comprehensive error handling patterns, API interaction flows, torrent lifecycle management, and integration with CircuitBreakerOpen exception handling.
  • Ensure _validate_premium and premium expiry handling align with existing patterns from other downloaders.
  • Verify SmartSession configuration (rate limits, retries, proxy) consistency with other downloaders.
  • Confirm select_files implementation as no-op is intentional per Debrid-Link API design.

Possibly related PRs

  • fix: api manual scraping fixes #915: Introduces Union[int, str] torrent ID support in DownloaderBase and DebridFile.create; directly complements the string-based torrent ID signatures in DebridLinkDownloader methods (add_torrent, select_files, get_torrent_info, delete_torrent).

Suggested reviewers

  • iPromKnight
  • davidemarcoli

Poem

🐰 A new link in the debrid chain appears,
With API calls and premier years!
Torrent torrents flow through SmartSession streams,
Files resolve like downloader dreams! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description follows the required template structure with the checklist heading, Resolves line, and Description section header present. However, the Description section is entirely empty, containing no actual explanation of what changes were made, why they were made, or how the Debrid-Link downloader functionality works. While the checklist items are appropriately unchecked (indicating tests and documentation updates are not yet complete), the absence of any descriptive content makes this description largely incomplete for understanding the purpose and scope of the changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: debrid-link downloader support" is directly and clearly related to the primary change in this pull request. The changeset introduces a complete new downloader service for Debrid-Link, including the DebridLinkDownloader class, DebridLinkAPI client, and all supporting integrations across multiple files. The title accurately reflects this main addition and a teammate reviewing the commit history would immediately understand that this PR adds support for a new downloader service.
Docstring Coverage ✅ Passed Docstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/debrid_link

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fe436fc and 645a0a3.

📒 Files selected for processing (5)
  • src/program/services/downloaders/__init__.py (2 hunks)
  • src/program/services/downloaders/debridlink.py (1 hunks)
  • src/program/services/downloaders/models.py (1 hunks)
  • src/program/settings/models.py (2 hunks)
  • src/routers/secure/default.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/program/services/downloaders/debridlink.py (3)
src/program/services/downloaders/models.py (8)
  • DebridFile (70-130)
  • InvalidDebridFileException (66-67)
  • TorrentContainer (133-156)
  • TorrentInfo (159-190)
  • UserInfo (202-216)
  • create (79-121)
  • file_ids (147-149)
  • file_ids (188-190)
src/program/utils/request.py (6)
  • CircuitBreakerOpen (90-95)
  • SmartResponse (161-227)
  • SmartSession (272-704)
  • data (172-205)
  • post (585-586)
  • delete (591-592)
src/program/services/downloaders/shared.py (2)
  • DownloaderBase (17-99)
  • premium_days_left (107-122)
src/program/services/downloaders/__init__.py (1)
src/program/services/downloaders/debridlink.py (1)
  • DebridLinkDownloader (56-450)
🪛 Ruff (0.14.2)
src/program/services/downloaders/debridlink.py

113-113: Consider moving this statement to an else block

(TRY300)


114-114: Do not catch blind exception: Exception

(BLE001)


170-170: Do not catch blind exception: Exception

(BLE001)


181-181: Consider moving this statement to an else block

(TRY300)


188-189: try-except-pass detected, consider logging the exception

(S110)


188-188: Do not catch blind exception: Exception

(BLE001)


196-197: try-except-pass detected, consider logging the exception

(S110)


196-196: Do not catch blind exception: Exception

(BLE001)


206-207: try-except-pass detected, consider logging the exception

(S110)


206-206: Do not catch blind exception: Exception

(BLE001)


209-209: Do not catch blind exception: Exception

(BLE001)


214-215: try-except-pass detected, consider logging the exception

(S110)


214-214: Do not catch blind exception: Exception

(BLE001)


299-299: Avoid specifying long messages outside the exception class

(TRY003)


325-325: f-string without any placeholders

Remove extraneous f prefix

(F541)


344-344: Avoid specifying long messages outside the exception class

(TRY003)


448-448: Do not catch blind exception: Exception

(BLE001)

Comment on lines +414 to +446
resp: SmartResponse = self.api.session.get("account/infos")
self._maybe_backoff(resp)
if not resp.ok:
logger.error(f"Failed to get user info: {self._handle_error(resp)}")
return None

# Debrid-Link API v2 returns data in 'value' field
data = resp.data
if hasattr(data, "value"):
data = data.value

# Parse premium expiration
premium_expires_at = None
premium_days_left_val = None
account_type = getattr(data, "accountType", 0)

if account_type > 0: # Premium account
premium_until = getattr(data, "premiumLeft", 0)
if premium_until > 0:
premium_expires_at = datetime.fromtimestamp(premium_until)
premium_days_left_val = max(
0, (premium_expires_at - datetime.now()).days
)

return UserInfo(
service="debridlink",
username=getattr(data, "username", None),
email=getattr(data, "email", None),
user_id=getattr(data, "id", 0),
premium_status="premium" if account_type > 0 else "free",
premium_expires_at=premium_expires_at,
premium_days_left=premium_days_left_val,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix Debrid-Link premium expiry math.

premiumLeft from Debrid-Link’s API is the number of seconds remaining, not a Unix timestamp. Calling datetime.fromtimestamp(premium_until) therefore generates an expiry date in January 1970 and makes every premium account look expired (premium_days_left collapses to 0), which flows into _validate_premium() logging and the /downloader_user_info response. Please convert the duration to an absolute expiry (e.g. datetime.now(tz=UTC) + timedelta(seconds=premium_until)) before storing it. (pkg.go.dev)

🤖 Prompt for AI Agents
In src/program/services/downloaders/debridlink.py around lines 414 to 446, the
code treats data.premiumLeft as a Unix timestamp but the API returns a duration
in seconds; replace the timestamp conversion with an expiry computed from now:
set premium_expires_at = datetime.now(tz=timezone.utc) +
timedelta(seconds=premium_until) (or use the project's UTC helper) and compute
premium_days_left as max(0, (premium_expires_at -
datetime.now(tz=timezone.utc)).days); ensure the stored datetime is
timezone-aware and update any imports if needed (datetime, timezone, timedelta).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants