kk
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -246,4 +246,5 @@ medios.db
|
|||||||
medios*
|
medios*
|
||||||
mypy.ini
|
mypy.ini
|
||||||
\logs\*
|
\logs\*
|
||||||
logs
|
logs
|
||||||
|
logs.db
|
||||||
@@ -436,9 +436,9 @@ local function get_mpv_ipc_path()
|
|||||||
-- Fallback: fixed pipe/socket name used by MPV/mpv_ipc.py
|
-- Fallback: fixed pipe/socket name used by MPV/mpv_ipc.py
|
||||||
local sep = package and package.config and package.config:sub(1, 1) or '/'
|
local sep = package and package.config and package.config:sub(1, 1) or '/'
|
||||||
if sep == '\\' then
|
if sep == '\\' then
|
||||||
return '\\\\.\\pipe\\mpv-medeia-macina'
|
return '\\\\.\\pipe\\mpv-medios-macina'
|
||||||
end
|
end
|
||||||
return '/tmp/mpv-medeia-macina.sock'
|
return '/tmp/mpv-medios-macina.sock'
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ensure_mpv_ipc_server()
|
local function ensure_mpv_ipc_server()
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from typing import Any, Dict, Optional, List, BinaryIO, Tuple, cast
|
|||||||
from SYS.logger import debug
|
from SYS.logger import debug
|
||||||
|
|
||||||
# Fixed pipe name for persistent MPV connection across all Python sessions
|
# 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")
|
MPV_LUA_SCRIPT_PATH = str(Path(__file__).resolve().parent / "LUA" / "main.lua")
|
||||||
|
|
||||||
_LYRIC_PROCESS: Optional[subprocess.Popen] = None
|
_LYRIC_PROCESS: Optional[subprocess.Popen] = None
|
||||||
|
|||||||
131
SYS/pipeline.py
131
SYS/pipeline.py
@@ -653,6 +653,28 @@ def set_last_result_table(
|
|||||||
logger.exception("Failed to sort overlay result_table and reorder items")
|
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:
|
def set_last_result_items_only(items: Optional[List[Any]]) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -1835,6 +1857,8 @@ class PipelineExecutor:
|
|||||||
except Exception:
|
except Exception:
|
||||||
stage_table = None
|
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)
|
# Prefer selecting from the last selectable *table* (search/playlist)
|
||||||
# rather than from display-only emitted items, unless we're explicitly
|
# rather than from display-only emitted items, unless we're explicitly
|
||||||
# selecting from an overlay table.
|
# selecting from an overlay table.
|
||||||
@@ -1846,9 +1870,11 @@ class PipelineExecutor:
|
|||||||
items_list = ctx.get_last_selectable_result_items() or []
|
items_list = ctx.get_last_selectable_result_items() or []
|
||||||
else:
|
else:
|
||||||
items_list = ctx.get_last_result_items() or []
|
items_list = ctx.get_last_result_items() or []
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
|
debug(f"@N: Exception getting items_list: {exc}")
|
||||||
items_list = []
|
items_list = []
|
||||||
|
|
||||||
|
debug(f"@N: selection_indices={selection_indices}, items_list length={len(items_list)}")
|
||||||
resolved_items = items_list if items_list else []
|
resolved_items = items_list if items_list else []
|
||||||
if items_list:
|
if items_list:
|
||||||
filtered = [
|
filtered = [
|
||||||
@@ -1920,12 +1946,15 @@ class PipelineExecutor:
|
|||||||
filtered = track_items
|
filtered = track_items
|
||||||
table_type_hint = "tidal.track"
|
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(
|
if PipelineExecutor._maybe_run_class_selector(
|
||||||
ctx,
|
ctx,
|
||||||
config,
|
config,
|
||||||
filtered,
|
filtered,
|
||||||
stage_is_last=(not stages)):
|
stage_is_last=(not stages)):
|
||||||
|
debug(f"@N: _maybe_run_class_selector returned True, returning False")
|
||||||
return False, None
|
return False, None
|
||||||
|
debug(f"@N: _maybe_run_class_selector returned False, continuing")
|
||||||
|
|
||||||
from cmdlet._shared import coerce_to_pipe_object
|
from cmdlet._shared import coerce_to_pipe_object
|
||||||
|
|
||||||
@@ -1934,6 +1963,7 @@ class PipelineExecutor:
|
|||||||
filtered_pipe_objs
|
filtered_pipe_objs
|
||||||
if len(filtered_pipe_objs) > 1 else filtered_pipe_objs[0]
|
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:
|
if pipeline_session and worker_manager:
|
||||||
try:
|
try:
|
||||||
@@ -2069,10 +2099,12 @@ class PipelineExecutor:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if not stages:
|
if not stages:
|
||||||
|
debug(f"@N: stages is empty, checking auto_stage and metadata")
|
||||||
if isinstance(table_type, str) and table_type.startswith("metadata."):
|
if isinstance(table_type, str) and table_type.startswith("metadata."):
|
||||||
print("Auto-applying metadata selection via get-tag")
|
print("Auto-applying metadata selection via get-tag")
|
||||||
stages.append(["get-tag"])
|
stages.append(["get-tag"])
|
||||||
elif auto_stage:
|
elif auto_stage:
|
||||||
|
debug(f"@N: Found auto_stage={auto_stage}, appending")
|
||||||
try:
|
try:
|
||||||
print(f"Auto-running selection via {auto_stage[0]}")
|
print(f"Auto-running selection via {auto_stage[0]}")
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -2084,9 +2116,7 @@ class PipelineExecutor:
|
|||||||
stages.append(list(auto_stage))
|
stages.append(list(auto_stage))
|
||||||
debug(f"Inserted auto stage before row action: {stages[-1]}")
|
debug(f"Inserted auto stage before row action: {stages[-1]}")
|
||||||
|
|
||||||
# If the caller included a selection (e.g., @1) try to attach
|
# Attach selection args to auto stage
|
||||||
# the selection args immediately to the inserted auto stage so
|
|
||||||
# the expansion is effective in a single pass.
|
|
||||||
if selection_indices:
|
if selection_indices:
|
||||||
try:
|
try:
|
||||||
if not _apply_row_action_to_stage(len(stages) - 1):
|
if not _apply_row_action_to_stage(len(stages) - 1):
|
||||||
@@ -2115,61 +2145,56 @@ class PipelineExecutor:
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to attach selection args to auto-inserted stage")
|
logger.exception("Failed to attach selection args to auto-inserted stage")
|
||||||
|
|
||||||
# If no auto stage inserted and there are selection-action tokens available
|
# Look for row_action in payload if still no stages
|
||||||
# for the single selected row, apply it as the pipeline stage so a bare
|
if not stages and selection_indices and len(selection_indices) == 1:
|
||||||
# `@N` runs the intended action (e.g., get-file for hash-backed rows).
|
debug(f"@N: No stages and no auto_stage, looking for row_action in payload")
|
||||||
if not stages and selection_indices and len(selection_indices) == 1:
|
try:
|
||||||
|
idx = selection_indices[0]
|
||||||
|
debug(f"@N: idx={idx}, looking for row_action")
|
||||||
|
row_action = None
|
||||||
try:
|
try:
|
||||||
idx = selection_indices[0]
|
row_action = ctx.get_current_stage_table_row_selection_action(idx)
|
||||||
debug(f"@N initial selection idx={idx} last_items={len(ctx.get_last_result_items() or [])}")
|
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
|
row_action = None
|
||||||
|
|
||||||
|
if not row_action:
|
||||||
|
debug(f"@N: row_action not found from table, checking payload")
|
||||||
try:
|
try:
|
||||||
row_action = ctx.get_current_stage_table_row_selection_action(idx)
|
items = ctx.get_last_result_items() or []
|
||||||
except Exception:
|
debug(f"@N: got items, length={len(items)}")
|
||||||
logger.exception("Failed to get current_stage_table row selection action for idx %s", idx)
|
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
|
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:
|
try:
|
||||||
items = ctx.get_last_result_items() or []
|
worker_manager.log_step(
|
||||||
if 0 <= idx < len(items):
|
pipeline_session.worker_id,
|
||||||
maybe = items[idx]
|
f"@N applied row action -> {' '.join(row_action)}",
|
||||||
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]
|
|
||||||
except Exception:
|
except Exception:
|
||||||
row_action = None
|
logger.exception("Failed to record pipeline log step for applied row action (pipeline_session=%r)", getattr(pipeline_session, 'worker_id', None))
|
||||||
|
except Exception:
|
||||||
if row_action:
|
logger.exception("Failed to apply single-row selection 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))
|
|
||||||
else:
|
else:
|
||||||
first_cmd = stages[0][0] if stages and stages[0] else None
|
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 (
|
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).
|
# selection-expansion logic can still run (e.g., for example selectors).
|
||||||
return True, piped_result
|
return True, piped_result
|
||||||
else:
|
else:
|
||||||
|
debug(f"@N: No items to select from (items_list empty)")
|
||||||
print("No previous results to select from\n")
|
print("No previous results to select from\n")
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
@@ -2342,6 +2368,7 @@ class PipelineExecutor:
|
|||||||
ctx = sys.modules[__name__]
|
ctx = sys.modules[__name__]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
debug(f"execute_tokens: tokens={tokens}")
|
||||||
self._try_clear_pipeline_stop(ctx)
|
self._try_clear_pipeline_stop(ctx)
|
||||||
|
|
||||||
# REPL guard: stage-local tables should not persist across independent
|
# REPL guard: stage-local tables should not persist across independent
|
||||||
|
|||||||
@@ -795,6 +795,16 @@ class Table:
|
|||||||
elif isinstance(result, str):
|
elif isinstance(result, str):
|
||||||
row.add_column("Result", result)
|
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
|
return self
|
||||||
|
|
||||||
def get_row_payload(self, row_index: int) -> Optional[Any]:
|
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 getattr(self.rows[row_index], "payload", None)
|
||||||
return 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]:
|
def get_payloads(self) -> List[Any]:
|
||||||
"""Return the payloads for every row, preserving table order."""
|
"""Return the payloads for every row, preserving table order."""
|
||||||
payloads: List[Any] = []
|
payloads: List[Any] = []
|
||||||
|
|||||||
@@ -663,8 +663,9 @@ class search_file(Cmdlet):
|
|||||||
if backend is None:
|
if backend is None:
|
||||||
# Last-resort: instantiate full registry for this backend only
|
# Last-resort: instantiate full registry for this backend only
|
||||||
from Store import Store as _Store
|
from Store import Store as _Store
|
||||||
_store = _Store(config=config)
|
_store = _Store(config=config, suppress_debug=True)
|
||||||
backend = _store[backend_name]
|
if _store.is_available(backend_name):
|
||||||
|
backend = _store[backend_name]
|
||||||
except Exception:
|
except Exception:
|
||||||
backend = None
|
backend = None
|
||||||
if backend is 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)
|
target_backend = get_backend_instance(config, backend_to_search, suppress_debug=True)
|
||||||
if target_backend is None:
|
if target_backend is None:
|
||||||
from Store import Store as _Store
|
from Store import Store as _Store
|
||||||
_store = _Store(config=config)
|
_store = _Store(config=config, suppress_debug=True)
|
||||||
target_backend = _store[backend_to_search]
|
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:
|
except Exception as exc:
|
||||||
log(f"Backend '{backend_to_search}' not found: {exc}", file=sys.stderr)
|
log(f"Backend '{backend_to_search}' not found: {exc}", file=sys.stderr)
|
||||||
db.update_worker_status(worker_id, "error")
|
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)
|
backend = get_backend_instance(config, backend_name, suppress_debug=True)
|
||||||
if backend is None:
|
if backend is None:
|
||||||
from Store import Store as _Store
|
from Store import Store as _Store
|
||||||
_store = _Store(config=config)
|
_store = _Store(config=config, suppress_debug=True)
|
||||||
backend = _store[backend_name]
|
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)
|
searched_backends.append(backend_name)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user