From 4f7bac464f23ad64eb9715c0282981ee5c83357b Mon Sep 17 00:00:00 2001 From: Nose Date: Mon, 2 Feb 2026 02:32:28 -0800 Subject: [PATCH] kk --- .gitignore | 3 +- MPV/LUA/main.lua | 4 +- MPV/mpv_ipc.py | 2 +- SYS/pipeline.py | 131 +++++++++++++++++++++++++----------------- SYS/result_table.py | 28 +++++++++ cmdlet/search_file.py | 22 +++++-- 6 files changed, 128 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index 4db6640..d51fefd 100644 --- a/.gitignore +++ b/.gitignore @@ -246,4 +246,5 @@ medios.db medios* mypy.ini \logs\* -logs \ No newline at end of file +logs +logs.db \ No newline at end of file diff --git a/MPV/LUA/main.lua b/MPV/LUA/main.lua index 7d3dfda..776fbdf 100644 --- a/MPV/LUA/main.lua +++ b/MPV/LUA/main.lua @@ -436,9 +436,9 @@ local function get_mpv_ipc_path() -- Fallback: fixed pipe/socket name used by MPV/mpv_ipc.py local sep = package and package.config and package.config:sub(1, 1) or '/' if sep == '\\' then - return '\\\\.\\pipe\\mpv-medeia-macina' + return '\\\\.\\pipe\\mpv-medios-macina' end - return '/tmp/mpv-medeia-macina.sock' + return '/tmp/mpv-medios-macina.sock' end local function ensure_mpv_ipc_server() diff --git a/MPV/mpv_ipc.py b/MPV/mpv_ipc.py index a90782c..d2b7e46 100644 --- a/MPV/mpv_ipc.py +++ b/MPV/mpv_ipc.py @@ -21,7 +21,7 @@ from typing import Any, Dict, Optional, List, BinaryIO, Tuple, cast from SYS.logger import debug # Fixed pipe name for persistent MPV connection across all Python sessions -FIXED_IPC_PIPE_NAME = "mpv-medeia-macina" +FIXED_IPC_PIPE_NAME = "mpv-medios-macina" MPV_LUA_SCRIPT_PATH = str(Path(__file__).resolve().parent / "LUA" / "main.lua") _LYRIC_PROCESS: Optional[subprocess.Popen] = None diff --git a/SYS/pipeline.py b/SYS/pipeline.py index 108b122..17cc539 100644 --- a/SYS/pipeline.py +++ b/SYS/pipeline.py @@ -653,6 +653,28 @@ def set_last_result_table( logger.exception("Failed to sort overlay result_table and reorder items") +def set_last_result_table_overlay( + result_table: Optional[Any], + items: Optional[List[Any]] = None, + subject: Optional[Any] = None +) -> None: + """Store a display table and items WITHOUT affecting history stack. + + Used by action cmdlets (get-metadata, get-tag, get-url) to display detail + panels or filtered results without disrupting the primary search-result history. + """ + state = _get_pipeline_state() + state.display_table = result_table + state.display_items = items or [] + state.display_subject = subject + +def set_last_result_table_preserve_history( + result_table: Optional[Any], + items: Optional[List[Any]] = None, + subject: Optional[Any] = None +) -> None: + """Compatibility alias for set_last_result_table_overlay.""" + set_last_result_table_overlay(result_table, items=items, subject=subject) def set_last_result_items_only(items: Optional[List[Any]]) -> None: """ @@ -1835,6 +1857,8 @@ class PipelineExecutor: except Exception: stage_table = None + debug(f"@N: stage_table={stage_table is not None}, display_table={display_table is not None}") + # Prefer selecting from the last selectable *table* (search/playlist) # rather than from display-only emitted items, unless we're explicitly # selecting from an overlay table. @@ -1846,9 +1870,11 @@ class PipelineExecutor: items_list = ctx.get_last_selectable_result_items() or [] else: items_list = ctx.get_last_result_items() or [] - except Exception: + except Exception as exc: + debug(f"@N: Exception getting items_list: {exc}") items_list = [] + debug(f"@N: selection_indices={selection_indices}, items_list length={len(items_list)}") resolved_items = items_list if items_list else [] if items_list: filtered = [ @@ -1920,12 +1946,15 @@ class PipelineExecutor: filtered = track_items table_type_hint = "tidal.track" + debug(f"@N: calling _maybe_run_class_selector with filtered={len(filtered)} items, stage_is_last={not stages}") if PipelineExecutor._maybe_run_class_selector( ctx, config, filtered, stage_is_last=(not stages)): + debug(f"@N: _maybe_run_class_selector returned True, returning False") return False, None + debug(f"@N: _maybe_run_class_selector returned False, continuing") from cmdlet._shared import coerce_to_pipe_object @@ -1934,6 +1963,7 @@ class PipelineExecutor: filtered_pipe_objs if len(filtered_pipe_objs) > 1 else filtered_pipe_objs[0] ) + debug(f"@N: coerced piped_result, stages={stages}") if pipeline_session and worker_manager: try: @@ -2069,10 +2099,12 @@ class PipelineExecutor: return False if not stages: + debug(f"@N: stages is empty, checking auto_stage and metadata") if isinstance(table_type, str) and table_type.startswith("metadata."): print("Auto-applying metadata selection via get-tag") stages.append(["get-tag"]) elif auto_stage: + debug(f"@N: Found auto_stage={auto_stage}, appending") try: print(f"Auto-running selection via {auto_stage[0]}") except Exception: @@ -2084,9 +2116,7 @@ class PipelineExecutor: stages.append(list(auto_stage)) debug(f"Inserted auto stage before row action: {stages[-1]}") - # If the caller included a selection (e.g., @1) try to attach - # the selection args immediately to the inserted auto stage so - # the expansion is effective in a single pass. + # Attach selection args to auto stage if selection_indices: try: if not _apply_row_action_to_stage(len(stages) - 1): @@ -2115,61 +2145,56 @@ class PipelineExecutor: except Exception: logger.exception("Failed to attach selection args to auto-inserted stage") - # If no auto stage inserted and there are selection-action tokens available - # for the single selected row, apply it as the pipeline stage so a bare - # `@N` runs the intended action (e.g., get-file for hash-backed rows). - if not stages and selection_indices and len(selection_indices) == 1: + # Look for row_action in payload if still no stages + if not stages and selection_indices and len(selection_indices) == 1: + debug(f"@N: No stages and no auto_stage, looking for row_action in payload") + try: + idx = selection_indices[0] + debug(f"@N: idx={idx}, looking for row_action") + row_action = None try: - idx = selection_indices[0] - debug(f"@N initial selection idx={idx} last_items={len(ctx.get_last_result_items() or [])}") + row_action = ctx.get_current_stage_table_row_selection_action(idx) + debug(f"@N: row_action from table={row_action}") + except Exception as exc: + debug(f"@N: Exception getting row_selection_action: {exc}") row_action = None + + if not row_action: + debug(f"@N: row_action not found from table, checking payload") try: - row_action = ctx.get_current_stage_table_row_selection_action(idx) - except Exception: - logger.exception("Failed to get current_stage_table row selection action for idx %s", idx) + items = ctx.get_last_result_items() or [] + debug(f"@N: got items, length={len(items)}") + if 0 <= idx < len(items): + maybe = items[idx] + try: + if isinstance(maybe, dict): + debug(f"@N: payload is dict with _selection_action={maybe.get('_selection_action')}") + else: + debug(f"@N: payload type={type(maybe).__name__}") + except Exception: + pass + if isinstance(maybe, dict): + candidate = maybe.get("_selection_action") + if isinstance(candidate, (list, tuple)): + row_action = [str(x) for x in candidate if x is not None] + debug(f"@N: extracted row_action from payload={row_action}") + except Exception as exc: + debug(f"@N: Exception checking payload: {exc}") row_action = None - if not row_action: + if row_action: + debug(f"@N: FOUND row_action, appending {row_action}") + stages.append(row_action) + if pipeline_session and worker_manager: try: - items = ctx.get_last_result_items() or [] - if 0 <= idx < len(items): - maybe = items[idx] - try: - if isinstance(maybe, dict): - debug(f"@N payload: hash={maybe.get('hash')} store={maybe.get('store')} _selection_args={maybe.get('_selection_args')} _selection_action={maybe.get('_selection_action')}") - else: - debug(f"@N payload object type: {type(maybe).__name__}") - except Exception: - logger.exception("Failed to debug selection payload for index %s", idx) - if isinstance(maybe, dict): - candidate = maybe.get("_selection_action") - if isinstance(candidate, (list, tuple)): - row_action = [str(x) for x in candidate if x is not None] + worker_manager.log_step( + pipeline_session.worker_id, + f"@N applied row action -> {' '.join(row_action)}", + ) except Exception: - row_action = None - - if row_action: - debug(f"@N applying row action -> {row_action}") - stages.append(row_action) - if pipeline_session and worker_manager: - try: - worker_manager.log_step( - pipeline_session.worker_id, - f"@N applied row action -> {' '.join(row_action)}", - ) - except Exception: - logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None)) - except Exception: - logger.exception("Failed to apply single-row selection action") - stages.append(row_action) - if pipeline_session and worker_manager: - try: - worker_manager.log_step( - pipeline_session.worker_id, - f"@N applied row action -> {' '.join(row_action)}", - ) - except Exception: - logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None)) + logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None)) + except Exception: + logger.exception("Failed to apply single-row selection action") else: first_cmd = stages[0][0] if stages and stages[0] else None if isinstance(table_type, str) and table_type.startswith("metadata.") and first_cmd not in ( @@ -2224,6 +2249,7 @@ class PipelineExecutor: # selection-expansion logic can still run (e.g., for example selectors). return True, piped_result else: + debug(f"@N: No items to select from (items_list empty)") print("No previous results to select from\n") return False, None @@ -2342,6 +2368,7 @@ class PipelineExecutor: ctx = sys.modules[__name__] try: + debug(f"execute_tokens: tokens={tokens}") self._try_clear_pipeline_stop(ctx) # REPL guard: stage-local tables should not persist across independent diff --git a/SYS/result_table.py b/SYS/result_table.py index ecc7175..d658fc3 100644 --- a/SYS/result_table.py +++ b/SYS/result_table.py @@ -795,6 +795,16 @@ class Table: elif isinstance(result, str): row.add_column("Result", result) + # Extract selection metadata from payload if available (for @N expansion) + if isinstance(result, dict): + sel_args = result.get("_selection_args") + if isinstance(sel_args, (list, tuple)): + row.selection_args = [str(x) for x in sel_args if x is not None] + + sel_action = result.get("_selection_action") + if isinstance(sel_action, (list, tuple)): + row.selection_action = [str(x) for x in sel_action if x is not None] + return self def get_row_payload(self, row_index: int) -> Optional[Any]: @@ -803,6 +813,24 @@ class Table: return getattr(self.rows[row_index], "payload", None) return None + def get_row_selection_args(self, row_index: int) -> Optional[List[str]]: + """Return selection arguments for the row at ``row_index`` from its payload.""" + payload = self.get_row_payload(row_index) + if isinstance(payload, dict): + args = payload.get("_selection_args") + if isinstance(args, (list, tuple)): + return [str(x) for x in args if x is not None] + return None + + def get_row_selection_action(self, row_index: int) -> Optional[List[str]]: + """Return primary selection action for the row at ``row_index`` from its payload.""" + payload = self.get_row_payload(row_index) + if isinstance(payload, dict): + action = payload.get("_selection_action") + if isinstance(action, (list, tuple)): + return [str(x) for x in action if x is not None] + return None + def get_payloads(self) -> List[Any]: """Return the payloads for every row, preserving table order.""" payloads: List[Any] = [] diff --git a/cmdlet/search_file.py b/cmdlet/search_file.py index 2307730..ae6974f 100644 --- a/cmdlet/search_file.py +++ b/cmdlet/search_file.py @@ -663,8 +663,9 @@ class search_file(Cmdlet): if backend is None: # Last-resort: instantiate full registry for this backend only from Store import Store as _Store - _store = _Store(config=config) - backend = _store[backend_name] + _store = _Store(config=config, suppress_debug=True) + if _store.is_available(backend_name): + backend = _store[backend_name] except Exception: backend = None if backend is None: @@ -812,8 +813,12 @@ class search_file(Cmdlet): target_backend = get_backend_instance(config, backend_to_search, suppress_debug=True) if target_backend is None: from Store import Store as _Store - _store = _Store(config=config) - target_backend = _store[backend_to_search] + _store = _Store(config=config, suppress_debug=True) + if _store.is_available(backend_to_search): + target_backend = _store[backend_to_search] + else: + debug(f"[search-file] Requested backend '{backend_to_search}' not found") + return 1 except Exception as exc: log(f"Backend '{backend_to_search}' not found: {exc}", file=sys.stderr) db.update_worker_status(worker_id, "error") @@ -838,8 +843,13 @@ class search_file(Cmdlet): backend = get_backend_instance(config, backend_name, suppress_debug=True) if backend is None: from Store import Store as _Store - _store = _Store(config=config) - backend = _store[backend_name] + _store = _Store(config=config, suppress_debug=True) + if _store.is_available(backend_name): + backend = _store[backend_name] + else: + # Configured backend name exists but has no registered implementation or failed to load. + # (e.g. 'all-debrid' being treated as a store but having no store provider). + continue searched_backends.append(backend_name)