fdf
This commit is contained in:
@@ -19,11 +19,17 @@ from contextlib import AbstractContextManager, nullcontext
|
||||
from API.HTTP import _download_direct_file
|
||||
from SYS.models import DownloadError, DownloadOptions, DownloadMediaResult
|
||||
from SYS.logger import log, debug, is_debug_enabled
|
||||
from SYS.payload_builders import build_file_result_payload, build_table_result_payload
|
||||
from SYS.pipeline_progress import PipelineProgress
|
||||
from SYS.result_table import Table
|
||||
from SYS.rich_display import stderr_console as get_stderr_console
|
||||
from SYS import pipeline as pipeline_context
|
||||
from SYS.metadata import normalize_urls as normalize_url_list
|
||||
from SYS.selection_builder import (
|
||||
extract_selection_fields,
|
||||
extract_urls_from_selection_args,
|
||||
selection_args_have_url,
|
||||
)
|
||||
from SYS.utils import sha256_file
|
||||
|
||||
from tool.ytdlp import (
|
||||
@@ -57,6 +63,7 @@ build_pipeline_preview = sh.build_pipeline_preview
|
||||
# URI scheme prefixes owned by AllDebrid (magic-link and emoji shorthand).
|
||||
# Defined once here so every method in this file references the same constant.
|
||||
_ALLDEBRID_PREFIXES: tuple[str, ...] = ("alldebrid:", "alldebrid🧲")
|
||||
_FORMAT_INDEX_RE = re.compile(r"^\s*#?\d+\s*$")
|
||||
|
||||
|
||||
class Download_File(Cmdlet):
|
||||
@@ -1008,9 +1015,7 @@ class Download_File(Cmdlet):
|
||||
formats_cache: Dict[str, Optional[List[Dict[str, Any]]]],
|
||||
ytdlp_tool: YtDlpTool,
|
||||
) -> Optional[str]:
|
||||
import re
|
||||
|
||||
if not query_format or not re.match(r"^\s*#?\d+\s*$", str(query_format)):
|
||||
if not query_format or not _FORMAT_INDEX_RE.match(str(query_format)):
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -1221,22 +1226,24 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
row: Dict[str, Any] = {
|
||||
"table": "download-file",
|
||||
"title": str(title or f"Item {idx}"),
|
||||
"detail": str(uploader or ""),
|
||||
"media_kind": "playlist-item",
|
||||
"playlist_index": idx,
|
||||
"_selection_args": (["-url", str(entry_url)] if entry_url else ["-url", str(url), "-item", str(idx)]),
|
||||
"url": entry_url,
|
||||
"target": entry_url,
|
||||
"columns": [
|
||||
row = build_table_result_payload(
|
||||
table="download-file",
|
||||
title=str(title or f"Item {idx}"),
|
||||
detail=str(uploader or ""),
|
||||
columns=[
|
||||
("#", str(idx)),
|
||||
("Title", str(title or "")),
|
||||
("Duration", str(duration or "")),
|
||||
("Uploader", str(uploader or "")),
|
||||
],
|
||||
}
|
||||
selection_args=(
|
||||
["-url", str(entry_url)] if entry_url else ["-url", str(url), "-item", str(idx)]
|
||||
),
|
||||
media_kind="playlist-item",
|
||||
playlist_index=idx,
|
||||
url=entry_url,
|
||||
target=entry_url,
|
||||
)
|
||||
results_list.append(row)
|
||||
table.add_result(row)
|
||||
|
||||
@@ -1782,14 +1789,11 @@ class Download_File(Cmdlet):
|
||||
desc_parts.append(size_str)
|
||||
format_desc = " | ".join(desc_parts)
|
||||
|
||||
format_dict: Dict[str, Any] = {
|
||||
"table": "download-file",
|
||||
"title": f"Format {format_id}",
|
||||
"url": url,
|
||||
"target": url,
|
||||
"detail": format_desc,
|
||||
"media_kind": "format",
|
||||
"columns": [
|
||||
format_dict = build_table_result_payload(
|
||||
table="download-file",
|
||||
title=f"Format {format_id}",
|
||||
detail=format_desc,
|
||||
columns=[
|
||||
("ID", format_id),
|
||||
("Resolution", resolution or "N/A"),
|
||||
("Ext", ext),
|
||||
@@ -1797,13 +1801,16 @@ class Download_File(Cmdlet):
|
||||
("Video", vcodec),
|
||||
("Audio", acodec),
|
||||
],
|
||||
"full_metadata": {
|
||||
selection_args=["-query", f"format:{selection_format_id}"],
|
||||
url=url,
|
||||
target=url,
|
||||
media_kind="format",
|
||||
full_metadata={
|
||||
"format_id": format_id,
|
||||
"url": url,
|
||||
"item_selector": selection_format_id,
|
||||
},
|
||||
"_selection_args": ["-query", f"format:{selection_format_id}"],
|
||||
}
|
||||
)
|
||||
|
||||
results_list.append(format_dict)
|
||||
table.add_result(format_dict)
|
||||
@@ -2379,18 +2386,18 @@ class Download_File(Cmdlet):
|
||||
if not final_url and url:
|
||||
final_url = str(url)
|
||||
|
||||
return {
|
||||
"path": str(media_path),
|
||||
"hash": hash_value,
|
||||
"title": title,
|
||||
"url": final_url,
|
||||
"tag": tag,
|
||||
"action": "cmdlet:download-file",
|
||||
"is_temp": True,
|
||||
"ytdl_format": getattr(opts, "ytdl_format", None),
|
||||
"store": getattr(opts, "storage_name", None) or getattr(opts, "storage_location", None) or "PATH",
|
||||
"media_kind": "video" if opts.mode == "video" else "audio",
|
||||
}
|
||||
return build_file_result_payload(
|
||||
title=title,
|
||||
path=str(media_path),
|
||||
hash_value=hash_value,
|
||||
url=final_url,
|
||||
tag=tag,
|
||||
store=getattr(opts, "storage_name", None) or getattr(opts, "storage_location", None) or "PATH",
|
||||
action="cmdlet:download-file",
|
||||
is_temp=True,
|
||||
ytdl_format=getattr(opts, "ytdl_format", None),
|
||||
media_kind="video" if opts.mode == "video" else "audio",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def download_streaming_url_as_pipe_objects(
|
||||
@@ -2609,22 +2616,13 @@ class Download_File(Cmdlet):
|
||||
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def _normalize_hash_hex(value: Optional[str]) -> Optional[str]:
|
||||
if not value or not isinstance(value, str):
|
||||
return None
|
||||
candidate = value.strip().lower()
|
||||
if len(candidate) == 64 and all(c in "0123456789abcdef" for c in candidate):
|
||||
return candidate
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _extract_hash_from_search_hit(cls, hit: Any) -> Optional[str]:
|
||||
if not isinstance(hit, dict):
|
||||
return None
|
||||
for key in ("hash", "hash_hex", "file_hash", "hydrus_hash"):
|
||||
v = hit.get(key)
|
||||
normalized = cls._normalize_hash_hex(str(v) if v is not None else None)
|
||||
normalized = sh.normalize_hash(str(v) if v is not None else None)
|
||||
if normalized:
|
||||
return normalized
|
||||
return None
|
||||
@@ -2717,10 +2715,10 @@ class Download_File(Cmdlet):
|
||||
|
||||
hashes: List[str] = []
|
||||
for po in pipe_objects:
|
||||
h_val = cls._normalize_hash_hex(str(po.get("hash") or ""))
|
||||
h_val = sh.normalize_hash(str(po.get("hash") or ""))
|
||||
hashes.append(h_val or "")
|
||||
|
||||
king_hash = cls._normalize_hash_hex(source_king_hash) if source_king_hash else None
|
||||
king_hash = sh.normalize_hash(source_king_hash) if source_king_hash else None
|
||||
if not king_hash:
|
||||
king_hash = hashes[0] if hashes and hashes[0] else None
|
||||
if not king_hash:
|
||||
@@ -2774,10 +2772,10 @@ class Download_File(Cmdlet):
|
||||
# Fallback to piped items if no explicit URLs provided
|
||||
piped_items = []
|
||||
if not raw_url:
|
||||
if isinstance(result, list):
|
||||
piped_items = list(result)
|
||||
elif result is not None:
|
||||
piped_items = [result]
|
||||
piped_items = sh.normalize_result_items(
|
||||
result,
|
||||
include_falsey_single=True,
|
||||
)
|
||||
|
||||
# Handle TABLE_AUTO_STAGES routing: if a piped item has _selection_args,
|
||||
# re-invoke download-file with those args instead of processing the PipeObject itself.
|
||||
@@ -2785,68 +2783,18 @@ class Download_File(Cmdlet):
|
||||
selection_runs: List[List[str]] = []
|
||||
residual_items: List[Any] = []
|
||||
|
||||
def _looks_like_url(value: Any) -> bool:
|
||||
try:
|
||||
s_val = str(value or "").strip().lower()
|
||||
except Exception:
|
||||
return False
|
||||
return s_val.startswith(
|
||||
("http://", "https://", "magnet:", "torrent:") + _ALLDEBRID_PREFIXES
|
||||
)
|
||||
|
||||
def _extract_selection_args(item: Any) -> tuple[Optional[List[str]], Optional[str]]:
|
||||
selection_args: Optional[List[str]] = None
|
||||
item_url: Optional[str] = None
|
||||
|
||||
if isinstance(item, dict):
|
||||
selection_args = item.get("_selection_args") or item.get("selection_args")
|
||||
item_url = item.get("url") or item.get("path") or item.get("target")
|
||||
md = item.get("metadata") or item.get("full_metadata")
|
||||
if isinstance(md, dict):
|
||||
selection_args = selection_args or md.get("_selection_args") or md.get("selection_args")
|
||||
item_url = item_url or md.get("url") or md.get("source_url")
|
||||
extra = item.get("extra")
|
||||
if isinstance(extra, dict):
|
||||
selection_args = selection_args or extra.get("_selection_args") or extra.get("selection_args")
|
||||
item_url = item_url or extra.get("url") or extra.get("source_url")
|
||||
else:
|
||||
item_url = getattr(item, "url", None) or getattr(item, "path", None) or getattr(item, "target", None)
|
||||
md = getattr(item, "metadata", None)
|
||||
if isinstance(md, dict):
|
||||
selection_args = md.get("_selection_args") or md.get("selection_args")
|
||||
item_url = item_url or md.get("url") or md.get("source_url")
|
||||
extra = getattr(item, "extra", None)
|
||||
if isinstance(extra, dict):
|
||||
selection_args = selection_args or extra.get("_selection_args") or extra.get("selection_args")
|
||||
item_url = item_url or extra.get("url") or extra.get("source_url")
|
||||
|
||||
if isinstance(selection_args, (list, tuple)):
|
||||
normalized_args = [str(arg) for arg in selection_args if arg is not None]
|
||||
elif selection_args is not None:
|
||||
normalized_args = [str(selection_args)]
|
||||
else:
|
||||
normalized_args = None
|
||||
|
||||
if item_url and not _looks_like_url(item_url):
|
||||
item_url = None
|
||||
|
||||
return normalized_args, item_url
|
||||
|
||||
def _selection_args_have_url(args_list: Sequence[str]) -> bool:
|
||||
for idx, arg in enumerate(args_list):
|
||||
low = str(arg or "").strip().lower()
|
||||
if low in {"-url", "--url"}:
|
||||
return True
|
||||
if _looks_like_url(arg):
|
||||
return True
|
||||
return False
|
||||
|
||||
for item in piped_items:
|
||||
handled = False
|
||||
try:
|
||||
normalized_args, item_url = _extract_selection_args(item)
|
||||
normalized_args, _normalized_action, item_url = extract_selection_fields(
|
||||
item,
|
||||
extra_url_prefixes=_ALLDEBRID_PREFIXES,
|
||||
)
|
||||
if normalized_args:
|
||||
if _selection_args_have_url(normalized_args):
|
||||
if selection_args_have_url(
|
||||
normalized_args,
|
||||
extra_url_prefixes=_ALLDEBRID_PREFIXES,
|
||||
):
|
||||
selection_runs.append(list(normalized_args))
|
||||
handled = True
|
||||
elif item_url:
|
||||
@@ -2860,25 +2808,11 @@ class Download_File(Cmdlet):
|
||||
if selection_runs:
|
||||
selection_urls: List[str] = []
|
||||
|
||||
def _extract_urls_from_args(args_list: Sequence[str]) -> List[str]:
|
||||
urls: List[str] = []
|
||||
idx = 0
|
||||
while idx < len(args_list):
|
||||
token = str(args_list[idx] or "")
|
||||
low = token.strip().lower()
|
||||
if low in {"-url", "--url"} and idx + 1 < len(args_list):
|
||||
candidate = str(args_list[idx + 1] or "").strip()
|
||||
if _looks_like_url(candidate):
|
||||
urls.append(candidate)
|
||||
idx += 2
|
||||
continue
|
||||
if _looks_like_url(token):
|
||||
urls.append(token.strip())
|
||||
idx += 1
|
||||
return urls
|
||||
|
||||
for run_args in selection_runs:
|
||||
for u in _extract_urls_from_args(run_args):
|
||||
for u in extract_urls_from_selection_args(
|
||||
run_args,
|
||||
extra_url_prefixes=_ALLDEBRID_PREFIXES,
|
||||
):
|
||||
if u not in selection_urls:
|
||||
selection_urls.append(u)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user