158 lines
6.1 KiB
Python
158 lines
6.1 KiB
Python
"""download-provider cmdlet: Download items from external providers."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, Sequence, List, Optional
|
|
from pathlib import Path
|
|
import sys
|
|
import json
|
|
|
|
from SYS.logger import log, debug
|
|
from Provider.registry import get_search_provider, SearchResult
|
|
from SYS.utils import unique_path
|
|
|
|
from ._shared import Cmdlet, CmdletArg, should_show_help, get_field, coerce_to_pipe_object
|
|
import pipeline as ctx
|
|
|
|
# Optional dependencies
|
|
try:
|
|
from config import get_local_storage_path, resolve_output_dir
|
|
except Exception: # pragma: no cover
|
|
get_local_storage_path = None # type: ignore
|
|
resolve_output_dir = None # type: ignore
|
|
|
|
class Download_Provider(Cmdlet):
|
|
"""Download items from external providers."""
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
name="download-provider",
|
|
summary="Download items from external providers (soulseek, libgen, etc).",
|
|
usage="download-provider [item] [-output DIR]",
|
|
arg=[
|
|
CmdletArg("output", type="string", alias="o", description="Output directory"),
|
|
],
|
|
detail=[
|
|
"Download items from external providers.",
|
|
"Usually called automatically by @N selection on provider results.",
|
|
"Can be used manually by piping a provider result item.",
|
|
],
|
|
exec=self.run
|
|
)
|
|
self.register()
|
|
|
|
def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|
"""Execute download-provider cmdlet."""
|
|
if should_show_help(args):
|
|
ctx.emit(self.__dict__)
|
|
return 0
|
|
|
|
# Parse arguments
|
|
output_dir_arg = None
|
|
i = 0
|
|
while i < len(args):
|
|
arg = args[i]
|
|
if arg in ("-output", "--output", "-o") and i + 1 < len(args):
|
|
output_dir_arg = args[i+1]
|
|
i += 2
|
|
else:
|
|
i += 1
|
|
|
|
# Determine output directory
|
|
if output_dir_arg:
|
|
output_dir = Path(output_dir_arg)
|
|
elif resolve_output_dir:
|
|
output_dir = resolve_output_dir(config)
|
|
else:
|
|
output_dir = Path("./downloads")
|
|
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Process input result
|
|
items = []
|
|
if isinstance(result, list):
|
|
items = result
|
|
elif result:
|
|
items = [result]
|
|
|
|
if not items:
|
|
log("No items to download", file=sys.stderr)
|
|
return 1
|
|
|
|
success_count = 0
|
|
|
|
for item in items:
|
|
try:
|
|
# Extract provider info
|
|
table = get_field(item, "table")
|
|
if not table:
|
|
log(f"Skipping item without provider info: {item}", file=sys.stderr)
|
|
continue
|
|
|
|
provider = get_search_provider(table, config)
|
|
if not provider:
|
|
log(f"Provider '{table}' not available for download", file=sys.stderr)
|
|
continue
|
|
|
|
# Reconstruct SearchResult if needed
|
|
# The provider.download method expects a SearchResult object or compatible dict
|
|
if isinstance(item, dict):
|
|
# Ensure full_metadata is present
|
|
if "full_metadata" not in item and "extra" in item:
|
|
item["full_metadata"] = item["extra"].get("full_metadata", {})
|
|
|
|
search_result = SearchResult(
|
|
table=table,
|
|
title=item.get("title", "Unknown"),
|
|
path=item.get("path", ""),
|
|
full_metadata=item.get("full_metadata", {})
|
|
)
|
|
else:
|
|
# Assume it's an object with attributes (like PipeObject)
|
|
full_metadata = getattr(item, "full_metadata", {})
|
|
# Check extra dict if full_metadata is missing/empty
|
|
if not full_metadata and hasattr(item, "extra") and isinstance(item.extra, dict):
|
|
full_metadata = item.extra.get("full_metadata", {})
|
|
# Fallback: if full_metadata key isn't there, maybe the extra dict IS the metadata
|
|
if not full_metadata and "username" in item.extra:
|
|
full_metadata = item.extra
|
|
|
|
search_result = SearchResult(
|
|
table=table,
|
|
title=getattr(item, "title", "Unknown"),
|
|
path=getattr(item, "path", ""),
|
|
full_metadata=full_metadata
|
|
)
|
|
|
|
debug(f"[download-provider] Downloading '{search_result.title}' via {table}...")
|
|
downloaded_path = provider.download(search_result, output_dir)
|
|
|
|
if downloaded_path:
|
|
debug(f"[download-provider] Download successful: {downloaded_path}")
|
|
|
|
# Create PipeObject for the downloaded file
|
|
pipe_obj = coerce_to_pipe_object({
|
|
"path": str(downloaded_path),
|
|
"title": search_result.title,
|
|
"table": "local", # Now it's a local file
|
|
"media_kind": getattr(item, "media_kind", "other"),
|
|
"tags": getattr(item, "tags", []),
|
|
"full_metadata": search_result.full_metadata
|
|
})
|
|
|
|
ctx.emit(pipe_obj)
|
|
success_count += 1
|
|
else:
|
|
log(f"Download failed for '{search_result.title}'", file=sys.stderr)
|
|
|
|
except Exception as e:
|
|
log(f"Error downloading item: {e}", file=sys.stderr)
|
|
import traceback
|
|
debug(traceback.format_exc())
|
|
|
|
if success_count > 0:
|
|
return 0
|
|
return 1
|
|
|
|
# Register cmdlet instance
|
|
Download_Provider_Instance = Download_Provider()
|