diff --git a/CLI.py b/CLI.py index daa7194..77b52f8 100644 --- a/CLI.py +++ b/CLI.py @@ -886,33 +886,10 @@ def _execute_pipeline(tokens: list): source_args = ctx.get_last_result_table_source_args() if source_cmd == 'search-file' and source_args and 'youtube' in source_args: - 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']) + # 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") + stages.append(['.pipe']) else: print(f"No items matched selection in pipeline\n") diff --git a/cmdlets/get_file.py b/cmdlets/get_file.py index cb8c519..d0b8fcc 100644 --- a/cmdlets/get_file.py +++ b/cmdlets/get_file.py @@ -453,10 +453,10 @@ def _handle_search_result(result: Any, args: Sequence[str], config: Dict[str, An if storage_name.lower() == 'hydrus': return _handle_hydrus_file(file_hash, file_title, config, args, mime_type=mime_type) 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': # 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': # Extract magnet_id from result (search-file stores it in full_metadata or as custom attribute) if not magnet_id: @@ -591,7 +591,7 @@ def _handle_hydrus_file(file_hash: Optional[str], file_title: str, config: Dict[ 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.""" if not file_path: log("Error: No file path provided", file=sys.stderr) diff --git a/cmdlets/pipe.py b/cmdlets/pipe.py index 4471df7..1f50439 100644 --- a/cmdlets/pipe.py +++ b/cmdlets/pipe.py @@ -127,6 +127,17 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: else: debug("Failed to pause playback (MPV not running?)", file=sys.stderr) 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) if result: @@ -366,7 +377,7 @@ CMDLET = Cmdlet( CmdletArg( name="clear", type="flag", - description="Remove the selected item from the playlist" + description="Remove the selected item, or clear entire playlist if no index provided" ), CmdletArg( name="list", diff --git a/helper/download.py b/helper/download.py index 84933ec..dd6473e 100644 --- a/helper/download.py +++ b/helper/download.py @@ -54,9 +54,16 @@ def _progress_callback(status: Dict[str, Any]) -> None: if event == "downloading": percent = status.get("_percent_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": - 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"): 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: ydl_opts = { - "quiet": False, - "no_warnings": False, + "quiet": True, + "no_warnings": True, "socket_timeout": 30, } @@ -154,9 +161,9 @@ def _build_ytdlp_options(opts: DownloadOptions) -> Dict[str, Any]: base_options: Dict[str, Any] = { "outtmpl": outtmpl, - "quiet": False, - "no_warnings": False, - "noprogress": False, + "quiet": True, + "no_warnings": True, + "noprogress": True, "socket_timeout": 30, "retries": 10, "fragment_retries": 10,