h
This commit is contained in:
@@ -4,6 +4,8 @@ import hashlib
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterable, List, Optional, Callable, Tuple
|
||||
from urllib.parse import urlparse
|
||||
@@ -14,7 +16,7 @@ from ProviderCore.base import Provider, SearchResult
|
||||
from SYS.provider_helpers import TableProviderMixin
|
||||
from SYS.utils import sanitize_filename
|
||||
from SYS.logger import log, debug
|
||||
from SYS.models import DownloadError
|
||||
from SYS.models import DownloadError, PipeObject
|
||||
|
||||
_HOSTS_CACHE_TTL_SECONDS = 24 * 60 * 60
|
||||
|
||||
@@ -273,13 +275,22 @@ def _parse_alldebrid_magnet_id(target: str) -> Optional[int]:
|
||||
candidate = str(target or "").strip()
|
||||
if not candidate:
|
||||
return None
|
||||
if not candidate.lower().startswith(_ALD_MAGNET_PREFIX):
|
||||
return None
|
||||
try:
|
||||
magnet_id_raw = candidate[len(_ALD_MAGNET_PREFIX):].strip()
|
||||
return int(magnet_id_raw)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
# Handle various prefix variations: alldebrid:magnet: (standard), alldebrid🧲, or alldebrid:
|
||||
id_part = None
|
||||
if candidate.lower().startswith(_ALD_MAGNET_PREFIX):
|
||||
id_part = candidate[len(_ALD_MAGNET_PREFIX):].strip()
|
||||
elif candidate.startswith("alldebrid🧲"):
|
||||
id_part = candidate[len("alldebrid🧲"):].strip()
|
||||
elif candidate.lower().startswith("alldebrid:"):
|
||||
id_part = candidate[len("alldebrid:"):].strip()
|
||||
|
||||
if id_part:
|
||||
try:
|
||||
return int(id_part)
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def resolve_magnet_spec(target: str) -> Optional[str]:
|
||||
@@ -302,6 +313,50 @@ def resolve_magnet_spec(target: str) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def _looks_like_torrent_source(candidate: str) -> bool:
|
||||
value = str(candidate or "").strip()
|
||||
if not value:
|
||||
return False
|
||||
|
||||
def _clean_segment(text: str) -> str:
|
||||
cleaned = text.split("#", 1)[0].split("?", 1)[0]
|
||||
return cleaned.strip().lower()
|
||||
|
||||
paths: list[str] = []
|
||||
try:
|
||||
parsed = urlparse(value)
|
||||
except Exception:
|
||||
parsed = None
|
||||
|
||||
if parsed and parsed.path:
|
||||
paths.append(_clean_segment(parsed.path))
|
||||
paths.append(_clean_segment(value))
|
||||
|
||||
for path in paths:
|
||||
if path and is_torrent_file(path):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _extract_value(source: Any, field: str) -> Any:
|
||||
if source is None:
|
||||
return None
|
||||
if isinstance(source, dict):
|
||||
if field in source:
|
||||
return source.get(field)
|
||||
else:
|
||||
try:
|
||||
value = getattr(source, field)
|
||||
except Exception:
|
||||
value = None
|
||||
if value is not None:
|
||||
return value
|
||||
extra = getattr(source, "extra", None)
|
||||
if isinstance(extra, dict) and field in extra:
|
||||
return extra.get(field)
|
||||
return None
|
||||
|
||||
|
||||
def _dispatch_alldebrid_magnet_search(
|
||||
magnet_id: int,
|
||||
config: Dict[str, Any],
|
||||
@@ -581,7 +636,7 @@ class AllDebrid(TableProviderMixin, Provider):
|
||||
# Magnet URIs should be routed through this provider.
|
||||
TABLE_AUTO_STAGES = {"alldebrid": ["download-file"]}
|
||||
AUTO_STAGE_USE_SELECTION_ARGS = True
|
||||
URL = ("magnet:", "alldebrid:magnet:")
|
||||
URL = ("magnet:", "alldebrid:magnet:", "alldebrid:", "alldebrid🧲")
|
||||
URL_DOMAINS = ()
|
||||
|
||||
@classmethod
|
||||
@@ -662,6 +717,8 @@ class AllDebrid(TableProviderMixin, Provider):
|
||||
|
||||
spec = resolve_magnet_spec(url)
|
||||
if not spec:
|
||||
if _looks_like_torrent_source(url):
|
||||
log(f"[alldebrid] Torrent source ignored (unable to parse magnet/hash): {url}", file=sys.stderr)
|
||||
return False, None
|
||||
|
||||
cfg = self.config if isinstance(self.config, dict) else {}
|
||||
@@ -869,6 +926,106 @@ class AllDebrid(TableProviderMixin, Provider):
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def download_for_pipe_result(
|
||||
cls,
|
||||
result: Any,
|
||||
pipe_obj: Optional[PipeObject],
|
||||
config: Dict[str, Any],
|
||||
) -> Tuple[Optional[Path], Optional[str], Optional[Path]]:
|
||||
"""Download a remote provider result on behalf of add-file."""
|
||||
|
||||
download_url = (
|
||||
_extract_value(result, "path")
|
||||
or (getattr(pipe_obj, "url", None) if pipe_obj is not None else None)
|
||||
)
|
||||
download_url = str(download_url or "").strip()
|
||||
if not download_url:
|
||||
return None, None, None
|
||||
|
||||
metadata: Dict[str, Any] = {}
|
||||
maybe_meta = _extract_value(result, "full_metadata") or _extract_value(result, "metadata")
|
||||
if isinstance(maybe_meta, dict):
|
||||
metadata.update(maybe_meta)
|
||||
if pipe_obj is not None:
|
||||
pipe_meta = getattr(pipe_obj, "metadata", None)
|
||||
if isinstance(pipe_meta, dict):
|
||||
metadata.update(pipe_meta)
|
||||
|
||||
table_name = str(
|
||||
_extract_value(result, "table")
|
||||
or (getattr(pipe_obj, "provider", None) if pipe_obj is not None else None)
|
||||
or "alldebrid"
|
||||
).strip()
|
||||
if not table_name:
|
||||
table_name = "alldebrid"
|
||||
|
||||
title = str(
|
||||
_extract_value(result, "title")
|
||||
or (getattr(pipe_obj, "title", None) if pipe_obj is not None else None)
|
||||
or metadata.get("name")
|
||||
or ""
|
||||
).strip()
|
||||
if not title:
|
||||
fallback_name = None
|
||||
parsed_url = None
|
||||
try:
|
||||
parsed_url = urlparse(download_url)
|
||||
except Exception:
|
||||
parsed_url = None
|
||||
if parsed_url and parsed_url.path:
|
||||
try:
|
||||
fallback_name = Path(parsed_url.path).stem
|
||||
except Exception:
|
||||
fallback_name = None
|
||||
title = fallback_name or "alldebrid"
|
||||
|
||||
media_kind = str(
|
||||
_extract_value(result, "media_kind")
|
||||
or metadata.get("media_kind")
|
||||
or "file"
|
||||
).strip() or "file"
|
||||
|
||||
search_result = SearchResult(
|
||||
table=table_name,
|
||||
title=title,
|
||||
path=download_url,
|
||||
detail=str(_extract_value(result, "detail") or ""),
|
||||
annotations=[],
|
||||
media_kind=media_kind,
|
||||
tag=set(getattr(pipe_obj, "tag", []) or []),
|
||||
columns=[],
|
||||
full_metadata=metadata,
|
||||
)
|
||||
|
||||
if not download_url.startswith(("http://", "https://")):
|
||||
return None, None, None
|
||||
|
||||
download_dir = Path(tempfile.mkdtemp(prefix="add-file-alldebrid-"))
|
||||
try:
|
||||
provider = cls(config)
|
||||
downloaded_path = provider.download(search_result, download_dir)
|
||||
if not downloaded_path:
|
||||
shutil.rmtree(download_dir, ignore_errors=True)
|
||||
return None, None, None
|
||||
if pipe_obj is not None:
|
||||
pipe_obj.is_temp = True
|
||||
hash_hint = (
|
||||
_extract_value(result, "hash")
|
||||
or _extract_value(result, "file_hash")
|
||||
or (getattr(pipe_obj, "hash", None) if pipe_obj is not None else None)
|
||||
)
|
||||
if not hash_hint:
|
||||
for candidate in ("hash", "file_hash", "info_hash"):
|
||||
candidate_val = metadata.get(candidate)
|
||||
if isinstance(candidate_val, str) and candidate_val.strip():
|
||||
hash_hint = candidate_val.strip()
|
||||
break
|
||||
return downloaded_path, hash_hint, download_dir
|
||||
except Exception as exc:
|
||||
log(f"[alldebrid] add-file download failed: {exc}", file=sys.stderr)
|
||||
shutil.rmtree(download_dir, ignore_errors=True)
|
||||
return None, None, None
|
||||
def download_items(
|
||||
self,
|
||||
result: SearchResult,
|
||||
@@ -880,28 +1037,33 @@ class AllDebrid(TableProviderMixin, Provider):
|
||||
path_from_result: Callable[[Any], Path],
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
) -> int:
|
||||
# Check if this is a direct magnet_id from the account (e.g., from selector)
|
||||
# Check if this is a direct magnet_id from the account (from selector or custom URL scheme)
|
||||
path_id = _parse_alldebrid_magnet_id(getattr(result, "path", ""))
|
||||
full_metadata = getattr(result, "full_metadata", None) or {}
|
||||
magnet_id_direct = None
|
||||
if isinstance(full_metadata, dict):
|
||||
magnet_id_direct = full_metadata.get("magnet_id")
|
||||
if magnet_id_direct is not None:
|
||||
try:
|
||||
magnet_id = int(magnet_id_direct)
|
||||
debug(f"[download_items] Found magnet_id {magnet_id} in metadata, downloading files directly")
|
||||
cfg = config if isinstance(config, dict) else (self.config or {})
|
||||
count = self._download_magnet_by_id(
|
||||
magnet_id,
|
||||
output_dir,
|
||||
cfg,
|
||||
emit,
|
||||
progress,
|
||||
quiet_mode,
|
||||
path_from_result,
|
||||
)
|
||||
debug(f"[download_items] _download_magnet_by_id returned {count}")
|
||||
return count
|
||||
except Exception as e:
|
||||
debug(f"[download_items] Failed to download by magnet_id: {e}")
|
||||
|
||||
active_id = magnet_id_direct if magnet_id_direct is not None else path_id
|
||||
|
||||
if active_id is not None:
|
||||
try:
|
||||
magnet_id = int(active_id)
|
||||
debug(f"[download_items] Found magnet_id {magnet_id}, downloading files directly")
|
||||
cfg = config if isinstance(config, dict) else (self.config or {})
|
||||
count = self._download_magnet_by_id(
|
||||
magnet_id,
|
||||
output_dir,
|
||||
cfg,
|
||||
emit,
|
||||
progress,
|
||||
quiet_mode,
|
||||
path_from_result,
|
||||
)
|
||||
debug(f"[download_items] _download_magnet_by_id returned {count}")
|
||||
return count
|
||||
except Exception as e:
|
||||
debug(f"[download_items] Failed to download by magnet_id: {e}")
|
||||
|
||||
spec = self._resolve_magnet_spec_from_result(result)
|
||||
if not spec:
|
||||
|
||||
Reference in New Issue
Block a user