df
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
This commit is contained in:
@@ -44,8 +44,12 @@ class Download_File(Cmdlet):
|
||||
SharedArgs.URL,
|
||||
SharedArgs.PATH,
|
||||
# Prefer -path for output directory to match other cmdlets; keep -output for backwards compatibility.
|
||||
CmdletArg(name="-output", type="string", alias="o", description="(deprecated) Output directory (use -path instead)"),
|
||||
|
||||
CmdletArg(
|
||||
name="-output",
|
||||
type="string",
|
||||
alias="o",
|
||||
description="(deprecated) Output directory (use -path instead)",
|
||||
),
|
||||
],
|
||||
detail=[
|
||||
"Download files directly via HTTP without yt-dlp processing.",
|
||||
@@ -67,7 +71,7 @@ class Download_File(Cmdlet):
|
||||
raw_url = [raw_url]
|
||||
|
||||
expanded_urls: List[str] = []
|
||||
for u in (raw_url or []):
|
||||
for u in raw_url or []:
|
||||
if u is None:
|
||||
continue
|
||||
s = str(u).strip()
|
||||
@@ -99,7 +103,9 @@ class Download_File(Cmdlet):
|
||||
return 1
|
||||
|
||||
@staticmethod
|
||||
def _build_preview(raw_urls: Sequence[str], piped_items: Sequence[Any], total_items: int) -> List[Any]:
|
||||
def _build_preview(
|
||||
raw_urls: Sequence[str], piped_items: Sequence[Any], total_items: int
|
||||
) -> List[Any]:
|
||||
try:
|
||||
preview: List[Any] = []
|
||||
preview.extend(list(raw_urls or [])[: max(0, total_items)])
|
||||
@@ -212,7 +218,11 @@ class Download_File(Cmdlet):
|
||||
title = str(get_field(item, "title") or "").strip() if item is not None else ""
|
||||
except Exception:
|
||||
title = ""
|
||||
table_title = f"Internet Archive: {title}".strip().rstrip(":") if title else f"Internet Archive: {identifier}"
|
||||
table_title = (
|
||||
f"Internet Archive: {title}".strip().rstrip(":")
|
||||
if title
|
||||
else f"Internet Archive: {identifier}"
|
||||
)
|
||||
|
||||
try:
|
||||
from result_table import ResultTable
|
||||
@@ -329,7 +339,9 @@ class Download_File(Cmdlet):
|
||||
config: Dict[str, Any],
|
||||
provider_hint: Optional[str] = None,
|
||||
) -> None:
|
||||
title_val = (title_hint or downloaded_path.stem or "Unknown").strip() or downloaded_path.stem
|
||||
title_val = (
|
||||
title_hint or downloaded_path.stem or "Unknown"
|
||||
).strip() or downloaded_path.stem
|
||||
hash_value = self._compute_file_hash(downloaded_path)
|
||||
tag: List[str] = []
|
||||
if tags_hint:
|
||||
@@ -406,9 +418,13 @@ class Download_File(Cmdlet):
|
||||
|
||||
provider = _get_provider("telegram", config)
|
||||
if provider is None:
|
||||
raise DownloadError("Telegram provider not configured or not available (check telethon/app_id/api_hash)")
|
||||
raise DownloadError(
|
||||
"Telegram provider not configured or not available (check telethon/app_id/api_hash)"
|
||||
)
|
||||
|
||||
sr = SearchResult(table="telegram", title=str(url), path=str(url), full_metadata={})
|
||||
sr = SearchResult(
|
||||
table="telegram", title=str(url), path=str(url), full_metadata={}
|
||||
)
|
||||
downloaded_path = None
|
||||
telegram_info: Optional[Dict[str, Any]] = None
|
||||
if hasattr(provider, "download_url"):
|
||||
@@ -428,9 +444,15 @@ class Download_File(Cmdlet):
|
||||
try:
|
||||
chat_info_raw = telegram_info.get("chat")
|
||||
msg_info_raw = telegram_info.get("message")
|
||||
chat_info: Dict[str, Any] = chat_info_raw if isinstance(chat_info_raw, dict) else {}
|
||||
msg_info: Dict[str, Any] = msg_info_raw if isinstance(msg_info_raw, dict) else {}
|
||||
channel = str(chat_info.get("title") or chat_info.get("username") or "").strip()
|
||||
chat_info: Dict[str, Any] = (
|
||||
chat_info_raw if isinstance(chat_info_raw, dict) else {}
|
||||
)
|
||||
msg_info: Dict[str, Any] = (
|
||||
msg_info_raw if isinstance(msg_info_raw, dict) else {}
|
||||
)
|
||||
channel = str(
|
||||
chat_info.get("title") or chat_info.get("username") or ""
|
||||
).strip()
|
||||
post = msg_info.get("id")
|
||||
except Exception:
|
||||
channel = ""
|
||||
@@ -479,7 +501,16 @@ class Download_File(Cmdlet):
|
||||
p = urlparse(str(url))
|
||||
h = (p.hostname or "").strip().lower()
|
||||
path = (p.path or "").strip().lower()
|
||||
if "libgen" in h and any(x in path for x in ("/edition.php", "/file.php", "/ads.php", "/get.php", "/series.php")):
|
||||
if "libgen" in h and any(
|
||||
x in path
|
||||
for x in (
|
||||
"/edition.php",
|
||||
"/file.php",
|
||||
"/ads.php",
|
||||
"/get.php",
|
||||
"/series.php",
|
||||
)
|
||||
):
|
||||
provider_name = "libgen"
|
||||
except Exception:
|
||||
pass
|
||||
@@ -489,7 +520,9 @@ class Download_File(Cmdlet):
|
||||
if provider_name == "openlibrary":
|
||||
provider = get_provider("openlibrary", config)
|
||||
if provider is None:
|
||||
raise DownloadError("OpenLibrary provider not configured or not available")
|
||||
raise DownloadError(
|
||||
"OpenLibrary provider not configured or not available"
|
||||
)
|
||||
|
||||
edition_id = self._openlibrary_edition_id_from_url(str(url))
|
||||
title_hint = self._title_hint_from_url_slug(str(url))
|
||||
@@ -512,7 +545,9 @@ class Download_File(Cmdlet):
|
||||
# High-level steps for OpenLibrary borrow/download flow.
|
||||
progress.begin_steps(5)
|
||||
|
||||
def _progress(kind: str, done: int, total: Optional[int], label: str) -> None:
|
||||
def _progress(
|
||||
kind: str, done: int, total: Optional[int], label: str
|
||||
) -> None:
|
||||
# kind:
|
||||
# - "step": advance step text
|
||||
# - "pages": update pipe percent/status
|
||||
@@ -525,7 +560,9 @@ class Download_File(Cmdlet):
|
||||
t = int(total) if isinstance(total, int) else 0
|
||||
d = int(done) if isinstance(done, int) else 0
|
||||
if t > 0:
|
||||
pct = int(round((max(0, min(d, t)) / max(1, t)) * 100.0))
|
||||
pct = int(
|
||||
round((max(0, min(d, t)) / max(1, t)) * 100.0)
|
||||
)
|
||||
progress.set_percent(pct)
|
||||
progress.set_status(f"downloading pages {d}/{t}")
|
||||
else:
|
||||
@@ -538,9 +575,15 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
lbl = "download"
|
||||
progress.begin_transfer(label=lbl, total=total)
|
||||
progress.update_transfer(label=lbl, completed=done, total=total)
|
||||
progress.update_transfer(
|
||||
label=lbl, completed=done, total=total
|
||||
)
|
||||
try:
|
||||
if isinstance(total, int) and total > 0 and int(done) >= int(total):
|
||||
if (
|
||||
isinstance(total, int)
|
||||
and total > 0
|
||||
and int(done) >= int(total)
|
||||
):
|
||||
progress.finish_transfer(label=lbl)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -590,12 +633,18 @@ class Download_File(Cmdlet):
|
||||
|
||||
exec_fn = getattr(_SEARCH_PROVIDER_CMDLET, "exec", None)
|
||||
if callable(exec_fn):
|
||||
ret = exec_fn(None, ["-provider", "libgen", "-query", fallback_query], config)
|
||||
ret = exec_fn(
|
||||
None,
|
||||
["-provider", "libgen", "-query", fallback_query],
|
||||
config,
|
||||
)
|
||||
try:
|
||||
table = pipeline_context.get_last_result_table()
|
||||
items = pipeline_context.get_last_result_items()
|
||||
if table is not None:
|
||||
pipeline_context.set_last_result_table_overlay(table, items)
|
||||
pipeline_context.set_last_result_table_overlay(
|
||||
table, items
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -606,7 +655,10 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
log("[download-file] OpenLibrary URL could not be downloaded", file=sys.stderr)
|
||||
log(
|
||||
"[download-file] OpenLibrary URL could not be downloaded",
|
||||
file=sys.stderr,
|
||||
)
|
||||
continue
|
||||
|
||||
# Generic provider URL handler (if a provider implements `download_url`).
|
||||
@@ -734,11 +786,18 @@ class Download_File(Cmdlet):
|
||||
full_metadata = get_field(item, "full_metadata")
|
||||
target = get_field(item, "path") or get_field(item, "url")
|
||||
|
||||
if str(table or "").lower() == "alldebrid" and str(media_kind or "").lower() == "folder":
|
||||
if (
|
||||
str(table or "").lower() == "alldebrid"
|
||||
and str(media_kind or "").lower() == "folder"
|
||||
):
|
||||
magnet_id = None
|
||||
if isinstance(full_metadata, dict):
|
||||
magnet_id = full_metadata.get("magnet_id")
|
||||
if magnet_id is None and isinstance(target, str) and target.lower().startswith("alldebrid:magnet:"):
|
||||
if (
|
||||
magnet_id is None
|
||||
and isinstance(target, str)
|
||||
and target.lower().startswith("alldebrid:magnet:")
|
||||
):
|
||||
try:
|
||||
magnet_id = int(target.split(":")[-1])
|
||||
except Exception:
|
||||
@@ -748,12 +807,20 @@ class Download_File(Cmdlet):
|
||||
provider = get_search_provider("alldebrid", config)
|
||||
if provider is not None:
|
||||
try:
|
||||
files = provider.search("*", limit=10_000, filters={"view": "files", "magnet_id": int(magnet_id)})
|
||||
files = provider.search(
|
||||
"*",
|
||||
limit=10_000,
|
||||
filters={"view": "files", "magnet_id": int(magnet_id)},
|
||||
)
|
||||
except Exception:
|
||||
files = []
|
||||
|
||||
# If the magnet isn't ready, provider.search returns a single not-ready folder row.
|
||||
if files and len(files) == 1 and getattr(files[0], "media_kind", "") == "folder":
|
||||
if (
|
||||
files
|
||||
and len(files) == 1
|
||||
and getattr(files[0], "media_kind", "") == "folder"
|
||||
):
|
||||
detail = getattr(files[0], "detail", "")
|
||||
log(
|
||||
f"[download-file] AllDebrid magnet {magnet_id} not ready ({detail or 'unknown'})",
|
||||
@@ -761,7 +828,9 @@ class Download_File(Cmdlet):
|
||||
)
|
||||
else:
|
||||
for sr in files:
|
||||
expanded_items.append(sr.to_dict() if hasattr(sr, "to_dict") else sr)
|
||||
expanded_items.append(
|
||||
sr.to_dict() if hasattr(sr, "to_dict") else sr
|
||||
)
|
||||
continue
|
||||
|
||||
expanded_items.append(item)
|
||||
@@ -784,7 +853,9 @@ class Download_File(Cmdlet):
|
||||
get_search_provider = registry.get("get_search_provider")
|
||||
SearchResult = registry.get("SearchResult")
|
||||
|
||||
expanded_items = self._expand_provider_items(piped_items=piped_items, registry=registry, config=config)
|
||||
expanded_items = self._expand_provider_items(
|
||||
piped_items=piped_items, registry=registry, config=config
|
||||
)
|
||||
|
||||
for item in expanded_items:
|
||||
try:
|
||||
@@ -800,7 +871,11 @@ class Download_File(Cmdlet):
|
||||
tags_list = None
|
||||
|
||||
full_metadata = get_field(item, "full_metadata")
|
||||
if (not full_metadata) and isinstance(item, dict) and isinstance(item.get("extra"), dict):
|
||||
if (
|
||||
(not full_metadata)
|
||||
and isinstance(item, dict)
|
||||
and isinstance(item.get("extra"), dict)
|
||||
):
|
||||
extra_md = item["extra"].get("full_metadata")
|
||||
if isinstance(extra_md, dict):
|
||||
full_metadata = extra_md
|
||||
@@ -832,7 +907,9 @@ class Download_File(Cmdlet):
|
||||
if isinstance(md, dict):
|
||||
magnet_name = md.get("magnet_name") or md.get("folder")
|
||||
if not magnet_name:
|
||||
magnet_name = str(get_field(item, "detail") or "").strip() or None
|
||||
magnet_name = (
|
||||
str(get_field(item, "detail") or "").strip() or None
|
||||
)
|
||||
|
||||
magnet_dir_name = _sf(str(magnet_name)) if magnet_name else ""
|
||||
|
||||
@@ -845,7 +922,9 @@ class Download_File(Cmdlet):
|
||||
base_tail_norm = _sf(base_tail).lower() if base_tail.strip() else ""
|
||||
magnet_dir_norm = magnet_dir_name.lower() if magnet_dir_name else ""
|
||||
|
||||
if magnet_dir_name and (not base_tail_norm or base_tail_norm != magnet_dir_norm):
|
||||
if magnet_dir_name and (
|
||||
not base_tail_norm or base_tail_norm != magnet_dir_norm
|
||||
):
|
||||
output_dir = Path(output_dir) / magnet_dir_name
|
||||
|
||||
relpath = None
|
||||
@@ -855,7 +934,11 @@ class Download_File(Cmdlet):
|
||||
relpath = md["file"].get("_relpath")
|
||||
|
||||
if relpath:
|
||||
parts = [p for p in str(relpath).replace("\\", "/").split("/") if p and p not in {".", ".."}]
|
||||
parts = [
|
||||
p
|
||||
for p in str(relpath).replace("\\", "/").split("/")
|
||||
if p and p not in {".", ".."}
|
||||
]
|
||||
|
||||
# If the provider relpath already includes the magnet folder name as a
|
||||
# root directory (common), strip it to prevent double nesting.
|
||||
@@ -881,7 +964,11 @@ class Download_File(Cmdlet):
|
||||
provider_sr = sr
|
||||
|
||||
# OpenLibrary: if provider download failed, do NOT try to download the OpenLibrary page HTML.
|
||||
if downloaded_path is None and attempted_provider_download and str(table or "").lower() == "openlibrary":
|
||||
if (
|
||||
downloaded_path is None
|
||||
and attempted_provider_download
|
||||
and str(table or "").lower() == "openlibrary"
|
||||
):
|
||||
availability = None
|
||||
reason = None
|
||||
if isinstance(full_metadata, dict):
|
||||
@@ -898,7 +985,10 @@ class Download_File(Cmdlet):
|
||||
if not title_text and isinstance(full_metadata, dict):
|
||||
title_text = str(full_metadata.get("title") or "").strip()
|
||||
if title_text:
|
||||
log(f"[download-file] Not available on OpenLibrary; searching LibGen for: {title_text}", file=sys.stderr)
|
||||
log(
|
||||
f"[download-file] Not available on OpenLibrary; searching LibGen for: {title_text}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
from cmdlet.search_provider import CMDLET as _SEARCH_PROVIDER_CMDLET
|
||||
|
||||
fallback_query = title_text
|
||||
@@ -921,7 +1011,9 @@ class Download_File(Cmdlet):
|
||||
table_obj = pipeline_context.get_last_result_table()
|
||||
items_obj = pipeline_context.get_last_result_items()
|
||||
if table_obj is not None:
|
||||
pipeline_context.set_last_result_table_overlay(table_obj, items_obj)
|
||||
pipeline_context.set_last_result_table_overlay(
|
||||
table_obj, items_obj
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -935,7 +1027,11 @@ class Download_File(Cmdlet):
|
||||
continue
|
||||
|
||||
# Fallback: if we have a direct HTTP URL, download it directly
|
||||
if downloaded_path is None and isinstance(target, str) and target.startswith("http"):
|
||||
if (
|
||||
downloaded_path is None
|
||||
and isinstance(target, str)
|
||||
and target.startswith("http")
|
||||
):
|
||||
# Guard: provider landing pages (e.g. LibGen ads.php) are HTML, not files.
|
||||
# Never download these as "files".
|
||||
if str(table or "").lower() == "libgen":
|
||||
@@ -946,7 +1042,9 @@ class Download_File(Cmdlet):
|
||||
file=sys.stderr,
|
||||
)
|
||||
continue
|
||||
debug(f"[download-file] Provider item looks like direct URL, downloading: {target}")
|
||||
debug(
|
||||
f"[download-file] Provider item looks like direct URL, downloading: {target}"
|
||||
)
|
||||
suggested_name = str(title).strip() if title is not None else None
|
||||
result_obj = _download_direct_file(
|
||||
target,
|
||||
@@ -958,7 +1056,10 @@ class Download_File(Cmdlet):
|
||||
downloaded_path = self._path_from_download_result(result_obj)
|
||||
|
||||
if downloaded_path is None:
|
||||
log(f"Cannot download item (no provider handler / unsupported target): {title or target}", file=sys.stderr)
|
||||
log(
|
||||
f"Cannot download item (no provider handler / unsupported target): {title or target}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
continue
|
||||
|
||||
# Allow providers to add/enrich tags and metadata during download.
|
||||
@@ -1038,11 +1139,19 @@ class Download_File(Cmdlet):
|
||||
|
||||
# UX: In piped mode, allow a single positional arg to be the destination directory.
|
||||
# Example: @1-4 | download-file "C:\\Users\\Me\\Downloads\\yoyo"
|
||||
if had_piped_input and raw_url and len(raw_url) == 1 and (not parsed.get("path")) and (not parsed.get("output")):
|
||||
if (
|
||||
had_piped_input
|
||||
and raw_url
|
||||
and len(raw_url) == 1
|
||||
and (not parsed.get("path"))
|
||||
and (not parsed.get("output"))
|
||||
):
|
||||
candidate = str(raw_url[0] or "").strip()
|
||||
low = candidate.lower()
|
||||
looks_like_url = low.startswith(("http://", "https://", "ftp://"))
|
||||
looks_like_provider = low.startswith(("magnet:", "alldebrid:", "hydrus:", "ia:", "internetarchive:"))
|
||||
looks_like_provider = low.startswith(
|
||||
("magnet:", "alldebrid:", "hydrus:", "ia:", "internetarchive:")
|
||||
)
|
||||
looks_like_windows_path = (
|
||||
(len(candidate) >= 2 and candidate[1] == ":")
|
||||
or candidate.startswith("\\\\")
|
||||
@@ -1058,7 +1167,9 @@ class Download_File(Cmdlet):
|
||||
log("No url or piped items to download", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
quiet_mode = bool(config.get("_quiet_background_output")) if isinstance(config, dict) else False
|
||||
quiet_mode = (
|
||||
bool(config.get("_quiet_background_output")) if isinstance(config, dict) else False
|
||||
)
|
||||
ia_picker_exit = self._maybe_show_internetarchive_formats(
|
||||
raw_urls=raw_url,
|
||||
piped_items=piped_items,
|
||||
@@ -1082,7 +1193,9 @@ class Download_File(Cmdlet):
|
||||
total_items = self._safe_total_items(raw_url, piped_items)
|
||||
preview = self._build_preview(raw_url, piped_items, total_items)
|
||||
|
||||
progress.ensure_local_ui(label="download-file", total_items=total_items, items_preview=preview)
|
||||
progress.ensure_local_ui(
|
||||
label="download-file", total_items=total_items, items_preview=preview
|
||||
)
|
||||
|
||||
registry = self._load_provider_registry()
|
||||
|
||||
@@ -1155,6 +1268,7 @@ class Download_File(Cmdlet):
|
||||
# Priority 2: Config default output/temp directory
|
||||
try:
|
||||
from config import resolve_output_dir
|
||||
|
||||
final_output_dir = resolve_output_dir(config)
|
||||
except Exception:
|
||||
final_output_dir = Path.home() / "Downloads"
|
||||
@@ -1173,6 +1287,7 @@ class Download_File(Cmdlet):
|
||||
def _compute_file_hash(self, filepath: Path) -> str:
|
||||
"""Compute SHA256 hash of a file."""
|
||||
import hashlib
|
||||
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
|
||||
Reference in New Issue
Block a user