updated old legacy store names
This commit is contained in:
+3
-3
@@ -1084,9 +1084,9 @@ def get_store_backend(
|
||||
registry = store_registry
|
||||
if registry is None:
|
||||
try:
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
registry = Store(config or {}, suppress_debug=suppress_debug)
|
||||
registry = BackendRegistry(config or {}, suppress_debug=suppress_debug)
|
||||
except Exception as exc:
|
||||
return None, None, exc
|
||||
|
||||
@@ -1110,7 +1110,7 @@ def get_preferred_store_backend(
|
||||
"""Prefer a targeted backend instance before falling back to registry lookup."""
|
||||
direct_exc: Optional[Exception] = None
|
||||
try:
|
||||
from Store.registry import get_backend_instance
|
||||
from PluginCore.backend_registry import get_backend_instance
|
||||
|
||||
backend = get_backend_instance(
|
||||
config or {},
|
||||
|
||||
@@ -14,7 +14,7 @@ from ._shared import (
|
||||
normalize_hash,
|
||||
)
|
||||
from SYS.logger import log
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
|
||||
class Delete_Url(Cmdlet):
|
||||
@@ -106,7 +106,7 @@ class Delete_Url(Cmdlet):
|
||||
|
||||
# Get backend and delete url
|
||||
try:
|
||||
storage = Store(config)
|
||||
storage = BackendRegistry(config)
|
||||
|
||||
store_override = parsed.get("instance")
|
||||
|
||||
|
||||
+67
-94
@@ -16,7 +16,7 @@ from SYS.pipeline_progress import PipelineProgress
|
||||
from SYS.result_publication import overlay_existing_result_table, publish_result_table
|
||||
from SYS.rich_display import show_available_plugins_panel, show_plugin_config_panel
|
||||
from SYS.utils_constant import ALL_SUPPORTED_EXTENSIONS
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
from API.HTTP import _download_direct_file
|
||||
from .. import _shared as sh
|
||||
|
||||
@@ -45,21 +45,21 @@ SUPPORTED_MEDIA_EXTENSIONS = ALL_SUPPORTED_EXTENSIONS
|
||||
|
||||
|
||||
class _CommandDependencies:
|
||||
"""Command-scope cache for Store and plugin instances to avoid repeated instantiation."""
|
||||
"""Command-scope cache for the backend registry and plugin instances."""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
self.config = config
|
||||
self._store: Optional[Store] = None
|
||||
self._backend_registry: Optional[BackendRegistry] = None
|
||||
self._plugins: Dict[str, Any] = {}
|
||||
|
||||
def get_store(self) -> Optional[Store]:
|
||||
"""Lazily initialize and return the command-scope Store instance."""
|
||||
if self._store is None:
|
||||
def get_backend_registry(self) -> Optional[BackendRegistry]:
|
||||
"""Lazily initialize and return the command-scope backend registry."""
|
||||
if self._backend_registry is None:
|
||||
try:
|
||||
self._store = Store(self.config)
|
||||
self._backend_registry = BackendRegistry(self.config)
|
||||
except Exception:
|
||||
self._store = None
|
||||
return self._store
|
||||
self._backend_registry = None
|
||||
return self._backend_registry
|
||||
|
||||
def get_plugin(self, name: str) -> Optional[Any]:
|
||||
"""Cached plugin lookup by name."""
|
||||
@@ -236,7 +236,7 @@ class Add_File(Cmdlet):
|
||||
|
||||
# Initialize command-scope dependency context (caches Store/plugins)
|
||||
deps = _CommandDependencies(config)
|
||||
storage_registry = deps.get_store()
|
||||
storage_registry = deps.get_backend_registry()
|
||||
|
||||
path_arg = parsed.get("path")
|
||||
location = parsed.get("instance")
|
||||
@@ -354,8 +354,8 @@ class Add_File(Cmdlet):
|
||||
is_storage_backend_location = False
|
||||
if location:
|
||||
try:
|
||||
store_for_lookup = storage_registry or deps.get_store()
|
||||
is_storage_backend_location = Add_File._resolve_backend_by_name(store_for_lookup, str(location)) is not None
|
||||
backend_registry_for_lookup = storage_registry or deps.get_backend_registry()
|
||||
is_storage_backend_location = Add_File._resolve_backend_by_name(backend_registry_for_lookup, str(location)) is not None
|
||||
except Exception:
|
||||
is_storage_backend_location = False
|
||||
|
||||
@@ -697,8 +697,8 @@ class Add_File(Cmdlet):
|
||||
|
||||
if location:
|
||||
try:
|
||||
store = storage_registry or deps.get_store()
|
||||
resolved_backend = Add_File._resolve_backend_by_name(store, str(location))
|
||||
backend_registry = storage_registry or deps.get_backend_registry()
|
||||
resolved_backend = Add_File._resolve_backend_by_name(backend_registry, str(location))
|
||||
if resolved_backend is not None:
|
||||
code = self._handle_storage_backend(
|
||||
item,
|
||||
@@ -845,7 +845,7 @@ class Add_File(Cmdlet):
|
||||
hash_values: List[str],
|
||||
config: Dict[str,
|
||||
Any],
|
||||
store_instance: Optional[Store] = None,
|
||||
store_instance: Optional[BackendRegistry] = None,
|
||||
) -> Optional[List[Any]]:
|
||||
"""Run search-file for a list of hashes and promote the table to a display overlay.
|
||||
|
||||
@@ -1053,7 +1053,7 @@ class Add_File(Cmdlet):
|
||||
str]]],
|
||||
config: Dict[str,
|
||||
Any],
|
||||
store_instance: Optional[Store] = None,
|
||||
store_instance: Optional[BackendRegistry] = None,
|
||||
deps: Optional[_CommandDependencies] = None,
|
||||
) -> None:
|
||||
"""Persist relationships to backends that support relationships.
|
||||
@@ -1067,7 +1067,7 @@ class Add_File(Cmdlet):
|
||||
deps = _CommandDependencies(config)
|
||||
|
||||
try:
|
||||
store = store_instance if store_instance is not None else deps.get_store()
|
||||
backend_registry = store_instance if store_instance is not None else deps.get_backend_registry()
|
||||
except Exception:
|
||||
return
|
||||
|
||||
@@ -1076,7 +1076,7 @@ class Add_File(Cmdlet):
|
||||
continue
|
||||
|
||||
try:
|
||||
backend = store[str(backend_name)]
|
||||
backend = backend_registry[str(backend_name)]
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
@@ -1356,9 +1356,9 @@ class Add_File(Cmdlet):
|
||||
try:
|
||||
if deps is None:
|
||||
deps = _CommandDependencies(config)
|
||||
store = store_instance or deps.get_store()
|
||||
|
||||
backend = Add_File._resolve_backend_by_name(store, r_store)
|
||||
backend_registry = store_instance or deps.get_backend_registry()
|
||||
|
||||
backend = Add_File._resolve_backend_by_name(backend_registry, r_store)
|
||||
if backend is not None:
|
||||
mp = backend.get_file(r_hash)
|
||||
if isinstance(mp, Path) and mp.exists():
|
||||
@@ -1497,14 +1497,14 @@ class Add_File(Cmdlet):
|
||||
|
||||
explicit_instance = str(instance_name or "").strip() or None
|
||||
try:
|
||||
storage = store_instance if store_instance is not None else Store(config)
|
||||
backend_registry = store_instance if store_instance is not None else BackendRegistry(config)
|
||||
except Exception:
|
||||
storage = None
|
||||
backend_registry = None
|
||||
|
||||
try:
|
||||
resolved_name, backend = resolver(
|
||||
explicit_instance,
|
||||
storage=storage,
|
||||
storage=backend_registry,
|
||||
require_explicit=bool(explicit_instance),
|
||||
)
|
||||
except TypeError:
|
||||
@@ -1622,8 +1622,8 @@ class Add_File(Cmdlet):
|
||||
if deps is None:
|
||||
deps = _CommandDependencies(config)
|
||||
|
||||
store = store_instance or deps.get_store()
|
||||
backend = Add_File._resolve_backend_by_name(store, r_store) if store is not None else None
|
||||
backend_registry = store_instance or deps.get_backend_registry()
|
||||
backend = Add_File._resolve_backend_by_name(backend_registry, r_store) if backend_registry is not None else None
|
||||
if backend is None:
|
||||
return None, None, None
|
||||
|
||||
@@ -2475,10 +2475,23 @@ class Add_File(Cmdlet):
|
||||
List[str]]]]] = None,
|
||||
suppress_last_stage_overlay: bool = False,
|
||||
auto_search_file: bool = True,
|
||||
store_instance: Optional[Store] = None,
|
||||
store_instance: Optional[BackendRegistry] = None,
|
||||
) -> int:
|
||||
"""Handle uploading to a registered storage backend (e.g., 'test' folder store, 'hydrus', etc.)."""
|
||||
##log(f"Adding file to storage backend '{backend_name}': {media_path.name}", file=sys.stderr)
|
||||
pipeline_progress = PipelineProgress(ctx)
|
||||
|
||||
def _set_status(text: str) -> None:
|
||||
try:
|
||||
pipeline_progress.set_status(f"{backend_name}: {text}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _clear_status() -> None:
|
||||
try:
|
||||
pipeline_progress.clear_status()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
delete_after_effective = bool(delete_after)
|
||||
# ... (lines omitted for brevity but I need to keep them contextually correct)
|
||||
@@ -2510,11 +2523,11 @@ class Add_File(Cmdlet):
|
||||
pass
|
||||
|
||||
try:
|
||||
store = store_instance if store_instance is not None else Store(config)
|
||||
backend, store, backend_exc = sh.get_preferred_store_backend(
|
||||
backend_registry = store_instance if store_instance is not None else BackendRegistry(config)
|
||||
backend, backend_registry, backend_exc = sh.get_preferred_store_backend(
|
||||
config,
|
||||
backend_name,
|
||||
store_registry=store,
|
||||
store_registry=backend_registry,
|
||||
suppress_debug=True,
|
||||
)
|
||||
if backend is None:
|
||||
@@ -2654,6 +2667,8 @@ class Add_File(Cmdlet):
|
||||
tag=upload_tags,
|
||||
url=[] if ((defer_url_association and url) or (not supports_url_association)) else url,
|
||||
file_hash=f_hash,
|
||||
pipeline_progress=pipeline_progress,
|
||||
transfer_label=title or media_path.name,
|
||||
)
|
||||
##log(f"✓ File added to '{backend_name}': {file_identifier}", file=sys.stderr)
|
||||
|
||||
@@ -2704,6 +2719,7 @@ class Add_File(Cmdlet):
|
||||
try:
|
||||
adder = getattr(backend, "add_tag", None)
|
||||
if callable(adder):
|
||||
_set_status("applying deferred tags")
|
||||
adder(resolved_hash, list(tags))
|
||||
except Exception as exc:
|
||||
log(f"[add-file] Post-upload tagging failed for {backend_name}: {exc}", file=sys.stderr)
|
||||
@@ -2724,88 +2740,43 @@ class Add_File(Cmdlet):
|
||||
try:
|
||||
# Folder.add_file already persists URLs, avoid extra DB traffic here.
|
||||
if not is_folder_backend:
|
||||
_set_status("associating urls")
|
||||
backend.add_url(resolved_hash, list(url))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If a subtitle note was provided upstream (e.g., download-media writes notes.sub),
|
||||
# persist it automatically like add-note would.
|
||||
sub_note = Add_File._get_note_text(result, pipe_obj, "sub")
|
||||
if sub_note and supports_note_association:
|
||||
def _write_note(note_name: str, note_text: Optional[str]) -> None:
|
||||
if not note_text or not supports_note_association:
|
||||
return
|
||||
try:
|
||||
setter = getattr(backend, "set_note", None)
|
||||
if callable(setter):
|
||||
setter(resolved_hash, "sub", sub_note)
|
||||
_set_status(f"writing {note_name} note")
|
||||
setter(resolved_hash, note_name, note_text)
|
||||
except Exception as exc:
|
||||
debug_panel(
|
||||
"add-file note write failed",
|
||||
[
|
||||
("store", backend_name),
|
||||
("hash", resolved_hash),
|
||||
("note", "sub"),
|
||||
("note", note_name),
|
||||
("error", exc),
|
||||
],
|
||||
border_style="yellow",
|
||||
)
|
||||
|
||||
lyric_note = Add_File._get_note_text(result, pipe_obj, "lyric")
|
||||
if lyric_note and supports_note_association:
|
||||
try:
|
||||
setter = getattr(backend, "set_note", None)
|
||||
if callable(setter):
|
||||
setter(resolved_hash, "lyric", lyric_note)
|
||||
except Exception as exc:
|
||||
debug_panel(
|
||||
"add-file note write failed",
|
||||
[
|
||||
("store", backend_name),
|
||||
("hash", resolved_hash),
|
||||
("note", "lyric"),
|
||||
("error", exc),
|
||||
],
|
||||
border_style="yellow",
|
||||
)
|
||||
|
||||
chapters_note = Add_File._get_note_text(result, pipe_obj, "chapters")
|
||||
if chapters_note and supports_note_association:
|
||||
try:
|
||||
setter = getattr(backend, "set_note", None)
|
||||
if callable(setter):
|
||||
setter(resolved_hash, "chapters", chapters_note)
|
||||
except Exception as exc:
|
||||
debug_panel(
|
||||
"add-file note write failed",
|
||||
[
|
||||
("store", backend_name),
|
||||
("hash", resolved_hash),
|
||||
("note", "chapters"),
|
||||
("error", exc),
|
||||
],
|
||||
border_style="yellow",
|
||||
)
|
||||
|
||||
caption_note = Add_File._get_note_text(result, pipe_obj, "caption")
|
||||
if caption_note and supports_note_association:
|
||||
try:
|
||||
setter = getattr(backend, "set_note", None)
|
||||
if callable(setter):
|
||||
setter(resolved_hash, "caption", caption_note)
|
||||
except Exception as exc:
|
||||
debug_panel(
|
||||
"add-file note write failed",
|
||||
[
|
||||
("store", backend_name),
|
||||
("hash", resolved_hash),
|
||||
("note", "caption"),
|
||||
("error", exc),
|
||||
],
|
||||
border_style="yellow",
|
||||
)
|
||||
_write_note("sub", Add_File._get_note_text(result, pipe_obj, "sub"))
|
||||
_write_note("lyric", Add_File._get_note_text(result, pipe_obj, "lyric"))
|
||||
_write_note("chapters", Add_File._get_note_text(result, pipe_obj, "chapters"))
|
||||
_write_note("caption", Add_File._get_note_text(result, pipe_obj, "caption"))
|
||||
|
||||
meta: Dict[str,
|
||||
Any] = {}
|
||||
try:
|
||||
if not is_folder_backend:
|
||||
_set_status("loading stored metadata")
|
||||
meta = backend.get_metadata(resolved_hash) or {}
|
||||
except Exception:
|
||||
meta = {}
|
||||
@@ -2886,9 +2857,11 @@ class Add_File(Cmdlet):
|
||||
media_path,
|
||||
delete_source=delete_after_effective
|
||||
)
|
||||
_clear_status()
|
||||
return 0
|
||||
|
||||
except Exception as exc:
|
||||
_clear_status()
|
||||
log(
|
||||
f"❌ Failed to add file to backend '{backend_name}': {exc}",
|
||||
file=sys.stderr
|
||||
@@ -2907,12 +2880,12 @@ class Add_File(Cmdlet):
|
||||
List[str]]]],
|
||||
config: Dict[str,
|
||||
Any],
|
||||
store_instance: Optional[Store] = None,
|
||||
store_instance: Optional[BackendRegistry] = None,
|
||||
) -> None:
|
||||
"""Apply deferred URL associations in bulk, grouped per backend."""
|
||||
|
||||
try:
|
||||
store = store_instance if store_instance is not None else Store(config)
|
||||
backend_registry = store_instance if store_instance is not None else BackendRegistry(config)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
@@ -2920,10 +2893,10 @@ class Add_File(Cmdlet):
|
||||
if not pairs:
|
||||
continue
|
||||
try:
|
||||
backend, store, _exc = sh.get_store_backend(
|
||||
backend, backend_registry, _exc = sh.get_store_backend(
|
||||
config,
|
||||
backend_name,
|
||||
store_registry=store,
|
||||
store_registry=backend_registry,
|
||||
)
|
||||
if backend is None:
|
||||
continue
|
||||
@@ -2960,12 +2933,12 @@ class Add_File(Cmdlet):
|
||||
List[str]]]],
|
||||
config: Dict[str,
|
||||
Any],
|
||||
store_instance: Optional[Store] = None,
|
||||
store_instance: Optional[BackendRegistry] = None,
|
||||
) -> None:
|
||||
"""Apply deferred tag associations in bulk, grouped per backend."""
|
||||
|
||||
try:
|
||||
store = store_instance if store_instance is not None else Store(config)
|
||||
backend_registry = store_instance if store_instance is not None else BackendRegistry(config)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
@@ -2974,7 +2947,7 @@ class Add_File(Cmdlet):
|
||||
pending or {},
|
||||
bulk_method_name="add_tags_bulk",
|
||||
single_method_name="add_tag",
|
||||
store_registry=store,
|
||||
store_registry=backend_registry,
|
||||
pass_config_to_bulk=False,
|
||||
pass_config_to_single=False,
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ from pathlib import Path
|
||||
|
||||
from SYS.logger import debug, log
|
||||
from PluginCore.registry import get_plugin
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
from .. import _shared as sh
|
||||
from SYS import pipeline as ctx
|
||||
from SYS.result_table_helpers import add_row_columns
|
||||
@@ -156,7 +156,7 @@ class Delete_File(sh.Cmdlet):
|
||||
backend = None
|
||||
try:
|
||||
if instance:
|
||||
registry = Store(config)
|
||||
registry = BackendRegistry(config)
|
||||
if registry.is_available(str(store)):
|
||||
backend = registry[str(store)]
|
||||
except Exception:
|
||||
@@ -242,7 +242,7 @@ class Delete_File(sh.Cmdlet):
|
||||
try:
|
||||
# Re-use an already resolved backend when available.
|
||||
if backend is None:
|
||||
registry = Store(config)
|
||||
registry = BackendRegistry(config)
|
||||
if registry.is_available(str(store)):
|
||||
backend = registry[str(store)]
|
||||
|
||||
|
||||
@@ -1661,7 +1661,7 @@ class Download_File(Cmdlet):
|
||||
"""Initialize store registry and determine whether a Hydrus backend is usable."""
|
||||
storage = None
|
||||
try:
|
||||
from Store import Store as _Store
|
||||
from PluginCore.backend_registry import BackendRegistry as _Store
|
||||
|
||||
storage = _Store(config)
|
||||
except Exception:
|
||||
|
||||
+12
-12
@@ -1865,8 +1865,8 @@ class search_file(Cmdlet):
|
||||
|
||||
worker_id = str(uuid.uuid4())
|
||||
|
||||
from Store import Store
|
||||
storage_registry = Store(config=config or {})
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
storage_registry = BackendRegistry(config=config or {})
|
||||
|
||||
if not storage_registry.list_backends():
|
||||
# Internal refreshes should not trigger config panels or stop progress.
|
||||
@@ -1911,8 +1911,8 @@ class search_file(Cmdlet):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from Store.registry import list_configured_backend_names, get_backend_instance
|
||||
from Store._base import Store as BaseStore
|
||||
from PluginCore.backend_registry import list_configured_backend_names, get_backend_instance
|
||||
from PluginCore.backend_base import BackendBase
|
||||
|
||||
backend_to_search = storage_backend or None
|
||||
|
||||
@@ -1929,13 +1929,13 @@ class search_file(Cmdlet):
|
||||
for h in hash_query:
|
||||
resolved_backend_name: Optional[str] = None
|
||||
resolved_backend = None
|
||||
store_registry = None
|
||||
backend_registry_cache = None
|
||||
|
||||
for backend_name in backends_to_try:
|
||||
backend, store_registry, _exc = get_preferred_store_backend(
|
||||
backend, backend_registry_cache, _exc = get_preferred_store_backend(
|
||||
config,
|
||||
backend_name,
|
||||
store_registry=store_registry,
|
||||
store_registry=backend_registry_cache,
|
||||
suppress_debug=True,
|
||||
)
|
||||
if backend is None:
|
||||
@@ -2128,7 +2128,7 @@ class search_file(Cmdlet):
|
||||
db.update_worker_status(worker_id, "error")
|
||||
return 1
|
||||
|
||||
if type(target_backend).search is BaseStore.search:
|
||||
if type(target_backend).search is BackendBase.search:
|
||||
log(
|
||||
f"Backend '{backend_to_search}' does not support searching",
|
||||
file=sys.stderr,
|
||||
@@ -2138,13 +2138,13 @@ class search_file(Cmdlet):
|
||||
results = target_backend.search(query, limit=limit)
|
||||
else:
|
||||
all_results = []
|
||||
store_registry = None
|
||||
backend_registry_cache = None
|
||||
for backend_name in list_configured_backend_names(config or {}):
|
||||
try:
|
||||
backend, store_registry, _exc = get_preferred_store_backend(
|
||||
backend, backend_registry_cache, _exc = get_preferred_store_backend(
|
||||
config,
|
||||
backend_name,
|
||||
store_registry=store_registry,
|
||||
store_registry=backend_registry_cache,
|
||||
suppress_debug=True,
|
||||
)
|
||||
if backend is None:
|
||||
@@ -2154,7 +2154,7 @@ class search_file(Cmdlet):
|
||||
|
||||
searched_backends.append(backend_name)
|
||||
|
||||
if type(backend).search is BaseStore.search:
|
||||
if type(backend).search is BackendBase.search:
|
||||
continue
|
||||
|
||||
backend_results = backend.search(
|
||||
|
||||
+3
-2
@@ -14,6 +14,7 @@ from urllib.parse import urlparse
|
||||
from SYS.logger import log, debug
|
||||
from SYS.item_accessors import get_store_name
|
||||
from SYS.utils import sha256_file
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
from .. import _shared as sh
|
||||
|
||||
Cmdlet = sh.Cmdlet
|
||||
@@ -166,8 +167,8 @@ def _persist_alt_relationship(
|
||||
) -> None:
|
||||
"""Persist directional alt -> king relationship in the given backend."""
|
||||
try:
|
||||
store = Store(config)
|
||||
backend: Any = store[str(store_name)]
|
||||
backend_registry = BackendRegistry(config)
|
||||
backend: Any = backend_registry[str(store_name)]
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
+68
-25
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Sequence, Optional
|
||||
from typing import Any, Dict, Sequence, Optional, List
|
||||
import json
|
||||
import sys
|
||||
|
||||
@@ -235,6 +235,50 @@ class Get_Metadata(Cmdlet):
|
||||
columns_to_add.append(("Duration(s)", ""))
|
||||
add_row_columns(table, columns_to_add)
|
||||
|
||||
@staticmethod
|
||||
def _extract_metadata_tags(metadata: Dict[str, Any]) -> List[str]:
|
||||
tags: List[str] = []
|
||||
|
||||
def _append(tag_value: Any) -> None:
|
||||
text = str(tag_value or "").strip()
|
||||
if text and text not in tags:
|
||||
tags.append(text)
|
||||
|
||||
def _walk_tag_values(value: Any) -> None:
|
||||
if isinstance(value, str):
|
||||
_append(value)
|
||||
return
|
||||
if isinstance(value, dict):
|
||||
for nested_value in value.values():
|
||||
_walk_tag_values(nested_value)
|
||||
return
|
||||
if isinstance(value, (list, tuple, set)):
|
||||
for nested_value in value:
|
||||
_walk_tag_values(nested_value)
|
||||
|
||||
raw_tags = metadata.get("tags")
|
||||
if isinstance(raw_tags, dict):
|
||||
for service_data in raw_tags.values():
|
||||
if not isinstance(service_data, dict):
|
||||
continue
|
||||
matched_tag_key = False
|
||||
for key, tag_mapping in service_data.items():
|
||||
if "tag" not in str(key).strip().lower():
|
||||
continue
|
||||
matched_tag_key = True
|
||||
_walk_tag_values(tag_mapping)
|
||||
if not matched_tag_key:
|
||||
_walk_tag_values(service_data)
|
||||
elif isinstance(raw_tags, list):
|
||||
for tag_value in raw_tags:
|
||||
_append(tag_value)
|
||||
|
||||
for key in ("tags_flat", "tag"):
|
||||
raw_value = metadata.get(key)
|
||||
_walk_tag_values(raw_value)
|
||||
|
||||
return tags
|
||||
|
||||
def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
"""Execute get-metadata cmdlet - retrieve and display file metadata.
|
||||
|
||||
@@ -309,30 +353,29 @@ class Get_Metadata(Cmdlet):
|
||||
else:
|
||||
item_tags = [str(t) for t in item_tags]
|
||||
|
||||
# Extract tags from metadata response instead of making a separate get_tag() request
|
||||
# This prevents duplicate API calls to Hydrus (metadata already includes tags)
|
||||
metadata_tags = metadata.get("tags")
|
||||
if isinstance(metadata_tags, dict):
|
||||
# metadata["tags"] is {service_key: {service_data}}
|
||||
for service_data in metadata_tags.values():
|
||||
if isinstance(service_data, dict):
|
||||
display_tags = service_data.get("display_tags", {})
|
||||
if isinstance(display_tags, dict):
|
||||
# display_tags is typically {status: tag_list}
|
||||
for tag_list in display_tags.values():
|
||||
if isinstance(tag_list, list):
|
||||
for t in tag_list:
|
||||
ts = str(t) if t else ""
|
||||
if ts and ts not in item_tags:
|
||||
item_tags.append(ts)
|
||||
# Check for title tag
|
||||
if not get_field(result, "title") and ts.lower().startswith("title:"):
|
||||
parts = ts.split(":", 1)
|
||||
if len(parts) > 1:
|
||||
title = parts[1].strip()
|
||||
break # Only use first status level
|
||||
if any(t for t in item_tags if str(t).lower().startswith("title:")):
|
||||
break # Found title tag, stop searching services
|
||||
metadata_tags = self._extract_metadata_tags(metadata)
|
||||
if not metadata_tags:
|
||||
get_tag = getattr(backend, "get_tag", None)
|
||||
if callable(get_tag):
|
||||
try:
|
||||
backend_tags, _source = get_tag(file_hash, config=config)
|
||||
metadata_tags = [
|
||||
str(tag) for tag in (backend_tags or [])
|
||||
if str(tag or "").strip()
|
||||
]
|
||||
except Exception:
|
||||
metadata_tags = []
|
||||
|
||||
for tag_value in metadata_tags:
|
||||
tag_text = str(tag_value or "").strip()
|
||||
if not tag_text:
|
||||
continue
|
||||
if tag_text not in item_tags:
|
||||
item_tags.append(tag_text)
|
||||
if not get_field(result, "title") and tag_text.lower().startswith("title:"):
|
||||
parts = tag_text.split(":", 1)
|
||||
if len(parts) > 1:
|
||||
title = parts[1].strip()
|
||||
|
||||
|
||||
# Extract metadata fields
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ from SYS.logger import log
|
||||
from SYS.payload_builders import build_file_result_payload
|
||||
from SYS.result_publication import publish_result_table
|
||||
from SYS.result_table import Table
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
from SYS import pipeline as ctx
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ class Get_Url(Cmdlet):
|
||||
MAX_RESULTS = 256
|
||||
|
||||
try:
|
||||
storage = Store(config)
|
||||
storage = BackendRegistry(config)
|
||||
store_names = storage.list_backends() if hasattr(storage,
|
||||
"list_backends") else []
|
||||
|
||||
@@ -508,7 +508,7 @@ class Get_Url(Cmdlet):
|
||||
|
||||
# Get backend and retrieve url
|
||||
try:
|
||||
storage = Store(config)
|
||||
storage = BackendRegistry(config)
|
||||
backend = storage[store_name]
|
||||
|
||||
urls = backend.get_url(file_hash)
|
||||
|
||||
@@ -13,7 +13,6 @@ from SYS.result_publication import publish_result_table
|
||||
from SYS import models
|
||||
from SYS import pipeline as ctx
|
||||
from .. import _shared as sh
|
||||
from Store import Store # retained for test monkeypatch compatibility
|
||||
|
||||
normalize_result_input = sh.normalize_result_input
|
||||
filter_results_by_temp = sh.filter_results_by_temp
|
||||
@@ -31,6 +30,7 @@ should_show_help = sh.should_show_help
|
||||
get_field = sh.get_field
|
||||
|
||||
_FIELD_NAME_RE = re.compile(r"^[A-Za-z0-9_]+$")
|
||||
_DETAIL_PANEL_LIMIT = 9
|
||||
|
||||
|
||||
def _normalize_title_for_extract(text: str) -> str:
|
||||
@@ -1191,7 +1191,13 @@ class Add_Tag(Cmdlet):
|
||||
subject = display_items[0] if len(display_items) == 1 else list(display_items)
|
||||
# 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)
|
||||
display_type = "item" if len(display_items) <= _DETAIL_PANEL_LIMIT else "custom"
|
||||
display_and_persist_items(
|
||||
list(display_items),
|
||||
title="Result",
|
||||
subject=subject,
|
||||
display_type=display_type,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ get_field = sh.get_field
|
||||
from SYS.logger import debug, log
|
||||
|
||||
|
||||
_DETAIL_PANEL_LIMIT = 9
|
||||
|
||||
|
||||
def _matches_target(
|
||||
item: Any,
|
||||
target_hash: str | None,
|
||||
@@ -57,15 +60,56 @@ def _set_result_tags(result: Any, tags: list[str]) -> None:
|
||||
normalized = list(tags or [])
|
||||
set_field(result, "tag", normalized)
|
||||
|
||||
def _update_tag_columns(columns: Any) -> Any:
|
||||
if not isinstance(columns, (list, tuple)):
|
||||
return columns
|
||||
|
||||
updated_columns = []
|
||||
changed = False
|
||||
tag_value = ", ".join(normalized)
|
||||
for column in columns:
|
||||
if isinstance(column, tuple) and len(column) == 2:
|
||||
label, existing_value = column
|
||||
if str(label).strip().lower() in {"tag", "tags"}:
|
||||
updated_columns.append((label, tag_value))
|
||||
changed = True
|
||||
else:
|
||||
updated_columns.append((label, existing_value))
|
||||
elif isinstance(column, list) and len(column) == 2:
|
||||
label, existing_value = column
|
||||
if str(label).strip().lower() in {"tag", "tags"}:
|
||||
updated_columns.append([label, tag_value])
|
||||
changed = True
|
||||
else:
|
||||
updated_columns.append([label, existing_value])
|
||||
elif isinstance(column, dict):
|
||||
label = column.get("name") or column.get("label") or column.get("key")
|
||||
if str(label).strip().lower() in {"tag", "tags"}:
|
||||
updated_column = dict(column)
|
||||
updated_column["value"] = tag_value
|
||||
updated_columns.append(updated_column)
|
||||
changed = True
|
||||
else:
|
||||
updated_columns.append(column)
|
||||
else:
|
||||
updated_columns.append(column)
|
||||
if not changed:
|
||||
return columns
|
||||
if isinstance(columns, tuple):
|
||||
return tuple(updated_columns)
|
||||
return updated_columns
|
||||
|
||||
if isinstance(result, dict):
|
||||
result["tags_flat"] = list(normalized)
|
||||
if "tags" in result:
|
||||
result["tags"] = list(normalized)
|
||||
result["columns"] = _update_tag_columns(result.get("columns"))
|
||||
for container_name in ("extra", "metadata", "full_metadata"):
|
||||
container = result.get(container_name)
|
||||
if not isinstance(container, dict):
|
||||
continue
|
||||
if "tag" in container:
|
||||
container["tag"] = list(normalized)
|
||||
container["tag"] = list(normalized)
|
||||
container["tags_flat"] = list(normalized)
|
||||
if "tags" in container:
|
||||
container["tags"] = list(normalized)
|
||||
return
|
||||
@@ -74,12 +118,23 @@ def _set_result_tags(result: Any, tags: list[str]) -> None:
|
||||
setattr(result, "tags", list(normalized))
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
setattr(result, "tags_flat", list(normalized))
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
columns = getattr(result, "columns", None)
|
||||
updated_columns = _update_tag_columns(columns)
|
||||
if updated_columns is not columns:
|
||||
setattr(result, "columns", updated_columns)
|
||||
except Exception:
|
||||
pass
|
||||
for container_name in ("extra", "metadata", "full_metadata"):
|
||||
container = getattr(result, container_name, None)
|
||||
if not isinstance(container, dict):
|
||||
continue
|
||||
if "tag" in container:
|
||||
container["tag"] = list(normalized)
|
||||
container["tag"] = list(normalized)
|
||||
container["tags_flat"] = list(normalized)
|
||||
if "tags" in container:
|
||||
container["tags"] = list(normalized)
|
||||
|
||||
@@ -514,6 +569,11 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
|
||||
# Process each item
|
||||
success_count = 0
|
||||
stage_ctx = ctx.get_stage_context()
|
||||
is_last_stage = (stage_ctx is None) or bool(
|
||||
getattr(stage_ctx, "is_last_stage", False)
|
||||
)
|
||||
display_items: list[Any] = []
|
||||
|
||||
# If we have TagItems and no args, we are deleting the tags themselves
|
||||
# If we have Files (or other objects) and args, we are deleting tags FROM those files
|
||||
@@ -658,6 +718,8 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
if title_value:
|
||||
_apply_title_to_result(item, title_value)
|
||||
_refresh_result_table_tags(new_tags, h, store_str, path)
|
||||
if is_last_stage:
|
||||
display_items.append(item)
|
||||
try:
|
||||
ctx.emit(item)
|
||||
except Exception:
|
||||
@@ -667,12 +729,34 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
for item, item_hash, item_path, store_str in items_needing_individual:
|
||||
if _process_deletion(tags_arg, item_hash, item_path, store_str, config, result=item):
|
||||
success_count += 1
|
||||
if is_last_stage:
|
||||
display_items.append(item)
|
||||
try:
|
||||
ctx.emit(item)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if success_count > 0:
|
||||
if is_last_stage and display_items:
|
||||
try:
|
||||
subject = display_items[0] if len(display_items) == 1 else list(display_items)
|
||||
from ._shared import display_and_persist_items
|
||||
|
||||
display_type = "item" if len(display_items) <= _DETAIL_PANEL_LIMIT else "custom"
|
||||
display_and_persist_items(
|
||||
list(display_items),
|
||||
title="Result",
|
||||
subject=subject,
|
||||
display_type=display_type,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if stage_ctx is not None:
|
||||
stage_ctx.emits = []
|
||||
except Exception:
|
||||
pass
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
@@ -426,9 +426,9 @@ def _run_impl(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
backend = None
|
||||
if is_store_backed:
|
||||
try:
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
storage = Store(config, suppress_debug=True)
|
||||
storage = BackendRegistry(config, suppress_debug=True)
|
||||
backend = storage[str(store_name)]
|
||||
except Exception:
|
||||
backend = None
|
||||
@@ -445,9 +445,9 @@ def _run_impl(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
store_for_scrape = get_field(result, "store", None)
|
||||
if file_hash_for_scrape and store_for_scrape:
|
||||
try:
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
storage = Store(config, suppress_debug=True)
|
||||
storage = BackendRegistry(config, suppress_debug=True)
|
||||
backend = storage[str(store_for_scrape)]
|
||||
current_tags, _src = backend.get_tag(file_hash_for_scrape, config=config)
|
||||
if isinstance(current_tags, (list, tuple, set)) and current_tags:
|
||||
@@ -764,9 +764,9 @@ def _run_impl(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
)
|
||||
return 0
|
||||
try:
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
storage = Store(config, suppress_debug=True)
|
||||
storage = BackendRegistry(config, suppress_debug=True)
|
||||
backend = storage[str(store_name)]
|
||||
ok = bool(backend.add_tag(file_hash, apply_tags, config=config))
|
||||
if not ok:
|
||||
@@ -896,9 +896,9 @@ def _run_impl(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
|
||||
# Get tags using storage backend
|
||||
try:
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
storage = Store(config, suppress_debug=True)
|
||||
storage = BackendRegistry(config, suppress_debug=True)
|
||||
backend = storage[store_name]
|
||||
current, source = backend.get_tag(file_hash, config=config)
|
||||
current = list(current or [])
|
||||
|
||||
@@ -6,7 +6,7 @@ import sys
|
||||
from SYS import pipeline as ctx
|
||||
from .. import _shared as sh
|
||||
from SYS.logger import log
|
||||
from Store import Store
|
||||
from PluginCore.backend_registry import BackendRegistry
|
||||
|
||||
|
||||
class Add_Url(sh.Cmdlet):
|
||||
@@ -117,7 +117,7 @@ class Add_Url(sh.Cmdlet):
|
||||
|
||||
# Get backend and add url
|
||||
try:
|
||||
storage = Store(config)
|
||||
storage = BackendRegistry(config)
|
||||
|
||||
# Build batches per store.
|
||||
store_override = parsed.get("instance")
|
||||
|
||||
Reference in New Issue
Block a user