update local and mpv plugins, add file cmdlet, update docs
This commit is contained in:
+187
-268
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional, Sequence, Tuple, List
|
||||
from pathlib import Path
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
@@ -11,7 +10,7 @@ from urllib.parse import urlparse
|
||||
|
||||
from SYS import models
|
||||
from SYS import pipeline as ctx
|
||||
from SYS.logger import log, debug, debug_panel, is_debug_enabled
|
||||
from SYS.logger import log, debug, debug_panel
|
||||
from SYS.payload_builders import build_table_result_payload
|
||||
from SYS.pipeline_progress import PipelineProgress
|
||||
from SYS.result_publication import overlay_existing_result_table, publish_result_table
|
||||
@@ -92,41 +91,11 @@ class _CommandDependencies:
|
||||
self._plugins[cache_key] = plugin
|
||||
return plugin
|
||||
|
||||
DEBUG_PIPE_NOTE_PREVIEW_LENGTH = 256
|
||||
|
||||
# Protocol schemes that identify a remote resource / not a local file path.
|
||||
# Used by multiple methods in this file to guard against URL strings being
|
||||
# treated as local file paths.
|
||||
_REMOTE_URL_PREFIXES: tuple[str, ...] = (
|
||||
"http://", "https://", "ftp://", "ftps://", "magnet:", "torrent:", "tidal:", "hydrus:",
|
||||
)
|
||||
|
||||
|
||||
def _truncate_debug_note_text(value: Any) -> str:
|
||||
raw = str(value or "")
|
||||
if len(raw) <= DEBUG_PIPE_NOTE_PREVIEW_LENGTH:
|
||||
return raw
|
||||
return raw[:DEBUG_PIPE_NOTE_PREVIEW_LENGTH].rstrip() + "..."
|
||||
|
||||
|
||||
def _sanitize_pipe_object_for_debug(pipe_obj: models.PipeObject) -> models.PipeObject:
|
||||
safe_po = deepcopy(pipe_obj)
|
||||
try:
|
||||
extra = safe_po.extra
|
||||
if isinstance(extra, dict):
|
||||
sanitized = dict(extra)
|
||||
notes = sanitized.get("notes")
|
||||
if isinstance(notes, dict):
|
||||
truncated_notes: Dict[str, str] = {}
|
||||
for note_name, note_value in notes.items():
|
||||
truncated_notes[str(note_name)] = _truncate_debug_note_text(note_value)
|
||||
sanitized["notes"] = truncated_notes
|
||||
safe_po.extra = sanitized
|
||||
except Exception:
|
||||
pass
|
||||
return safe_po
|
||||
|
||||
|
||||
def _maybe_apply_florencevision_tags(
|
||||
media_path: Path,
|
||||
tags: List[str],
|
||||
@@ -224,9 +193,9 @@ class Add_File(Cmdlet):
|
||||
super().__init__(
|
||||
name="add-file",
|
||||
summary=
|
||||
"Ingest a local media file to a configured instance, upload plugin, or local directory.",
|
||||
"Ingest a local media file to a configured store or plugin destination.",
|
||||
usage=
|
||||
"add-file (-path <filepath> | <piped>) (-instance <name|path> | -plugin <upload-plugin>) [-delete]",
|
||||
"add-file (-path <filepath> | <piped>) (-instance <store-name> | -plugin <plugin> [-instance <name|path>]) [-delete]",
|
||||
arg=[
|
||||
SharedArgs.PATH,
|
||||
SharedArgs.INSTANCE,
|
||||
@@ -242,11 +211,10 @@ class Add_File(Cmdlet):
|
||||
],
|
||||
detail=[
|
||||
"Note: add-file ingests local files. To fetch remote sources, use download-file and pipe into add-file.",
|
||||
"- Instance/location options (use -instance):",
|
||||
"- Store options (use -instance without -plugin):",
|
||||
" hydrus: Upload to Hydrus database with metadata tagging",
|
||||
" local: Copy file to local directory",
|
||||
" <path>: Copy file to specified directory",
|
||||
"- Upload plugin options (use -plugin):",
|
||||
"- Plugin options (use -plugin):",
|
||||
" local: Copy file to a configured local destination or direct path via -instance",
|
||||
" 0x0: Upload to 0x0.st for temporary hosting",
|
||||
" file.io: Upload to file.io for temporary hosting",
|
||||
" internetarchive: Upload to archive.org (optional tag: ia:<identifier> to upload into an existing item)",
|
||||
@@ -254,6 +222,7 @@ class Add_File(Cmdlet):
|
||||
],
|
||||
examples=[
|
||||
'download-file "https://themathesontrust.org/papers/christianity/alcock-alphabet1.pdf" | add-file -instance tutorial',
|
||||
'@1 | add-file -plugin local -instance C:\\Users\\Me\\Downloads',
|
||||
'add-file -plugin ftp -instance archive -path C:\\Media\\report.pdf',
|
||||
],
|
||||
exec=self.run,
|
||||
@@ -275,25 +244,19 @@ class Add_File(Cmdlet):
|
||||
source_url_arg = parsed.get("url")
|
||||
plugin_name = parsed.get("plugin")
|
||||
delete_after = parsed.get("delete", False)
|
||||
local_export_destination: Optional[str] = None
|
||||
if plugin_name and not plugin_instance and location:
|
||||
plugin_instance = location
|
||||
|
||||
# Convenience: when piping a file into add-file, allow `-path <existing dir>`
|
||||
# to act as the destination export directory.
|
||||
# Example: screen-shot "https://..." | add-file -path "C:\Users\Admin\Desktop"
|
||||
# Backward-compatible shorthand: when piping a file into add-file, allow
|
||||
# `-path <existing dir>` to normalize into the local export plugin path.
|
||||
if path_arg and not location and not plugin_name:
|
||||
try:
|
||||
candidate_dir = Path(str(path_arg))
|
||||
if candidate_dir.exists() and candidate_dir.is_dir():
|
||||
debug_panel(
|
||||
"add-file destination",
|
||||
[
|
||||
("mode", "local export"),
|
||||
("path", candidate_dir),
|
||||
],
|
||||
border_style="cyan",
|
||||
)
|
||||
location = str(candidate_dir)
|
||||
plugin_name = "local"
|
||||
plugin_instance = str(candidate_dir)
|
||||
local_export_destination = str(candidate_dir)
|
||||
path_arg = None
|
||||
except Exception:
|
||||
pass
|
||||
@@ -397,13 +360,44 @@ class Add_File(Cmdlet):
|
||||
is_storage_backend_location = False
|
||||
|
||||
if location and not plugin_name and not is_storage_backend_location:
|
||||
if not Add_File._looks_like_local_export_target(str(location)):
|
||||
resolved_local_instance, resolved_local_path = Add_File._resolve_local_export_plugin_target(
|
||||
location,
|
||||
config,
|
||||
deps=deps,
|
||||
require_explicit=True,
|
||||
)
|
||||
if resolved_local_path:
|
||||
plugin_name = "local"
|
||||
plugin_instance = resolved_local_instance or str(location)
|
||||
location = None
|
||||
local_export_destination = resolved_local_path
|
||||
else:
|
||||
log(
|
||||
f"Storage backend '{location}' not found. Use -path for local export or configure that store backend.",
|
||||
f"Storage backend '{location}' not found. Use -plugin local -instance <name|path> for local export or configure that store backend.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
normalized_plugin_name = Add_File._normalize_provider_key(plugin_name)
|
||||
if normalized_plugin_name == "local":
|
||||
resolved_local_instance, resolved_local_path = Add_File._resolve_local_export_plugin_target(
|
||||
plugin_instance or location,
|
||||
config,
|
||||
deps=deps,
|
||||
require_explicit=bool(plugin_instance or location),
|
||||
)
|
||||
if not resolved_local_path:
|
||||
requested_local = str(plugin_instance or location or "").strip() or "<default>"
|
||||
log(
|
||||
f"Local destination '{requested_local}' is not configured. Use -plugin local -instance <name|path>.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
plugin_name = "local"
|
||||
plugin_instance = resolved_local_instance or str(plugin_instance or location or "").strip() or None
|
||||
location = None
|
||||
local_export_destination = resolved_local_path
|
||||
|
||||
plugin_storage_backend = None
|
||||
if plugin_name:
|
||||
plugin_storage_backend = Add_File._resolve_plugin_storage_backend(
|
||||
@@ -469,46 +463,8 @@ class Add_File(Cmdlet):
|
||||
except Exception:
|
||||
use_steps = False
|
||||
|
||||
try:
|
||||
debug_panel(
|
||||
"add-file",
|
||||
[
|
||||
("result_type", type(result).__name__),
|
||||
("items", total_items),
|
||||
("location", location),
|
||||
("plugin", plugin_name),
|
||||
("instance", plugin_instance),
|
||||
("delete", delete_after),
|
||||
],
|
||||
border_style="cyan",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# add-file is ingestion-only: it does not download URLs here.
|
||||
|
||||
# Show a concise PipeObject preview when debug logging is enabled to aid pipeline troubleshooting.
|
||||
if is_debug_enabled():
|
||||
preview_items = (
|
||||
items_to_process if isinstance(items_to_process, list)
|
||||
else [items_to_process]
|
||||
)
|
||||
max_preview = 5
|
||||
for idx, item in enumerate(preview_items[:max_preview]):
|
||||
po = item if isinstance(item, models.PipeObject) else None
|
||||
if po is None:
|
||||
try:
|
||||
po = coerce_to_pipe_object(item, path_arg)
|
||||
except Exception:
|
||||
po = None
|
||||
if po is None:
|
||||
continue
|
||||
try:
|
||||
safe_po = _sanitize_pipe_object_for_debug(po)
|
||||
safe_po.debug_table()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
should_present_directory_selector = bool(dir_scan_mode and not has_downstream_stage)
|
||||
if dir_scan_mode and has_downstream_stage:
|
||||
debug(
|
||||
@@ -666,12 +622,19 @@ class Add_File(Cmdlet):
|
||||
if use_steps and steps_started:
|
||||
progress.step("resolving source")
|
||||
|
||||
export_destination = (
|
||||
Path(local_export_destination)
|
||||
if local_export_destination
|
||||
else Path(location)
|
||||
if location and not is_storage_backend_location
|
||||
else None
|
||||
)
|
||||
media_path, file_hash, temp_dir_to_cleanup = self._resolve_source(
|
||||
item,
|
||||
path_arg,
|
||||
pipe_obj,
|
||||
config,
|
||||
export_destination=(Path(location) if location and not is_storage_backend_location else None),
|
||||
export_destination=export_destination,
|
||||
store_instance=storage_registry,
|
||||
deps=deps,
|
||||
)
|
||||
@@ -679,19 +642,6 @@ class Add_File(Cmdlet):
|
||||
media_path, file_hash, temp_dir_to_cleanup = Add_File._download_piped_source(
|
||||
pipe_obj, config, storage_registry, deps=deps
|
||||
)
|
||||
if media_path:
|
||||
try:
|
||||
debug_panel(
|
||||
f"add-file source {idx}/{max(1, total_items)}",
|
||||
[
|
||||
("path", media_path),
|
||||
("hash", file_hash or "N/A"),
|
||||
("plugin", plugin_name or "local"),
|
||||
],
|
||||
border_style="green",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
if not media_path:
|
||||
failures += 1
|
||||
continue
|
||||
@@ -768,13 +718,8 @@ class Add_File(Cmdlet):
|
||||
store_instance=storage_registry,
|
||||
)
|
||||
else:
|
||||
code = self._handle_local_export(
|
||||
media_path,
|
||||
location,
|
||||
pipe_obj,
|
||||
config,
|
||||
delete_after_item
|
||||
)
|
||||
log(f"Invalid storage backend: {location}", file=sys.stderr)
|
||||
code = 1
|
||||
except Exception as exc:
|
||||
debug(f"[add-file] ERROR: Failed to resolve location: {exc}")
|
||||
log(f"Invalid location: {location}", file=sys.stderr)
|
||||
@@ -1371,27 +1316,6 @@ class Add_File(Cmdlet):
|
||||
pass
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _looks_like_local_export_target(location: str) -> bool:
|
||||
target = str(location or "").strip()
|
||||
if not target:
|
||||
return False
|
||||
|
||||
target_path = Path(target).expanduser()
|
||||
try:
|
||||
if target_path.exists():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if target.startswith((".", "~")):
|
||||
return True
|
||||
if "\\" in target or "/" in target:
|
||||
return True
|
||||
if len(target) >= 2 and target[1] == ":":
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _resolve_source(
|
||||
result: Any,
|
||||
@@ -1608,6 +1532,45 @@ class Add_File(Cmdlet):
|
||||
|
||||
return resolved_text
|
||||
|
||||
@staticmethod
|
||||
def _resolve_local_export_plugin_target(
|
||||
requested: Optional[Any],
|
||||
config: Dict[str, Any],
|
||||
*,
|
||||
deps: Optional[_CommandDependencies] = None,
|
||||
require_explicit: bool = False,
|
||||
) -> tuple[Optional[str], Optional[str]]:
|
||||
if deps is None:
|
||||
deps = _CommandDependencies(config)
|
||||
|
||||
file_provider = deps.get_plugin_with_capability("local", "upload")
|
||||
if file_provider is None:
|
||||
return None, None
|
||||
|
||||
resolver = getattr(file_provider, "resolve_destination", None)
|
||||
if not callable(resolver):
|
||||
return None, None
|
||||
|
||||
requested_text = str(requested or "").strip() or None
|
||||
try:
|
||||
resolved_name, settings = resolver(
|
||||
requested_text,
|
||||
require_explicit=require_explicit,
|
||||
)
|
||||
except TypeError:
|
||||
try:
|
||||
resolved_name, settings = resolver(requested_text)
|
||||
except Exception:
|
||||
return None, None
|
||||
except Exception:
|
||||
return None, None
|
||||
|
||||
path_value = str((settings or {}).get("path") or "").strip()
|
||||
if not path_value:
|
||||
return None, None
|
||||
resolved_text = str(resolved_name or requested_text or "").strip() or None
|
||||
return resolved_text, path_value
|
||||
|
||||
@staticmethod
|
||||
def _maybe_download_plugin_result(
|
||||
result: Any,
|
||||
@@ -2294,136 +2257,72 @@ class Add_File(Cmdlet):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _handle_local_export(
|
||||
media_path: Path,
|
||||
location: str,
|
||||
def _emit_plugin_upload_payload(
|
||||
upload_payload: Dict[str, Any],
|
||||
plugin_name: str,
|
||||
instance_name: Optional[str],
|
||||
pipe_obj: models.PipeObject,
|
||||
config: Dict[str,
|
||||
Any],
|
||||
media_path: Path,
|
||||
delete_after: bool,
|
||||
) -> int:
|
||||
"""Handle exporting to a specific local path (Copy)."""
|
||||
try:
|
||||
destination_root = Path(location)
|
||||
except Exception as exc:
|
||||
log(f"❌ Invalid destination path '{location}': {exc}", file=sys.stderr)
|
||||
return 1
|
||||
payload = dict(upload_payload or {})
|
||||
extra_updates: Dict[str, Any] = {}
|
||||
raw_extra = payload.get("extra")
|
||||
if isinstance(raw_extra, dict):
|
||||
extra_updates.update(raw_extra)
|
||||
|
||||
direct_export_download = False
|
||||
try:
|
||||
if isinstance(pipe_obj.extra, dict):
|
||||
direct_export_download = bool(pipe_obj.extra.pop("_direct_export_download", False))
|
||||
except Exception:
|
||||
direct_export_download = False
|
||||
if plugin_name:
|
||||
extra_updates.setdefault("plugin", plugin_name)
|
||||
if instance_name:
|
||||
extra_updates.setdefault("instance", instance_name)
|
||||
|
||||
try:
|
||||
debug_panel(
|
||||
"add-file export",
|
||||
[
|
||||
("destination", destination_root),
|
||||
("source", media_path),
|
||||
],
|
||||
border_style="green",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
raw_urls = payload.get("url")
|
||||
if isinstance(raw_urls, str):
|
||||
url_values = [raw_urls.strip()] if raw_urls.strip() else []
|
||||
extra_updates["url"] = url_values
|
||||
elif isinstance(raw_urls, (list, tuple, set)):
|
||||
url_values = [str(item).strip() for item in raw_urls if str(item).strip()]
|
||||
extra_updates["url"] = url_values
|
||||
|
||||
result = None
|
||||
tags, url, title, f_hash = Add_File._prepare_metadata(result, media_path, pipe_obj, config)
|
||||
|
||||
# Determine Filename (Title-based)
|
||||
title_value = title
|
||||
if not title_value:
|
||||
# Try to find title in tags
|
||||
title_tag = next(
|
||||
(t for t in tags if str(t).strip().lower().startswith("title:")),
|
||||
None
|
||||
)
|
||||
if title_tag:
|
||||
title_value = title_tag.split(":", 1)[1].strip()
|
||||
|
||||
if not title_value:
|
||||
title_value = media_path.stem.replace("_", " ").strip()
|
||||
|
||||
safe_title = "".join(
|
||||
c for c in title_value if c.isalnum() or c in " ._-()[]{}'`"
|
||||
).strip()
|
||||
base_name = safe_title or media_path.stem
|
||||
|
||||
# Fix to prevent double extensions (e.g., file.exe.exe)
|
||||
# If the base name already ends with the extension of the media file,
|
||||
# don't append it again.
|
||||
file_ext = media_path.suffix
|
||||
if file_ext and base_name.lower().endswith(file_ext.lower()):
|
||||
new_name = base_name
|
||||
else:
|
||||
new_name = base_name + file_ext
|
||||
|
||||
destination_root.mkdir(parents=True, exist_ok=True)
|
||||
target_path = destination_root / new_name
|
||||
|
||||
if direct_export_download:
|
||||
target_path = media_path
|
||||
else:
|
||||
if target_path.exists():
|
||||
target_path = unique_path(target_path)
|
||||
|
||||
# COPY Operation (Safe Export)
|
||||
try:
|
||||
shutil.copy2(str(media_path), target_path)
|
||||
except Exception as exc:
|
||||
log(f"❌ Failed to export file: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Copy Sidecars
|
||||
Add_File._copy_sidecars(media_path, target_path)
|
||||
|
||||
# Ensure hash for exported copy
|
||||
if not f_hash:
|
||||
try:
|
||||
f_hash = sha256_file(target_path)
|
||||
except Exception:
|
||||
f_hash = None
|
||||
|
||||
# Write Metadata Sidecars (since it's an export)
|
||||
relationships = Add_File._get_relationships(result, pipe_obj)
|
||||
try:
|
||||
write_sidecar(target_path, tags, url, f_hash)
|
||||
from SYS.metadata import write_metadata # lazy: avoids 1000+ module chain at startup
|
||||
write_metadata(
|
||||
target_path,
|
||||
hash_value=f_hash,
|
||||
url=url,
|
||||
relationships=relationships or []
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Update PipeObject and emit
|
||||
extra_updates = {
|
||||
"url": url,
|
||||
"export_path": str(destination_root),
|
||||
}
|
||||
relationships = payload.get("relationships")
|
||||
if relationships:
|
||||
extra_updates["relationships"] = relationships
|
||||
try:
|
||||
pipe_obj.relationships = relationships
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
chosen_title = title or title_value or pipe_obj.title or target_path.name
|
||||
tags = payload.get("tag")
|
||||
if isinstance(tags, list):
|
||||
tag_values = [str(tag) for tag in tags]
|
||||
else:
|
||||
tag_values = list(pipe_obj.tag or [])
|
||||
|
||||
title_value = str(payload.get("title") or pipe_obj.title or media_path.name).strip() or media_path.name
|
||||
path_value = str(payload.get("path") or pipe_obj.path or media_path).strip()
|
||||
hash_value = str(
|
||||
payload.get("hash")
|
||||
or payload.get("file_hash")
|
||||
or getattr(pipe_obj, "hash", None)
|
||||
or "unknown"
|
||||
).strip() or "unknown"
|
||||
store_value = str(payload.get("store") or "").strip()
|
||||
provider_value = payload.get("provider")
|
||||
if provider_value is None and plugin_name:
|
||||
provider_value = plugin_name
|
||||
|
||||
Add_File._update_pipe_object_destination(
|
||||
pipe_obj,
|
||||
hash_value=f_hash or "unknown",
|
||||
store="local",
|
||||
path=str(target_path),
|
||||
tag=tags,
|
||||
title=chosen_title,
|
||||
hash_value=hash_value,
|
||||
store=store_value,
|
||||
provider=str(provider_value) if provider_value else None,
|
||||
path=path_value,
|
||||
tag=tag_values,
|
||||
title=title_value,
|
||||
extra_updates=extra_updates,
|
||||
)
|
||||
Add_File._emit_pipe_object(pipe_obj)
|
||||
|
||||
# Cleanup
|
||||
# Only delete if explicitly requested!
|
||||
Add_File._cleanup_after_success(media_path, delete_source=delete_after)
|
||||
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -2459,10 +2358,37 @@ class Add_File(Cmdlet):
|
||||
show_available_plugins_panel(sorted(available_uploads))
|
||||
return 1
|
||||
|
||||
hoster_url = file_provider.upload(
|
||||
upload_kwargs: Dict[str, Any] = {
|
||||
"pipe_obj": pipe_obj,
|
||||
"instance": instance_name,
|
||||
}
|
||||
normalized_plugin_name = Add_File._normalize_provider_key(plugin_name)
|
||||
f_hash = Add_File._resolve_file_hash(None, media_path, pipe_obj, None)
|
||||
if normalized_plugin_name == "local":
|
||||
result = None
|
||||
tags, urls, title, f_hash = Add_File._prepare_metadata(result, media_path, pipe_obj, config)
|
||||
relationships = Add_File._get_relationships(result, pipe_obj)
|
||||
direct_export_download = False
|
||||
try:
|
||||
if isinstance(pipe_obj.extra, dict):
|
||||
direct_export_download = bool(pipe_obj.extra.pop("_direct_export_download", False))
|
||||
except Exception:
|
||||
direct_export_download = False
|
||||
|
||||
upload_kwargs.update(
|
||||
{
|
||||
"title": title,
|
||||
"tags": tags,
|
||||
"urls": urls,
|
||||
"hash_value": f_hash,
|
||||
"relationships": relationships,
|
||||
"direct_export_download": direct_export_download,
|
||||
}
|
||||
)
|
||||
|
||||
upload_result = file_provider.upload(
|
||||
str(media_path),
|
||||
pipe_obj=pipe_obj,
|
||||
instance=instance_name,
|
||||
**upload_kwargs,
|
||||
)
|
||||
|
||||
duplicate_upload = False
|
||||
@@ -2478,29 +2404,22 @@ class Add_File(Cmdlet):
|
||||
duplicate_rule = ""
|
||||
duplicate_target = ""
|
||||
|
||||
try:
|
||||
debug_panel(
|
||||
"add-file plugin upload",
|
||||
[
|
||||
("plugin", plugin_name),
|
||||
("instance", instance_name or "<default>"),
|
||||
("source", media_path),
|
||||
("duplicate", duplicate_upload),
|
||||
("rule", duplicate_rule or "none"),
|
||||
("target", duplicate_target or ""),
|
||||
("url", hoster_url),
|
||||
],
|
||||
border_style="yellow" if duplicate_upload else "green",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
f_hash = Add_File._resolve_file_hash(None, media_path, pipe_obj, None)
|
||||
|
||||
except Exception as exc:
|
||||
log(f"Upload failed: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if isinstance(upload_result, dict):
|
||||
return Add_File._emit_plugin_upload_payload(
|
||||
upload_result,
|
||||
plugin_name,
|
||||
instance_name,
|
||||
pipe_obj,
|
||||
media_path,
|
||||
delete_after,
|
||||
)
|
||||
|
||||
hoster_url = str(upload_result or "").strip()
|
||||
|
||||
# Update PipeObject and emit
|
||||
extra_updates: Dict[str,
|
||||
Any] = {
|
||||
|
||||
+20
-6
@@ -21,7 +21,6 @@ class File(Cmdlet):
|
||||
"get": {"-get", "--get"},
|
||||
"merge": {"-merge", "--merge"},
|
||||
"download": {"-download", "--download", "-dl", "--dl"},
|
||||
"search": {"-search", "--search"},
|
||||
"convert": {"-convert", "--convert"},
|
||||
"trim": {"-trim", "--trim"},
|
||||
"archive": {"-archive", "--archive"},
|
||||
@@ -45,9 +44,10 @@ class File(Cmdlet):
|
||||
super().__init__(
|
||||
name="file",
|
||||
summary="Manage file operations with one command",
|
||||
usage='file (-add|-delete|-get|-merge|-download|-search|-convert|-trim|-archive|-screenshot) [args]',
|
||||
usage='file -query <query> [args] | file (-add|-delete|-get|-merge|-download|-convert|-trim|-archive|-screenshot) [args]',
|
||||
arg=[
|
||||
SharedArgs.QUERY,
|
||||
SharedArgs.PLUGIN,
|
||||
SharedArgs.INSTANCE,
|
||||
SharedArgs.PATH,
|
||||
CmdletArg("-add", type="flag", required=False, description="Run add-file"),
|
||||
@@ -55,21 +55,32 @@ class File(Cmdlet):
|
||||
CmdletArg("-get", type="flag", required=False, description="Run get-file"),
|
||||
CmdletArg("-merge", type="flag", required=False, description="Run merge-file"),
|
||||
CmdletArg("-download", type="flag", required=False, description="Run download-file", alias="dl"),
|
||||
CmdletArg("-search", type="flag", required=False, description="Run search-file"),
|
||||
CmdletArg("-convert", type="flag", required=False, description="Run convert-file"),
|
||||
CmdletArg("-trim", type="flag", required=False, description="Run trim-file"),
|
||||
CmdletArg("-archive", type="flag", required=False, description="Run archive-file"),
|
||||
CmdletArg("-screenshot", type="flag", required=False, description="Run screen-shot", alias="shot"),
|
||||
],
|
||||
detail=[
|
||||
"- Exactly one action flag is required.",
|
||||
"- Use -query to run search-file through the unified file command.",
|
||||
"- Otherwise, exactly one non-search action flag is required.",
|
||||
"- Remaining args are passed through to the selected file cmdlet.",
|
||||
"- Examples: file -add ..., file -delete ..., file -merge ...",
|
||||
"- Examples: file -query ..., file -add ..., file -delete ...",
|
||||
],
|
||||
exec=self.run,
|
||||
)
|
||||
self.register()
|
||||
|
||||
@staticmethod
|
||||
def _has_query_arg(args: Sequence[str]) -> bool:
|
||||
query_flags = {"-query", "--query"}
|
||||
for token in args or []:
|
||||
text = str(token or "").strip().lower()
|
||||
if text in query_flags:
|
||||
return True
|
||||
if any(text.startswith(f"{flag}=") for flag in query_flags):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _extract_action(cls, args: Sequence[str]) -> tuple[str | None, List[str], List[str]]:
|
||||
matched_actions: List[str] = []
|
||||
@@ -93,6 +104,9 @@ class File(Cmdlet):
|
||||
if action not in unique_actions:
|
||||
unique_actions.append(action)
|
||||
|
||||
if not unique_actions and cls._has_query_arg(passthrough):
|
||||
return "search", passthrough, unique_actions
|
||||
|
||||
if len(unique_actions) != 1:
|
||||
return None, passthrough, unique_actions
|
||||
return unique_actions[0], passthrough, unique_actions
|
||||
@@ -125,7 +139,7 @@ class File(Cmdlet):
|
||||
if action is None:
|
||||
if not seen:
|
||||
log(
|
||||
"file: missing action flag; choose exactly one of -add, -delete, -get, -merge, -download, -search, -convert, -trim, -archive, -screenshot",
|
||||
"file: missing action; use -query for search or choose exactly one of -add, -delete, -get, -merge, -download, -convert, -trim, -archive, -screenshot",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user