Add YAPF style + ignore, and format tracked Python files
This commit is contained in:
164
API/alldebrid.py
164
API/alldebrid.py
@@ -30,19 +30,24 @@ _SUPPORTED_HOSTERS_CACHE: Optional[Dict[str, Dict[str, Any]]] = None
|
||||
_CACHE_TIMESTAMP: float = 0
|
||||
_CACHE_DURATION: float = 3600 # 1 hour
|
||||
|
||||
|
||||
# Cache for init-time connectivity checks (api_key fingerprint -> (ok, reason))
|
||||
_INIT_CHECK_CACHE: Dict[str, Tuple[bool, Optional[str]]] = {}
|
||||
_INIT_CHECK_CACHE: Dict[str,
|
||||
Tuple[bool,
|
||||
Optional[str]]] = {}
|
||||
|
||||
|
||||
def _ping_alldebrid(base_url: str) -> Tuple[bool, Optional[str]]:
|
||||
"""Ping the AllDebrid API base URL (no API key required)."""
|
||||
try:
|
||||
url = str(base_url or "").rstrip("/") + "/ping"
|
||||
with HTTPClient(timeout=10.0, headers={"User-Agent": "downlow/1.0"}) as client:
|
||||
with HTTPClient(timeout=10.0,
|
||||
headers={
|
||||
"User-Agent": "downlow/1.0"
|
||||
}) as client:
|
||||
response = client.get(url)
|
||||
data = json.loads(response.content.decode("utf-8"))
|
||||
if data.get("status") == "success" and data.get("data", {}).get("ping") == "pong":
|
||||
if data.get("status") == "success" and data.get("data",
|
||||
{}).get("ping") == "pong":
|
||||
return True, None
|
||||
return False, "Invalid API response"
|
||||
except Exception as exc:
|
||||
@@ -107,10 +112,12 @@ class AllDebridClient:
|
||||
def _request(
|
||||
self,
|
||||
endpoint: str,
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
params: Optional[Dict[str,
|
||||
Any]] = None,
|
||||
*,
|
||||
method: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
) -> Dict[str,
|
||||
Any]:
|
||||
"""Make a request to AllDebrid API.
|
||||
|
||||
Args:
|
||||
@@ -157,12 +164,19 @@ class AllDebridClient:
|
||||
except Exception as req_err:
|
||||
# Log detailed error info
|
||||
logger.error(
|
||||
f"[AllDebrid] Request error to {endpoint}: {req_err}", exc_info=True
|
||||
f"[AllDebrid] Request error to {endpoint}: {req_err}",
|
||||
exc_info=True
|
||||
)
|
||||
if hasattr(req_err, "response") and req_err.response is not None: # type: ignore
|
||||
if hasattr(req_err,
|
||||
"response"
|
||||
) and req_err.response is not None: # type: ignore
|
||||
try:
|
||||
error_body = req_err.response.content.decode("utf-8") # type: ignore
|
||||
logger.error(f"[AllDebrid] Response body: {error_body[:200]}")
|
||||
error_body = req_err.response.content.decode(
|
||||
"utf-8"
|
||||
) # type: ignore
|
||||
logger.error(
|
||||
f"[AllDebrid] Response body: {error_body[:200]}"
|
||||
)
|
||||
except:
|
||||
pass
|
||||
raise
|
||||
@@ -172,7 +186,9 @@ class AllDebridClient:
|
||||
|
||||
# Check for API errors
|
||||
if data.get("status") == "error":
|
||||
error_msg = data.get("error", {}).get("message", "Unknown error")
|
||||
error_msg = data.get("error",
|
||||
{}).get("message",
|
||||
"Unknown error")
|
||||
logger.error(f"[AllDebrid] API error: {error_msg}")
|
||||
raise AllDebridError(f"AllDebrid API error: {error_msg}")
|
||||
|
||||
@@ -200,11 +216,15 @@ class AllDebridClient:
|
||||
raise AllDebridError(f"Invalid URL: {link}")
|
||||
|
||||
try:
|
||||
response = self._request("link/unlock", {"link": link})
|
||||
response = self._request("link/unlock",
|
||||
{
|
||||
"link": link
|
||||
})
|
||||
|
||||
# Check if unlock was successful
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
|
||||
# AllDebrid returns the download info in 'link' field
|
||||
if "link" in data:
|
||||
@@ -251,10 +271,18 @@ class AllDebridClient:
|
||||
|
||||
for category in ("hosts", "streams", "redirectors"):
|
||||
values = domains.get(category)
|
||||
if isinstance(values, list) and any(str(d).lower() == host for d in values):
|
||||
return {"supported": True, "category": category, "domain": host}
|
||||
if isinstance(values,
|
||||
list) and any(str(d).lower() == host for d in values):
|
||||
return {
|
||||
"supported": True,
|
||||
"category": category,
|
||||
"domain": host
|
||||
}
|
||||
|
||||
return {"supported": False, "domain": host}
|
||||
return {
|
||||
"supported": False,
|
||||
"domain": host
|
||||
}
|
||||
except AllDebridError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
@@ -274,7 +302,8 @@ class AllDebridClient:
|
||||
response = self._request("user")
|
||||
|
||||
if response.get("status") == "success":
|
||||
return response.get("data", {})
|
||||
return response.get("data",
|
||||
{})
|
||||
|
||||
return {}
|
||||
except AllDebridError:
|
||||
@@ -296,8 +325,10 @@ class AllDebridClient:
|
||||
response = self._request("hosts/domains")
|
||||
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
return data if isinstance(data, dict) else {}
|
||||
data = response.get("data",
|
||||
{})
|
||||
return data if isinstance(data,
|
||||
dict) else {}
|
||||
|
||||
return {}
|
||||
except AllDebridError:
|
||||
@@ -331,10 +362,14 @@ class AllDebridClient:
|
||||
try:
|
||||
# API endpoint: POST /v4/magnet/upload
|
||||
# Format: /magnet/upload?apikey=key&magnets[]=magnet:?xt=...
|
||||
response = self._request("magnet/upload", {"magnets[]": magnet_uri})
|
||||
response = self._request("magnet/upload",
|
||||
{
|
||||
"magnets[]": magnet_uri
|
||||
})
|
||||
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
magnets = data.get("magnets", [])
|
||||
|
||||
if magnets and len(magnets) > 0:
|
||||
@@ -356,7 +391,10 @@ class AllDebridClient:
|
||||
except Exception as exc:
|
||||
raise AllDebridError(f"Failed to submit magnet: {exc}")
|
||||
|
||||
def magnet_status(self, magnet_id: int, include_files: bool = False) -> Dict[str, Any]:
|
||||
def magnet_status(self,
|
||||
magnet_id: int,
|
||||
include_files: bool = False) -> Dict[str,
|
||||
Any]:
|
||||
"""Get status of a magnet currently being processed or stored.
|
||||
|
||||
Status codes:
|
||||
@@ -396,13 +434,18 @@ class AllDebridClient:
|
||||
self.base_url = self.BASE_URL_V41
|
||||
|
||||
try:
|
||||
response = self._request("magnet/status", {"id": str(magnet_id)})
|
||||
response = self._request("magnet/status",
|
||||
{
|
||||
"id": str(magnet_id)
|
||||
})
|
||||
finally:
|
||||
self.base_url = old_base
|
||||
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
magnets = data.get("magnets", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
magnets = data.get("magnets",
|
||||
{})
|
||||
|
||||
# Handle both list and dict responses
|
||||
if isinstance(magnets, list) and len(magnets) > 0:
|
||||
@@ -439,7 +482,8 @@ class AllDebridClient:
|
||||
if response.get("status") != "success":
|
||||
return []
|
||||
|
||||
data = response.get("data", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
magnets = data.get("magnets", [])
|
||||
|
||||
if isinstance(magnets, list):
|
||||
@@ -459,8 +503,12 @@ class AllDebridClient:
|
||||
raise AllDebridError(f"Failed to list magnets: {exc}")
|
||||
|
||||
def magnet_status_live(
|
||||
self, magnet_id: int, session: Optional[int] = None, counter: int = 0
|
||||
) -> Dict[str, Any]:
|
||||
self,
|
||||
magnet_id: int,
|
||||
session: Optional[int] = None,
|
||||
counter: int = 0
|
||||
) -> Dict[str,
|
||||
Any]:
|
||||
"""Get live status of a magnet using delta sync mode.
|
||||
|
||||
The live mode endpoint provides real-time progress by only sending
|
||||
@@ -493,7 +541,10 @@ class AllDebridClient:
|
||||
old_base = self.base_url
|
||||
self.base_url = self.BASE_URL_V41
|
||||
try:
|
||||
payload: Dict[str, Any] = {"id": str(magnet_id)}
|
||||
payload: Dict[str,
|
||||
Any] = {
|
||||
"id": str(magnet_id)
|
||||
}
|
||||
if session is not None:
|
||||
payload["session"] = str(int(session))
|
||||
payload["counter"] = str(int(counter))
|
||||
@@ -502,7 +553,8 @@ class AllDebridClient:
|
||||
self.base_url = old_base
|
||||
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
magnets = data.get("magnets", [])
|
||||
|
||||
# For specific magnet id, return the first match from the array.
|
||||
@@ -552,7 +604,8 @@ class AllDebridClient:
|
||||
response = self._request("magnet/files", params)
|
||||
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
magnets = data.get("magnets", [])
|
||||
|
||||
# Convert list to dict keyed by ID (as string) for easier access
|
||||
@@ -603,10 +656,14 @@ class AllDebridClient:
|
||||
if not hash_value or len(hash_value) < 32:
|
||||
return None
|
||||
|
||||
response = self._request("magnet/instant", {"magnet": hash_value})
|
||||
response = self._request("magnet/instant",
|
||||
{
|
||||
"magnet": hash_value
|
||||
})
|
||||
|
||||
if response.get("status") == "success":
|
||||
data = response.get("data", {})
|
||||
data = response.get("data",
|
||||
{})
|
||||
# Returns 'files' array if available, or empty
|
||||
return data.get("files", [])
|
||||
|
||||
@@ -635,7 +692,10 @@ class AllDebridClient:
|
||||
raise AllDebridError(f"Invalid magnet ID: {magnet_id}")
|
||||
|
||||
try:
|
||||
response = self._request("magnet/delete", {"id": str(magnet_id)})
|
||||
response = self._request("magnet/delete",
|
||||
{
|
||||
"id": str(magnet_id)
|
||||
})
|
||||
|
||||
if response.get("status") == "success":
|
||||
return True
|
||||
@@ -664,7 +724,8 @@ def _get_cached_supported_hosters(api_key: str) -> Set[str]:
|
||||
now = time.time()
|
||||
|
||||
# Return cached result if still valid
|
||||
if _SUPPORTED_HOSTERS_CACHE is not None and (now - _CACHE_TIMESTAMP) < _CACHE_DURATION:
|
||||
if _SUPPORTED_HOSTERS_CACHE is not None and (now -
|
||||
_CACHE_TIMESTAMP) < _CACHE_DURATION:
|
||||
return set(_SUPPORTED_HOSTERS_CACHE.keys())
|
||||
|
||||
# Fetch fresh list from API
|
||||
@@ -686,11 +747,15 @@ def _get_cached_supported_hosters(api_key: str) -> Set[str]:
|
||||
all_domains.update(hosters_dict["streams"])
|
||||
|
||||
# Add redirectors
|
||||
if "redirectors" in hosters_dict and isinstance(hosters_dict["redirectors"], list):
|
||||
if "redirectors" in hosters_dict and isinstance(hosters_dict["redirectors"],
|
||||
list):
|
||||
all_domains.update(hosters_dict["redirectors"])
|
||||
|
||||
# Cache as dict for consistency
|
||||
_SUPPORTED_HOSTERS_CACHE = {domain: {} for domain in all_domains}
|
||||
_SUPPORTED_HOSTERS_CACHE = {
|
||||
domain: {}
|
||||
for domain in all_domains
|
||||
}
|
||||
_CACHE_TIMESTAMP = now
|
||||
|
||||
if all_domains:
|
||||
@@ -905,7 +970,8 @@ def unlock_link_cmdlet(result: Any, args: Sequence[str], config: Dict[str, Any])
|
||||
0 on success, 1 on failure
|
||||
"""
|
||||
|
||||
def _extract_link_from_args_or_result(result_obj: Any, argv: Sequence[str]) -> Optional[str]:
|
||||
def _extract_link_from_args_or_result(result_obj: Any,
|
||||
argv: Sequence[str]) -> Optional[str]:
|
||||
# Prefer an explicit URL in args.
|
||||
for a in argv or []:
|
||||
if isinstance(a, str) and a.startswith(("http://", "https://")):
|
||||
@@ -923,7 +989,9 @@ def unlock_link_cmdlet(result: Any, args: Sequence[str], config: Dict[str, Any])
|
||||
# Current config format
|
||||
try:
|
||||
provider_cfg = cfg.get("provider") if isinstance(cfg, dict) else None
|
||||
ad_cfg = provider_cfg.get("alldebrid") if isinstance(provider_cfg, dict) else None
|
||||
ad_cfg = provider_cfg.get("alldebrid"
|
||||
) if isinstance(provider_cfg,
|
||||
dict) else None
|
||||
api_key = ad_cfg.get("api_key") if isinstance(ad_cfg, dict) else None
|
||||
if isinstance(api_key, str) and api_key.strip():
|
||||
return api_key.strip()
|
||||
@@ -943,7 +1011,11 @@ def unlock_link_cmdlet(result: Any, args: Sequence[str], config: Dict[str, Any])
|
||||
|
||||
return None
|
||||
|
||||
def _add_direct_link_to_result(result_obj: Any, direct_link: str, original_link: str) -> None:
|
||||
def _add_direct_link_to_result(
|
||||
result_obj: Any,
|
||||
direct_link: str,
|
||||
original_link: str
|
||||
) -> None:
|
||||
if not isinstance(direct_link, str) or not direct_link.strip():
|
||||
return
|
||||
if isinstance(result_obj, dict):
|
||||
@@ -963,7 +1035,10 @@ def unlock_link_cmdlet(result: Any, args: Sequence[str], config: Dict[str, Any])
|
||||
api_key = _get_alldebrid_api_key_from_config(config)
|
||||
|
||||
if not api_key:
|
||||
log("AllDebrid API key not configured (provider.alldebrid.api_key)", file=sys.stderr)
|
||||
log(
|
||||
"AllDebrid API key not configured (provider.alldebrid.api_key)",
|
||||
file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
# Try to unlock the link
|
||||
@@ -995,7 +1070,12 @@ def _register_unlock_link():
|
||||
from cmdlet import register
|
||||
|
||||
@register(["unlock-link"])
|
||||
def unlock_link_wrapper(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
def unlock_link_wrapper(
|
||||
result: Any,
|
||||
args: Sequence[str],
|
||||
config: Dict[str,
|
||||
Any]
|
||||
) -> int:
|
||||
"""Wrapper to make unlock_link_cmdlet available as cmdlet."""
|
||||
import pipeline as ctx
|
||||
|
||||
|
||||
Reference in New Issue
Block a user