hh
This commit is contained in:
@@ -338,6 +338,30 @@ def _resolve_entry_and_path(info: Dict[str, Any], output_dir: Path) -> tuple[Dic
|
||||
raise FileNotFoundError("yt-dlp did not report a downloaded media file")
|
||||
|
||||
|
||||
def _resolve_entries_and_paths(info: Dict[str, Any], output_dir: Path) -> List[tuple[Dict[str, Any], Path]]:
|
||||
resolved: List[tuple[Dict[str, Any], Path]] = []
|
||||
seen: set[str] = set()
|
||||
for entry in _iter_download_entries(info):
|
||||
chosen: Optional[Path] = None
|
||||
for candidate in _candidate_paths(entry, output_dir):
|
||||
if candidate.is_file():
|
||||
chosen = candidate
|
||||
break
|
||||
if not candidate.is_absolute():
|
||||
maybe = output_dir / candidate
|
||||
if maybe.is_file():
|
||||
chosen = maybe
|
||||
break
|
||||
if chosen is None:
|
||||
continue
|
||||
key = str(chosen.resolve())
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
resolved.append((entry, chosen))
|
||||
return resolved
|
||||
|
||||
|
||||
def _extract_sha256(info: Dict[str, Any]) -> Optional[str]:
|
||||
for payload in [info] + info.get("entries", []):
|
||||
if not isinstance(payload, dict):
|
||||
@@ -679,7 +703,7 @@ def download_media(
|
||||
opts: DownloadOptions,
|
||||
*,
|
||||
debug_logger: Optional[DebugLogger] = None,
|
||||
) -> DownloadMediaResult:
|
||||
) -> Any:
|
||||
"""Download media from URL using yt-dlp or direct HTTP download.
|
||||
|
||||
Args:
|
||||
@@ -935,6 +959,48 @@ def download_media(
|
||||
},
|
||||
)
|
||||
|
||||
# Playlist/album handling: resolve ALL downloaded entries and return multiple results.
|
||||
# The cmdlet will emit one PipeObject per downloaded file.
|
||||
if info_dict.get("entries") and not opts.no_playlist:
|
||||
resolved = _resolve_entries_and_paths(info_dict, opts.output_dir)
|
||||
if resolved:
|
||||
results: List[DownloadMediaResult] = []
|
||||
for entry, media_path in resolved:
|
||||
hash_value = _extract_sha256(entry) or _extract_sha256(info_dict)
|
||||
if not hash_value:
|
||||
try:
|
||||
hash_value = sha256_file(media_path)
|
||||
except OSError:
|
||||
hash_value = None
|
||||
|
||||
tags: List[str] = []
|
||||
if extract_ytdlp_tags:
|
||||
try:
|
||||
tags = extract_ytdlp_tags(entry)
|
||||
except Exception as e:
|
||||
log(f"Error extracting tags: {e}", file=sys.stderr)
|
||||
|
||||
source_url = (
|
||||
entry.get("webpage_url")
|
||||
or entry.get("original_url")
|
||||
or entry.get("url")
|
||||
or opts.url
|
||||
)
|
||||
|
||||
results.append(
|
||||
DownloadMediaResult(
|
||||
path=media_path,
|
||||
info=entry,
|
||||
tag=tags,
|
||||
source_url=source_url,
|
||||
hash_value=hash_value,
|
||||
)
|
||||
)
|
||||
|
||||
if not opts.quiet:
|
||||
debug(f"✓ Downloaded playlist items: {len(results)}")
|
||||
return results
|
||||
|
||||
try:
|
||||
entry, media_path = _resolve_entry_and_path(info_dict, opts.output_dir)
|
||||
except FileNotFoundError as exc:
|
||||
@@ -1009,7 +1075,7 @@ def _download_with_timeout(opts: DownloadOptions, timeout_seconds: int = 300) ->
|
||||
timeout_seconds: Max seconds to wait (default 300s = 5 min)
|
||||
|
||||
Returns:
|
||||
DownloadMediaResult
|
||||
DownloadMediaResult or List[DownloadMediaResult]
|
||||
|
||||
Raises:
|
||||
DownloadError: If timeout exceeded
|
||||
@@ -1333,16 +1399,20 @@ class Download_Media(Cmdlet):
|
||||
debug(f"Starting download with 5-minute timeout...")
|
||||
result_obj = _download_with_timeout(opts, timeout_seconds=300)
|
||||
debug(f"Download completed, building pipe object...")
|
||||
pipe_obj_dict = self._build_pipe_object(result_obj, url, opts)
|
||||
debug(f"Emitting result to pipeline...")
|
||||
pipeline_context.emit(pipe_obj_dict)
|
||||
|
||||
# Automatically register url with local library
|
||||
if pipe_obj_dict.get("url"):
|
||||
pipe_obj = coerce_to_pipe_object(pipe_obj_dict)
|
||||
register_url_with_local_library(pipe_obj, config)
|
||||
|
||||
downloaded_count += 1
|
||||
|
||||
# Emit one PipeObject per downloaded file (playlists/albums return a list)
|
||||
results_to_emit = result_obj if isinstance(result_obj, list) else [result_obj]
|
||||
debug(f"Emitting {len(results_to_emit)} result(s) to pipeline...")
|
||||
for downloaded in results_to_emit:
|
||||
pipe_obj_dict = self._build_pipe_object(downloaded, url, opts)
|
||||
pipeline_context.emit(pipe_obj_dict)
|
||||
|
||||
# Automatically register url with local library
|
||||
if pipe_obj_dict.get("url"):
|
||||
pipe_obj = coerce_to_pipe_object(pipe_obj_dict)
|
||||
register_url_with_local_library(pipe_obj, config)
|
||||
|
||||
downloaded_count += len(results_to_emit)
|
||||
debug("✓ Downloaded and emitted")
|
||||
|
||||
except DownloadError as e:
|
||||
@@ -1373,18 +1443,15 @@ class Download_Media(Cmdlet):
|
||||
log(f"Invalid storage location: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
# Priority 2: Config outfile
|
||||
if config and config.get("outfile"):
|
||||
try:
|
||||
return Path(config["outfile"]).expanduser()
|
||||
except Exception:
|
||||
pass
|
||||
# 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() / "Videos"
|
||||
|
||||
# Priority 3: Default (home/Videos)
|
||||
final_output_dir = Path.home() / "Videos"
|
||||
debug(f"Using default directory: {final_output_dir}")
|
||||
|
||||
# Ensure directory exists
|
||||
try:
|
||||
final_output_dir.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user