From 6309a3ff3e08a419d3f65debde0ce7f6eed6f36b Mon Sep 17 00:00:00 2001 From: Nose Date: Mon, 2 Feb 2026 14:09:42 -0800 Subject: [PATCH] hh --- SYS/pipeline.py | 9 +---- cmdlet/_shared.py | 62 +++++++++++++++++++++++++++++++ cmdlet/add_file.py | 15 ++------ cmdlet/add_tag.py | 11 ++---- cmdlet/download_file.py | 26 +++++-------- cmdlet/get_metadata.py | 11 ++---- cmdlet/get_tag.py | 82 ++++++++++++++++++++++++++--------------- 7 files changed, 134 insertions(+), 82 deletions(-) diff --git a/SYS/pipeline.py b/SYS/pipeline.py index 5214121..1394a1f 100644 --- a/SYS/pipeline.py +++ b/SYS/pipeline.py @@ -1923,14 +1923,9 @@ class PipelineExecutor: # PHASE 4: Retrieve and filter items from current result set # ==================================================================== # Cache items_list to avoid redundant lookups in helper functions below. + # Priority: display items (from overlays like get-metadata) > last result items try: - if display_table is not None and stage_table is display_table: - items_list = ctx.get_last_result_items() or [] - else: - if hasattr(ctx, "get_last_selectable_result_items"): - items_list = ctx.get_last_selectable_result_items() or [] - else: - items_list = ctx.get_last_result_items() or [] + items_list = ctx.get_last_result_items() or [] except Exception as exc: debug(f"@N: Exception getting items_list: {exc}") items_list = [] diff --git a/cmdlet/_shared.py b/cmdlet/_shared.py index cfa276c..599f5f0 100644 --- a/cmdlet/_shared.py +++ b/cmdlet/_shared.py @@ -3825,3 +3825,65 @@ def check_url_exists_in_storage( _mark_preflight_checked() return True + +def display_and_persist_items( + items: List[Any], + title: str = "Result", + subject: Optional[Any] = None, + display_type: str = "item", + table: Optional[Table] = None, +) -> None: + """Display detail panels and persist items for @N selection. + + This helper function: + 1. Renders individual detail panels for each item + 2. Creates a result table with all items (or uses provided table) + 3. Persists the table so @N selection works across command boundaries + + Args: + items: List of items/dicts to display and make selectable + title: Title for the result table (default: "Result") + subject: Optional subject to associate with the results (usually first item or list) + display_type: Type of display - "item" (default), "tag", or "custom" (no panels) + table: Optional pre-built Table object (if None, creates new Table with title) + """ + if not items: + return + + try: + # Create result table if not provided + if table is None: + table = Table(title=title) + + # Render detail panels for each item (unless display_type is "custom") + if display_type != "custom": + try: + from SYS.rich_display import render_item_details_panel + + # Determine panel title prefix based on display type + panel_prefix = "Tag" if display_type == "tag" else "Item" + + # Render detail panel for each item + for idx, item in enumerate(items, 1): + try: + render_item_details_panel(item, title=f"#{idx} {panel_prefix} Details") + except Exception: + pass + except Exception: + pass + + # Add items to table if not already added by caller + if not getattr(table, "_items_added", False): + for item in items: + table.add_result(item) + + setattr(table, "_rendered_by_cmdlet", True) + + # Use provided subject or default to first item + if subject is None: + subject = items[0] if len(items) == 1 else list(items) + + # Persist table for @N selection across command boundaries + pipeline_context.set_last_result_table(table, list(items), subject=subject) + except Exception: + pass diff --git a/cmdlet/add_file.py b/cmdlet/add_file.py index f202b25..783c3c3 100644 --- a/cmdlet/add_file.py +++ b/cmdlet/add_file.py @@ -704,19 +704,10 @@ class Add_File(Cmdlet): except Exception: pass - for idx, payload in enumerate(collected_payloads, 1): - render_item_details_panel(payload, title=f"#{idx} Item Details") - - table = Table("Result") - for payload in collected_payloads: - table.add_result(payload) - setattr(table, "_rendered_by_cmdlet", True) subject = collected_payloads[0] if len(collected_payloads) == 1 else collected_payloads - ctx.set_last_result_table_overlay( - table, - collected_payloads, - subject=subject - ) + # Use helper to display items and make them @-selectable + from ._shared import display_and_persist_items + display_and_persist_items(collected_payloads, title="Result", subject=subject) try: ctx.set_last_result_items_only(list(collected_payloads)) diff --git a/cmdlet/add_tag.py b/cmdlet/add_tag.py index d8dd425..20e58b8 100644 --- a/cmdlet/add_tag.py +++ b/cmdlet/add_tag.py @@ -1082,15 +1082,10 @@ class Add_Tag(Cmdlet): from SYS.rich_display import render_item_details_panel from SYS.result_table import Table - for idx, item in enumerate(display_items, 1): - render_item_details_panel(item, title=f"#{idx} Item Details") - - table = Table("Result") - for item in display_items: - table.add_result(item) - setattr(table, "_rendered_by_cmdlet", True) subject = display_items[0] if len(display_items) == 1 else list(display_items) - ctx.set_last_result_table_overlay(table, list(display_items), subject=subject) + # Use helper to display items and make them @-selectable + from ._shared import display_and_persist_items + display_and_persist_items(list(display_items), title="Result", subject=subject) except Exception: pass diff --git a/cmdlet/download_file.py b/cmdlet/download_file.py index f02f8ed..f0faecd 100644 --- a/cmdlet/download_file.py +++ b/cmdlet/download_file.py @@ -743,23 +743,10 @@ class Download_File(Cmdlet): pass try: - from SYS.rich_display import render_item_details_panel - from SYS.result_table import Table - - for idx, item in enumerate(emitted_items, 1): - render_item_details_panel(item, title=f"#{idx} Item Details") - - table = Table("Result") - for item in emitted_items: - table.add_result(item) - setattr(table, "_rendered_by_cmdlet", True) - subject = emitted_items[0] if len(emitted_items) == 1 else list(emitted_items) - pipeline_context.set_last_result_table_overlay( - table, - list(emitted_items), - subject=subject, - ) + # Use helper to display items and make them @-selectable + from ._shared import display_and_persist_items + display_and_persist_items(list(emitted_items), title="Result", subject=subject) except Exception: pass @@ -1969,7 +1956,12 @@ class Download_File(Cmdlet): else: import re - if not re.match(r"^\s*#?\d+\s*$", str(query_format)): + if re.match(r"^\s*#?\d+\s*$", str(query_format)): + # Numeric format like "720" or "1080p" - will be resolved later via resolve_height_selector + # Don't set ytdl_format yet; let it fall through to per-URL resolution + pass + else: + # Non-numeric format string - use as literal ytdl_format = query_format debug(f"DEBUG: [download-file] Using literal query_format '{query_format}' as ytdl_format") playlist_selection_handled = False diff --git a/cmdlet/get_metadata.py b/cmdlet/get_metadata.py index 37dcd32..0478c69 100644 --- a/cmdlet/get_metadata.py +++ b/cmdlet/get_metadata.py @@ -317,14 +317,9 @@ class Get_Metadata(Cmdlet): "get-metadata", list(args)) self._add_table_body_row(table, row) - ctx.set_last_result_table_overlay(table, [row], row) - try: - from SYS.rich_display import render_item_details_panel - - render_item_details_panel(row) - table._rendered_by_cmdlet = True - except Exception: - pass + # Use helper to display item and make it @-selectable + from ._shared import display_and_persist_items + display_and_persist_items([row], title=table_title, subject=row) ctx.emit(row) return 0 diff --git a/cmdlet/get_tag.py b/cmdlet/get_tag.py index 481984a..7b49e9e 100644 --- a/cmdlet/get_tag.py +++ b/cmdlet/get_tag.py @@ -323,10 +323,21 @@ def _emit_tags_as_table( """ from SYS.result_table import ItemDetailView, extract_item_metadata - # Prepare metadata for the detail view - metadata = extract_item_metadata(subject) + # Prepare metadata for the detail view, extracting all fields from subject first + metadata = extract_item_metadata(subject) or {} - # Overlays/Overrides from explicit args if subject was partial + # Preserve all additional fields from subject dict if it's a dict-like object + if isinstance(subject, dict): + for key, value in subject.items(): + # Skip internal/control fields + if not key.startswith("_") and key not in {"selection_action", "selection_args"}: + # Convert keys to readable labels (snake_case -> Title Case) + label = str(key).replace("_", " ").title() + # Only add if not already present from extract_item_metadata + if label not in metadata and value is not None: + metadata[label] = value + + # Apply explicit parameter overrides (these take priority) if item_title: metadata["Title"] = item_title if file_hash: @@ -341,7 +352,7 @@ def _emit_tags_as_table( table = ItemDetailView("Tags", item_metadata=metadata, max_columns=1, exclude_tags=True) table.set_source_command("get-tag", []) - # Create TagItem for each tag + # Create TagItem for each tag and add to table tag_items = [] for idx, tag_name in enumerate(tags_list, start=1): tag_item = TagItem( @@ -356,38 +367,38 @@ def _emit_tags_as_table( table.add_result(tag_item) # Also emit to pipeline for downstream processing ctx.emit(tag_item) - - # Store the table and items in history so @.. works to go back - # Use overlay mode so it doesn't push the previous search to history stack - # This makes get-tag behave like a transient view - table_applied = False - try: - ctx.set_last_result_table_overlay(table, tag_items, subject) - table_applied = True - except AttributeError: - try: - ctx.set_last_result_table(table, tag_items, subject) - table_applied = True - except Exception: - table_applied = False - except Exception: - table_applied = False - - # Display the rich panel (metadata info) if not in quiet/emit-only mode. - # In the TUI, this output is captured and shown in the log pane. + + # Mark that items were already added to the table + setattr(table, "_items_added", True) + + # Display the table and persist for @N selection if not quiet: try: from SYS.rich_display import stdout_console stdout_console().print(table) except Exception: pass - - if table_applied: - try: - if hasattr(ctx, "set_current_stage_table"): - ctx.set_current_stage_table(table) - except Exception: - pass + + # Use the shared helper to persist the table for @N selection + try: + from cmdlet._shared import display_and_persist_items + # Skip panel rendering since table already exists with custom ItemDetailView + display_and_persist_items( + tag_items, + title=table.title if hasattr(table, 'title') else "Tags", + subject=subject, + display_type="custom", + table=table, + ) + except Exception: + pass + + # Also update the current stage table for TUI + try: + if hasattr(ctx, "set_current_stage_table"): + ctx.set_current_stage_table(table) + except Exception: + pass # Note: CLI will handle displaying the table via ResultTable formatting @@ -1705,6 +1716,11 @@ def _run_impl(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: # For store-backed items (Hydrus/Folders), we want the latest state. if display_tags and not emit_mode and not is_store_backed: subject_payload = _subject_payload_with(display_tags) + # Merge the full result object into subject_payload so all original metadata is preserved + if isinstance(result, dict): + for key, value in result.items(): + if key not in subject_payload and not key.startswith("_"): + subject_payload[key] = value _emit_tags_as_table( display_tags, file_hash=file_hash, @@ -1739,6 +1755,12 @@ def _run_impl(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: current, service_name if source == "hydrus" else None, ) + # Merge the full result object into subject_payload so all original metadata is preserved + # (e.g., url, source_url, etc. from search results) + if isinstance(result, dict): + for key, value in result.items(): + if key not in subject_payload and not key.startswith("_"): + subject_payload[key] = value _emit_tags_as_table( current, file_hash=file_hash,