This commit is contained in:
nose
2025-11-26 00:29:10 -08:00
parent 935ce303d0
commit e9b505e609
4 changed files with 33 additions and 38 deletions

27
CLI.py
View File

@@ -886,32 +886,9 @@ def _execute_pipeline(tokens: list):
source_args = ctx.get_last_result_table_source_args() source_args = ctx.get_last_result_table_source_args()
if source_cmd == 'search-file' and source_args and 'youtube' in source_args: if source_cmd == 'search-file' and source_args and 'youtube' in source_args:
# Only auto-pipe if no other stages follow (stages is empty because we popped the selection)
if not stages:
print(f"Auto-piping YouTube selection to .pipe") print(f"Auto-piping YouTube selection to .pipe")
# We can't modify stages here easily as we are outside the loop or before it?
# Actually, this block runs BEFORE the loop if stages[0] is a selection.
# But wait, the loop iterates over stages.
# If we are here, it means we handled the selection by filtering `piped_result`.
# The loop will then execute stages starting from 0?
# No, `_execute_pipeline` logic is complex.
# Let's look at where this block is.
# It is inside `_execute_pipeline`.
# It runs if `first_stage_selection_indices` is set (meaning stages[0] was a selection).
# And `command_expanded` is False (meaning we didn't replace stages[0] with a command).
# If we are here, `piped_result` holds the selected item(s).
# The loop below iterates `for stage_index, stage_tokens in enumerate(stages):`
# But we removed the first stage from `stages`? No.
# Wait, let's check how `first_stage_selection_indices` is used.
# It seems `stages` is modified earlier?
# "if stages and stages[0] and stages[0][0].startswith('@'): ... stages.pop(0)"
# Yes, lines 750-760 (approx) pop the first stage if it is a selection.
# So `stages` now contains the REST of the pipeline.
# If user typed just `@1`, `stages` is now empty `[]`.
# So if we want to pipe to `.pipe`, we should append `.pipe` to `stages`.
stages.append(['.pipe']) stages.append(['.pipe'])
else: else:

View File

@@ -453,10 +453,10 @@ def _handle_search_result(result: Any, args: Sequence[str], config: Dict[str, An
if storage_name.lower() == 'hydrus': if storage_name.lower() == 'hydrus':
return _handle_hydrus_file(file_hash, file_title, config, args, mime_type=mime_type) return _handle_hydrus_file(file_hash, file_title, config, args, mime_type=mime_type)
elif storage_name.lower() == 'local': elif storage_name.lower() == 'local':
return _handle_local_file(file_path, file_title, args, file_hash=file_hash) return _handle_local_file(file_path, file_title, config, args, file_hash=file_hash)
elif storage_name.lower() == 'download': elif storage_name.lower() == 'download':
# Downloads are local files # Downloads are local files
return _handle_local_file(file_path, file_title, args, file_hash=file_hash) return _handle_local_file(file_path, file_title, config, args, file_hash=file_hash)
elif storage_name.lower() == 'debrid': elif storage_name.lower() == 'debrid':
# Extract magnet_id from result (search-file stores it in full_metadata or as custom attribute) # Extract magnet_id from result (search-file stores it in full_metadata or as custom attribute)
if not magnet_id: if not magnet_id:
@@ -591,7 +591,7 @@ def _handle_hydrus_file(file_hash: Optional[str], file_title: str, config: Dict[
return 1 return 1
def _handle_local_file(file_path: Optional[str], file_title: str, args: Sequence[str], file_hash: Optional[str] = None) -> int: def _handle_local_file(file_path: Optional[str], file_title: str, config: Dict[str, Any], args: Sequence[str], file_hash: Optional[str] = None) -> int:
"""Handle file from local storage - auto-play in MPV if media, otherwise open with default app.""" """Handle file from local storage - auto-play in MPV if media, otherwise open with default app."""
if not file_path: if not file_path:
log("Error: No file path provided", file=sys.stderr) log("Error: No file path provided", file=sys.stderr)

View File

@@ -128,6 +128,17 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
debug("Failed to pause playback (MPV not running?)", file=sys.stderr) debug("Failed to pause playback (MPV not running?)", file=sys.stderr)
return 1 return 1
# Handle Clear All command (no index provided)
if clear_mode and index_arg is None:
cmd = {"command": ["playlist-clear"], "request_id": 105}
resp = _send_ipc_command(cmd)
if resp and resp.get("error") == "success":
debug("Playlist cleared")
return 0
else:
debug("Failed to clear playlist (MPV not running?)", file=sys.stderr)
return 1
# Handle piped input (add to playlist) # Handle piped input (add to playlist)
if result: if result:
# If result is a list of items, add them to playlist # If result is a list of items, add them to playlist
@@ -366,7 +377,7 @@ CMDLET = Cmdlet(
CmdletArg( CmdletArg(
name="clear", name="clear",
type="flag", type="flag",
description="Remove the selected item from the playlist" description="Remove the selected item, or clear entire playlist if no index provided"
), ),
CmdletArg( CmdletArg(
name="list", name="list",

View File

@@ -54,9 +54,16 @@ def _progress_callback(status: Dict[str, Any]) -> None:
if event == "downloading": if event == "downloading":
percent = status.get("_percent_str", "?") percent = status.get("_percent_str", "?")
speed = status.get("_speed_str", "?") speed = status.get("_speed_str", "?")
debug(f"Downloading {percent} at {speed}") eta = status.get("_eta_str", "?")
# Print progress to stdout with carriage return to update in place
sys.stdout.write(f"\r[download] {percent} at {speed} ETA {eta} ")
sys.stdout.flush()
elif event == "finished": elif event == "finished":
debug(f"✓ Download finished: {status.get('filename')}") # Clear the progress line
sys.stdout.write("\r" + " " * 70 + "\r")
sys.stdout.flush()
# Log finished message (visible)
log(f"✓ Download finished: {status.get('filename')}")
elif event in ("postprocessing", "processing"): elif event in ("postprocessing", "processing"):
debug(f"Post-processing: {status.get('postprocessor')}") debug(f"Post-processing: {status.get('postprocessor')}")
@@ -100,8 +107,8 @@ def list_formats(url: str, no_playlist: bool = False, playlist_items: Optional[s
try: try:
ydl_opts = { ydl_opts = {
"quiet": False, "quiet": True,
"no_warnings": False, "no_warnings": True,
"socket_timeout": 30, "socket_timeout": 30,
} }
@@ -154,9 +161,9 @@ def _build_ytdlp_options(opts: DownloadOptions) -> Dict[str, Any]:
base_options: Dict[str, Any] = { base_options: Dict[str, Any] = {
"outtmpl": outtmpl, "outtmpl": outtmpl,
"quiet": False, "quiet": True,
"no_warnings": False, "no_warnings": True,
"noprogress": False, "noprogress": True,
"socket_timeout": 30, "socket_timeout": 30,
"retries": 10, "retries": 10,
"fragment_retries": 10, "fragment_retries": 10,