cmdlet refactor
This commit is contained in:
@@ -392,7 +392,7 @@ def _dispatch_alldebrid_magnet_search(
|
||||
config: Dict[str, Any],
|
||||
) -> None:
|
||||
try:
|
||||
from cmdlet.search_file import CMDLET as _SEARCH_FILE_CMDLET
|
||||
from cmdlet.file.search import CMDLET as _SEARCH_FILE_CMDLET
|
||||
|
||||
exec_fn = getattr(_SEARCH_FILE_CMDLET, "exec", None)
|
||||
if callable(exec_fn):
|
||||
|
||||
@@ -549,6 +549,8 @@ class FTP(Provider):
|
||||
if not local_path.exists() or not local_path.is_file():
|
||||
raise FileNotFoundError(f"File not found: {local_path}")
|
||||
|
||||
pipe_obj = kwargs.get("pipe_obj")
|
||||
|
||||
settings = self._resolve_settings(
|
||||
instance_name=str(kwargs.get("instance") or kwargs.get("store") or "").strip() or None,
|
||||
require_explicit=bool(kwargs.get("instance") or kwargs.get("store")),
|
||||
@@ -569,6 +571,19 @@ class FTP(Provider):
|
||||
ftp = self._connect(settings=settings)
|
||||
try:
|
||||
self._ensure_directory(ftp, remote_dir, base_path=str(settings.get("base_path") or "/"))
|
||||
# FTP duplicate check is filename-based at the destination directory.
|
||||
# If the exact filename already exists remotely, skip re-upload.
|
||||
if self._remote_filename_exists(ftp, remote_dir, remote_name):
|
||||
try:
|
||||
if pipe_obj is not None:
|
||||
if not isinstance(getattr(pipe_obj, "extra", None), dict):
|
||||
pipe_obj.extra = {}
|
||||
pipe_obj.extra["upload_duplicate"] = True
|
||||
pipe_obj.extra["upload_duplicate_rule"] = "filename"
|
||||
pipe_obj.extra["upload_duplicate_target"] = remote_path
|
||||
except Exception:
|
||||
pass
|
||||
return self._build_url(remote_path, settings=settings)
|
||||
with local_path.open("rb") as handle:
|
||||
ftp.storbinary(f"STOR {remote_path}", handle)
|
||||
finally:
|
||||
@@ -930,6 +945,36 @@ class FTP(Provider):
|
||||
if not self._is_directory(ftp, partial):
|
||||
raise
|
||||
|
||||
def _remote_filename_exists(self, ftp: ftplib.FTP, remote_dir: str, filename: str) -> bool:
|
||||
target_name = str(filename or "").strip()
|
||||
if not target_name:
|
||||
return False
|
||||
|
||||
normalized_dir = self._normalize_remote_path(remote_dir, default=self._base_path)
|
||||
|
||||
try:
|
||||
for name, facts in ftp.mlsd(normalized_dir):
|
||||
_ = facts
|
||||
if str(name or "").strip() == target_name:
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
entries = ftp.nlst(normalized_dir)
|
||||
except Exception:
|
||||
entries = []
|
||||
|
||||
for entry in entries or []:
|
||||
entry_text = str(entry or "").strip().rstrip("/")
|
||||
if not entry_text:
|
||||
continue
|
||||
entry_name = posixpath.basename(entry_text)
|
||||
if entry_name == target_name:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _item_metadata(self, item: Any, *, pipe_obj: Any = None) -> Dict[str, Any]:
|
||||
metadata: Dict[str, Any] = {}
|
||||
for source in (item, pipe_obj):
|
||||
|
||||
+14
-14
@@ -2842,7 +2842,7 @@ local function _start_screenshot_store_save(store, out_path, tags)
|
||||
if screenshot_url == '' or not screenshot_url:match('^https?://') then
|
||||
screenshot_url = ''
|
||||
end
|
||||
local cmd = 'add-file -store ' .. quote_pipeline_arg(store)
|
||||
local cmd = 'file -add -store ' .. quote_pipeline_arg(store)
|
||||
.. ' -path ' .. quote_pipeline_arg(out_path)
|
||||
if screenshot_url ~= '' then
|
||||
cmd = cmd .. ' -url ' .. quote_pipeline_arg(screenshot_url)
|
||||
@@ -2854,7 +2854,7 @@ local function _start_screenshot_store_save(store, out_path, tags)
|
||||
local tag_suffix = (#tag_list > 0) and (' | tags: ' .. tostring(#tag_list)) or ''
|
||||
if #tag_list > 0 then
|
||||
local tag_string = table.concat(tag_list, ',')
|
||||
cmd = cmd .. ' | add-tag ' .. quote_pipeline_arg(tag_string)
|
||||
cmd = cmd .. ' | tag -add ' .. quote_pipeline_arg(tag_string)
|
||||
end
|
||||
|
||||
local queue_target = is_named_store and ('store ' .. store) or 'folder'
|
||||
@@ -5539,7 +5539,7 @@ local function _start_download_flow_for_current()
|
||||
end
|
||||
|
||||
ensure_mpv_ipc_server()
|
||||
local pipeline_cmd = 'get-file -store ' .. quote_pipeline_arg(store_hash.store) .. ' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) .. ' -path ' .. quote_pipeline_arg(folder)
|
||||
local pipeline_cmd = 'file -get -store ' .. quote_pipeline_arg(store_hash.store) .. ' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) .. ' -path ' .. quote_pipeline_arg(folder)
|
||||
_queue_pipeline_in_repl(
|
||||
pipeline_cmd,
|
||||
'Queued in REPL: store copy',
|
||||
@@ -5835,9 +5835,9 @@ mp.register_script_message('medios-download-pick-store', function(json)
|
||||
end
|
||||
local clip_suffix = clip_range ~= '' and (' [' .. clip_range .. ']') or ''
|
||||
|
||||
local pipeline_cmd = 'download-file -url ' .. quote_pipeline_arg(url)
|
||||
local pipeline_cmd = 'file -download -url ' .. quote_pipeline_arg(url)
|
||||
.. ' -query ' .. quote_pipeline_arg(query)
|
||||
.. ' | add-file -store ' .. quote_pipeline_arg(store)
|
||||
.. ' | file -add -store ' .. quote_pipeline_arg(store)
|
||||
|
||||
_set_selected_store(store)
|
||||
_queue_pipeline_in_repl(
|
||||
@@ -5901,9 +5901,9 @@ mp.register_script_message('medios-download-pick-path', function()
|
||||
end
|
||||
local clip_suffix = clip_range ~= '' and (' [' .. clip_range .. ']') or ''
|
||||
|
||||
local pipeline_cmd = 'download-file -url ' .. quote_pipeline_arg(url)
|
||||
local pipeline_cmd = 'file -download -url ' .. quote_pipeline_arg(url)
|
||||
.. ' -query ' .. quote_pipeline_arg(query)
|
||||
.. ' | add-file -path ' .. quote_pipeline_arg(folder)
|
||||
.. ' | file -add -path ' .. quote_pipeline_arg(folder)
|
||||
|
||||
_queue_pipeline_in_repl(
|
||||
pipeline_cmd,
|
||||
@@ -6137,7 +6137,7 @@ function M.delete_current_file()
|
||||
|
||||
local seed = {{path = path}}
|
||||
|
||||
M.run_pipeline('delete-file', seed, function(_, err)
|
||||
M.run_pipeline('file -delete', seed, function(_, err)
|
||||
if err then
|
||||
mp.osd_message('Delete failed: ' .. tostring(err), 3)
|
||||
return
|
||||
@@ -6302,17 +6302,17 @@ local function _start_trim_with_range(range)
|
||||
_lua_log('trim: building store file pipeline (original from store)')
|
||||
if selected_store then
|
||||
pipeline_cmd =
|
||||
'get-tag -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
'tag -get -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) ..
|
||||
' | add-file -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' | file -add -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' -store "' .. selected_store .. '"' ..
|
||||
' | add-relationship -store "' .. selected_store .. '"' ..
|
||||
' -to-hash ' .. quote_pipeline_arg(store_hash.hash)
|
||||
else
|
||||
pipeline_cmd =
|
||||
'get-tag -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
'tag -get -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) ..
|
||||
' | add-file -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' | file -add -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' -store "' .. store_hash.store .. '"' ..
|
||||
' | add-relationship -store "' .. store_hash.store .. '"' ..
|
||||
' -to-hash ' .. quote_pipeline_arg(store_hash.hash)
|
||||
@@ -6321,9 +6321,9 @@ local function _start_trim_with_range(range)
|
||||
-- Local file: save to selected store if available
|
||||
_lua_log('trim: local file pipeline (not from store)')
|
||||
if selected_store then
|
||||
_lua_log('trim: building add-file command to selected_store=' .. selected_store)
|
||||
_lua_log('trim: building file -add command to selected_store=' .. selected_store)
|
||||
-- Don't add title if empty - the file path will be used as title by default
|
||||
pipeline_cmd = 'add-file -path ' .. quote_pipeline_arg(output_path) ..
|
||||
pipeline_cmd = 'file -add -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' -store "' .. selected_store .. '"'
|
||||
_lua_log('trim: pipeline_cmd=' .. pipeline_cmd)
|
||||
else
|
||||
|
||||
@@ -470,11 +470,11 @@ class MPV:
|
||||
def _q(s: str) -> str:
|
||||
return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||
|
||||
pipeline = f"download-file -url {_q(url)} -query {_q(f'format:{fmt}')}"
|
||||
pipeline = f"file -download -url {_q(url)} -query {_q(f'format:{fmt}')}"
|
||||
if store:
|
||||
pipeline += f" | add-file -instance {_q(store)}"
|
||||
pipeline += f" | file -add -instance {_q(store)}"
|
||||
else:
|
||||
pipeline += f" | add-file -path {_q(path or '')}"
|
||||
pipeline += f" | file -add -path {_q(path or '')}"
|
||||
|
||||
try:
|
||||
from TUI.pipeline_runner import PipelineRunner # noqa: WPS433
|
||||
|
||||
@@ -582,6 +582,8 @@ class SCP(Provider):
|
||||
if not local_path.exists() or not local_path.is_file():
|
||||
raise FileNotFoundError(f"File not found: {local_path}")
|
||||
|
||||
pipe_obj = kwargs.get("pipe_obj")
|
||||
|
||||
settings = self._resolve_settings(
|
||||
instance_name=str(kwargs.get("instance") or kwargs.get("store") or "").strip() or None,
|
||||
require_explicit=bool(kwargs.get("instance") or kwargs.get("store")),
|
||||
@@ -609,8 +611,30 @@ class SCP(Provider):
|
||||
if not self._is_sftp_negotiation_error(exc):
|
||||
raise
|
||||
self._ensure_directory_via_ssh(ssh, remote_dir)
|
||||
if self._remote_filename_exists_via_ssh(ssh, remote_path):
|
||||
try:
|
||||
if pipe_obj is not None:
|
||||
if not isinstance(getattr(pipe_obj, "extra", None), dict):
|
||||
pipe_obj.extra = {}
|
||||
pipe_obj.extra["upload_duplicate"] = True
|
||||
pipe_obj.extra["upload_duplicate_rule"] = "filename"
|
||||
pipe_obj.extra["upload_duplicate_target"] = remote_path
|
||||
except Exception:
|
||||
pass
|
||||
return self._build_url(remote_path, settings=settings)
|
||||
else:
|
||||
self._ensure_directory(sftp, remote_dir, base_path=str(settings.get("base_path") or "/"))
|
||||
if self._remote_filename_exists(sftp, remote_path):
|
||||
try:
|
||||
if pipe_obj is not None:
|
||||
if not isinstance(getattr(pipe_obj, "extra", None), dict):
|
||||
pipe_obj.extra = {}
|
||||
pipe_obj.extra["upload_duplicate"] = True
|
||||
pipe_obj.extra["upload_duplicate_rule"] = "filename"
|
||||
pipe_obj.extra["upload_duplicate_target"] = remote_path
|
||||
except Exception:
|
||||
pass
|
||||
return self._build_url(remote_path, settings=settings)
|
||||
scp_client = self._open_scp(ssh)
|
||||
scp_client.put(str(local_path), remote_path=remote_path)
|
||||
finally:
|
||||
@@ -620,6 +644,19 @@ class SCP(Provider):
|
||||
|
||||
return self._build_url(remote_path, settings=settings)
|
||||
|
||||
def _remote_filename_exists(self, sftp: Any, remote_path: str) -> bool:
|
||||
try:
|
||||
sftp.stat(remote_path)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _remote_filename_exists_via_ssh(self, ssh: Any, remote_path: str) -> bool:
|
||||
normalized = self._normalize_remote_path(remote_path, default=self._base_path)
|
||||
quoted_path = shlex.quote(normalized)
|
||||
status, _, _ = self._run_ssh_command(ssh, f"test -e {quoted_path}")
|
||||
return status == 0
|
||||
|
||||
def _run_test_connection(self) -> Dict[str, Any]:
|
||||
settings = self._resolve_settings()
|
||||
if not settings.get("host"):
|
||||
|
||||
Reference in New Issue
Block a user