Skip to content

Fix: Download to Debrid and automatic RD playback routing for torrent-URL-only indexer results #167

@hbbernardo

Description

@hbbernardo

Fix: Download to Debrid and automatic RD playback routing for torrent-URL-only indexer results

Problem

Indexers like 1337x and TorrentGalaxy (via Prowlarr) return results that contain only a .torrent file URL — no magnet link and no info hash in the search result metadata. This caused a chain of failures:

1. "Download to Debrid" always failed with "Could not resolve torrent details"

_download_to_debrid in source_select.py only checked three sources for the info hash:

  • source.infoHash directly
  • Extracting from a magnet field
  • Extracting from URL if it starts with magnet:?

When none of these were present (Prowlarr .torrent URL results), it immediately showed the error notification without attempting to fetch the torrent file.

2. Root bug in get_magnet_from_uri

get_magnet_from_uri in debrid_utils.py already correctly downloads and parses .torrent files using extract_torrent_metadata, successfully extracting both the magnet link and info hash. However, when returning the result it discarded the magnet by returning "" instead of the extracted value:

# Before (bug):
return "", info_hash, torrent_url

# After (fix):
return magnet, info_hash, torrent_url

This caused every subsequent call to the same URL to re-fetch the torrent file — which then timed out on Prowlarr's rate-limited download proxy.

3. No automatic debrid routing after manual "Download to Debrid"

Even after successfully sending a torrent to a debrid service via the context menu, selecting the result normally always routed to Jacktorr instead of the debrid service. This happened because raw Torrent results from Prowlarr have no debridType set, so resolve_playback_url skipped the debrid path entirely and fell through to get_torrent_url.


Solution

lib/utils/debrid/debrid_utils.py

1. Fix get_magnet_from_uri to return the extracted magnet:

# Before:
return "", info_hash, torrent_url

# After:
return magnet, info_hash, torrent_url

2. Improve error visibility in get_debrid_direct_url:

except ValueError as e:
    kodilog(f"Unknown debrid type: {debrid_type}: {e}")
    return None
except Exception as e:
    kodilog(f"get_debrid_direct_url failed: {e}")
    return None

lib/gui/source_select.py

1. Add get_magnet_from_uri fallback in _download_to_debrid:

# Fallback: fetch torrent URL to extract info_hash
# (needed when indexer returns a .torrent file URL instead of a magnet)
if not info_hash and url:
    kodilog(f"Fetching torrent URL to extract info_hash: {url}")
    _, info_hash, _ = get_magnet_from_uri(url)
    if info_hash:
        info_hash = info_hash.lower()

2. Cache the extracted hash by IMDB ID after successful extraction:

# Cache the hash keyed by IMDB ID for future automatic playback routing
try:
    from lib.utils.general.utils import set_cached
    imdb_id = (self.item_information.get("ids") or {}).get("imdb_id", "")
    if imdb_id:
        set_cached(info_hash, f"rd_hash_{imdb_id}")
        kodilog(f"Cached RD hash for {imdb_id}: {info_hash[:12]}")
except Exception:
    pass

3. Add get_magnet_from_uri to imports:

from lib.utils.debrid.debrid_utils import add_source_to_debrid, get_source_status, get_magnet_from_uri

lib/utils/player/utils.py

Add automatic debrid routing in resolve_playback_url:

When a Torrent result has no debridType, check if its hash exists in the user's debrid account using the cached IMDB ID lookup. If found, route directly to debrid instead of Jacktorr:

# If no debrid_type, check if hash exists in debrid account
if not debrid_type:
    info_hash = data.get("info_hash", "") or ""
    if not info_hash:
        magnet = data.get("magnet", "") or ""
        if magnet:
            info_hash = get_info_hash_from_magnet(magnet).lower()

    # Check cache for hash stored during "Download to Debrid" (keyed by IMDB ID)
    if not info_hash:
        try:
            from lib.utils.general.utils import get_cached
            ids = data.get("ids") or {}
            imdb_id = ids.get("imdb_id", "") if isinstance(ids, dict) else ""
            if imdb_id:
                cached_hash = get_cached(f"rd_hash_{imdb_id}")
                if cached_hash and isinstance(cached_hash, str):
                    info_hash = cached_hash
                    kodilog(f"Using cached debrid hash for {imdb_id}: {info_hash[:12]}")
        except Exception as e:
            kodilog(f"Debrid hash cache lookup failed: {e}")

    if info_hash:
        try:
            from lib.utils.general.utils import is_rd_enabled
            if is_rd_enabled():
                from lib.clients.debrid.realdebrid import RealDebridHelper
                torrent_info = RealDebridHelper().client.get_available_torrent(info_hash)
                if torrent_info:
                    kodilog(f"Hash {info_hash[:12]} found in debrid account, routing to debrid")
                    data["debrid_type"] = "RealDebrid"
                    data["info_hash"] = info_hash
                    debrid_url = get_debrid_url(data, "RealDebrid", is_pack)
                    if debrid_url:
                        return data
        except Exception as e:
            kodilog(f"Debrid account check failed: {e}")

User Workflow (after fix)

For rare/low-seeder torrents found via Prowlarr that are not yet cached on a debrid service:

  1. Find the result in Jacktook source select screen
  2. Open context menu → "Download to Debrid"
  3. Wait for "Added to [debrid] cloud" notification
  4. Wait for the debrid service to finish downloading the torrent
  5. Select the result normally → plays automatically via debrid CDN

The hash is cached by IMDB ID so subsequent plays require no extra steps.


Tested On

  • LibreELEC nightly (RPi5.aarch64)
  • Jacktook v1.7.1
  • Prowlarr indexers: 1337x, TorrentGalaxy

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions