df
This commit is contained in:
@@ -102,7 +102,7 @@ def maybe_show_formats_table(
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
base_args: List[str] = []
|
base_args: List[str] = []
|
||||||
out_arg = parsed.get("path") or parsed.get("output")
|
out_arg = parsed.get("path")
|
||||||
if out_arg:
|
if out_arg:
|
||||||
base_args.extend(["-path", str(out_arg)])
|
base_args.extend(["-path", str(out_arg)])
|
||||||
|
|
||||||
|
|||||||
@@ -309,15 +309,10 @@ def get_provider_credentials(config: Dict[str, Any], provider: str) -> Optional[
|
|||||||
def resolve_cookies_path(
|
def resolve_cookies_path(
|
||||||
config: Dict[str, Any], script_dir: Optional[Path] = None
|
config: Dict[str, Any], script_dir: Optional[Path] = None
|
||||||
) -> Optional[Path]:
|
) -> Optional[Path]:
|
||||||
# Support both legacy top-level `cookies=...` and the modular conf style:
|
# Only support modular config style:
|
||||||
# [tool=ytdlp]
|
# [tool=ytdlp]
|
||||||
# cookies="C:\\path\\cookies.txt"
|
# cookies="C:\\path\\cookies.txt"
|
||||||
values: list[Any] = []
|
values: list[Any] = []
|
||||||
try:
|
|
||||||
values.append(config.get("cookies"))
|
|
||||||
except Exception as exc:
|
|
||||||
logger.debug("resolve_cookies_path: failed to read top-level cookies: %s", exc, exc_info=True)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tool = config.get("tool")
|
tool = config.get("tool")
|
||||||
if isinstance(tool, dict):
|
if isinstance(tool, dict):
|
||||||
@@ -328,14 +323,6 @@ def resolve_cookies_path(
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.debug("resolve_cookies_path: failed to read tool.ytdlp cookies: %s", exc, exc_info=True)
|
logger.debug("resolve_cookies_path: failed to read tool.ytdlp cookies: %s", exc, exc_info=True)
|
||||||
|
|
||||||
try:
|
|
||||||
ytdlp_block = config.get("ytdlp")
|
|
||||||
if isinstance(ytdlp_block, dict):
|
|
||||||
values.append(ytdlp_block.get("cookies"))
|
|
||||||
values.append(ytdlp_block.get("cookiefile"))
|
|
||||||
except Exception as exc:
|
|
||||||
logger.debug("resolve_cookies_path: failed to read ytdlp cookies block: %s", exc, exc_info=True)
|
|
||||||
|
|
||||||
base_dir = script_dir or SCRIPT_DIR
|
base_dir = script_dir or SCRIPT_DIR
|
||||||
for value in values:
|
for value in values:
|
||||||
if not value:
|
if not value:
|
||||||
|
|||||||
@@ -373,9 +373,6 @@ def normalize_urls(value: Any) -> List[str]:
|
|||||||
text = raw.strip()
|
text = raw.strip()
|
||||||
if not text:
|
if not text:
|
||||||
return
|
return
|
||||||
# Support legacy prefixes like "url:https://...".
|
|
||||||
if text.lower().startswith("url:"):
|
|
||||||
text = text.split(":", 1)[1].strip()
|
|
||||||
|
|
||||||
# Prefer extracting obvious URLs to avoid splitting inside query strings.
|
# Prefer extracting obvious URLs to avoid splitting inside query strings.
|
||||||
matches = re.findall(r"https?://[^\s,]+", text, flags=re.IGNORECASE)
|
matches = re.findall(r"https?://[^\s,]+", text, flags=re.IGNORECASE)
|
||||||
|
|||||||
@@ -162,13 +162,13 @@ def _as_dict(item: Any) -> Optional[Dict[str, Any]]:
|
|||||||
|
|
||||||
def extract_store_value(item: Any) -> str:
|
def extract_store_value(item: Any) -> str:
|
||||||
"""Extract storage backend name from item.
|
"""Extract storage backend name from item.
|
||||||
|
|
||||||
Searches item for store identifier using multiple field names:
|
Searches item for store identifier using field names:
|
||||||
store, table, source, storage (legacy).
|
store, table.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
item: Object or dict with store information
|
item: Object or dict with store information
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Store name as string (e.g., "hydrus", "local", "") if not found
|
Store name as string (e.g., "hydrus", "local", "") if not found
|
||||||
"""
|
"""
|
||||||
@@ -176,10 +176,8 @@ def extract_store_value(item: Any) -> str:
|
|||||||
store = _get_first_dict_value(
|
store = _get_first_dict_value(
|
||||||
data,
|
data,
|
||||||
["store",
|
["store",
|
||||||
"table",
|
"table"]
|
||||||
"source",
|
)
|
||||||
"storage"]
|
|
||||||
) # storage is legacy
|
|
||||||
return str(store or "").strip()
|
return str(store or "").strip()
|
||||||
|
|
||||||
|
|
||||||
@@ -1059,8 +1057,7 @@ class Table:
|
|||||||
row.add_column("Tag", item.tag_name)
|
row.add_column("Tag", item.tag_name)
|
||||||
|
|
||||||
# Source/Store (where the tag values come from)
|
# Source/Store (where the tag values come from)
|
||||||
# Support both 'source' (legacy) and 'store' (new) attribute names
|
source_val = getattr(item, "store", None)
|
||||||
source_val = getattr(item, "source", None) or getattr(item, "store", None)
|
|
||||||
if source_val:
|
if source_val:
|
||||||
row.add_column("Store", source_val)
|
row.add_column("Store", source_val)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,31 @@ import httpx
|
|||||||
from SYS.logger import debug, log
|
from SYS.logger import debug, log
|
||||||
from SYS.utils_constant import mime_maps
|
from SYS.utils_constant import mime_maps
|
||||||
|
|
||||||
|
_KNOWN_EXTS = {
|
||||||
|
str(info.get("ext") or "").strip().lstrip(".")
|
||||||
|
for category in mime_maps.values()
|
||||||
|
for info in category.values()
|
||||||
|
if isinstance(info, dict) and info.get("ext")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_ext_from_meta(meta: Dict[str, Any], mime_type: Optional[str]) -> str:
|
||||||
|
ext = str(meta.get("ext") or "").strip().lstrip(".")
|
||||||
|
if ext and ext not in _KNOWN_EXTS:
|
||||||
|
ext = ""
|
||||||
|
if ext.lower() == "ebook":
|
||||||
|
ext = ""
|
||||||
|
|
||||||
|
if not ext and mime_type:
|
||||||
|
for category in mime_maps.values():
|
||||||
|
for _ext_key, info in category.items():
|
||||||
|
if mime_type in info.get("mimes", []):
|
||||||
|
ext = str(info.get("ext", "")).strip().lstrip(".")
|
||||||
|
break
|
||||||
|
if ext:
|
||||||
|
break
|
||||||
|
return ext
|
||||||
|
|
||||||
from Store._base import Store
|
from Store._base import Store
|
||||||
|
|
||||||
_HYDRUS_INIT_CHECK_CACHE: dict[tuple[str,
|
_HYDRUS_INIT_CHECK_CACHE: dict[tuple[str,
|
||||||
@@ -67,6 +92,16 @@ class HydrusNetwork(Store):
|
|||||||
store_name = getattr(self, "NAME", None) or "unknown"
|
store_name = getattr(self, "NAME", None) or "unknown"
|
||||||
return f"[hydrusnetwork:{store_name}]"
|
return f"[hydrusnetwork:{store_name}]"
|
||||||
|
|
||||||
|
def _append_access_key(self, url: str) -> str:
|
||||||
|
if not url:
|
||||||
|
return url
|
||||||
|
if "access_key=" in url:
|
||||||
|
return url
|
||||||
|
if not getattr(self, "API", None):
|
||||||
|
return url
|
||||||
|
separator = "&" if "?" in url else "?"
|
||||||
|
return f"{url}{separator}access_key={quote(str(self.API))}"
|
||||||
|
|
||||||
def __new__(cls, *args: Any, **kwargs: Any) -> "HydrusNetwork":
|
def __new__(cls, *args: Any, **kwargs: Any) -> "HydrusNetwork":
|
||||||
instance = super().__new__(cls)
|
instance = super().__new__(cls)
|
||||||
name = kwargs.get("NAME")
|
name = kwargs.get("NAME")
|
||||||
@@ -1105,17 +1140,7 @@ class HydrusNetwork(Store):
|
|||||||
if not isinstance(meta, dict):
|
if not isinstance(meta, dict):
|
||||||
continue
|
continue
|
||||||
mime_type = meta.get("mime")
|
mime_type = meta.get("mime")
|
||||||
ext = str(meta.get("ext") or "").strip().lstrip(".")
|
ext = _resolve_ext_from_meta(meta, mime_type)
|
||||||
if not ext and mime_type:
|
|
||||||
for category in mime_maps.values():
|
|
||||||
for _ext_key, info in category.items():
|
|
||||||
if mime_type in info.get("mimes", []):
|
|
||||||
ext = str(info.get("ext",
|
|
||||||
"")
|
|
||||||
).strip().lstrip(".")
|
|
||||||
break
|
|
||||||
if ext:
|
|
||||||
break
|
|
||||||
if _normalize_ext_filter(ext) != ext_filter:
|
if _normalize_ext_filter(ext) != ext_filter:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1168,6 +1193,8 @@ class HydrusNetwork(Store):
|
|||||||
item_url = meta.get("known_urls") or meta.get("urls") or meta.get("url") or []
|
item_url = meta.get("known_urls") or meta.get("urls") or meta.get("url") or []
|
||||||
if not item_url:
|
if not item_url:
|
||||||
item_url = meta.get("file_url") or f"{self.URL.rstrip('/')}/view_file?hash={hash_hex}"
|
item_url = meta.get("file_url") or f"{self.URL.rstrip('/')}/view_file?hash={hash_hex}"
|
||||||
|
if isinstance(item_url, str) and "/view_file" in item_url:
|
||||||
|
item_url = self._append_access_key(item_url)
|
||||||
|
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
@@ -1181,7 +1208,7 @@ class HydrusNetwork(Store):
|
|||||||
"tag": all_tags,
|
"tag": all_tags,
|
||||||
"file_id": file_id,
|
"file_id": file_id,
|
||||||
"mime": mime_type,
|
"mime": mime_type,
|
||||||
"ext": ext,
|
"ext": _resolve_ext_from_meta(meta, mime_type),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1312,15 +1339,7 @@ class HydrusNetwork(Store):
|
|||||||
|
|
||||||
# Prefer Hydrus-provided extension (e.g. ".webm"); fall back to MIME map.
|
# Prefer Hydrus-provided extension (e.g. ".webm"); fall back to MIME map.
|
||||||
mime_type = meta.get("mime")
|
mime_type = meta.get("mime")
|
||||||
ext = str(meta.get("ext") or "").strip().lstrip(".")
|
ext = _resolve_ext_from_meta(meta, mime_type)
|
||||||
if not ext and mime_type:
|
|
||||||
for category in mime_maps.values():
|
|
||||||
for _ext_key, info in category.items():
|
|
||||||
if mime_type in info.get("mimes", []):
|
|
||||||
ext = str(info.get("ext", "")).strip().lstrip(".")
|
|
||||||
break
|
|
||||||
if ext:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Filter results based on query type
|
# Filter results based on query type
|
||||||
# If user provided explicit namespace (has ':'), don't do substring filtering
|
# If user provided explicit namespace (has ':'), don't do substring filtering
|
||||||
@@ -1331,6 +1350,8 @@ class HydrusNetwork(Store):
|
|||||||
item_url = meta.get("known_urls") or meta.get("urls") or meta.get("url") or []
|
item_url = meta.get("known_urls") or meta.get("urls") or meta.get("url") or []
|
||||||
if not item_url:
|
if not item_url:
|
||||||
item_url = meta.get("file_url") or f"{self.URL.rstrip('/')}/view_file?hash={hash_hex}"
|
item_url = meta.get("file_url") or f"{self.URL.rstrip('/')}/view_file?hash={hash_hex}"
|
||||||
|
if isinstance(item_url, str) and "/view_file" in item_url:
|
||||||
|
item_url = self._append_access_key(item_url)
|
||||||
|
|
||||||
if has_namespace:
|
if has_namespace:
|
||||||
# Explicit namespace search - already filtered by Hydrus tag search
|
# Explicit namespace search - already filtered by Hydrus tag search
|
||||||
|
|||||||
@@ -101,20 +101,7 @@ def _required_keys_for(store_cls: Type[BaseStore]) -> list[str]:
|
|||||||
return keys
|
return keys
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
return []
|
||||||
# Legacy __new__.keys support
|
|
||||||
keys = getattr(store_cls.__new__, "keys", None)
|
|
||||||
if keys is None:
|
|
||||||
return []
|
|
||||||
if isinstance(keys, dict):
|
|
||||||
return [str(k) for k in keys.keys()]
|
|
||||||
if isinstance(keys, (list, tuple, set, frozenset)):
|
|
||||||
return [str(k) for k in keys]
|
|
||||||
if isinstance(keys, str):
|
|
||||||
return [keys]
|
|
||||||
raise TypeError(
|
|
||||||
f"Unsupported __new__.keys type for {store_cls.__name__}: {type(keys)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Store type names that have been converted to providers-only.
|
# Store type names that have been converted to providers-only.
|
||||||
|
|||||||
@@ -340,7 +340,6 @@ class SharedArgs:
|
|||||||
# Path/File arguments
|
# Path/File arguments
|
||||||
PATH = CmdletArg("path", type="string", description="File or directory path.")
|
PATH = CmdletArg("path", type="string", description="File or directory path.")
|
||||||
|
|
||||||
OUTPUT = CmdletArg("output", type="string", description="Output file path.")
|
|
||||||
|
|
||||||
# Generic arguments
|
# Generic arguments
|
||||||
QUERY = CmdletArg(
|
QUERY = CmdletArg(
|
||||||
@@ -782,7 +781,7 @@ def resolve_target_dir(
|
|||||||
*,
|
*,
|
||||||
handle_creations: bool = True
|
handle_creations: bool = True
|
||||||
) -> Optional[Path]:
|
) -> Optional[Path]:
|
||||||
"""Resolve a target directory from -path, -output, -storage, or config fallback.
|
"""Resolve a target directory from -path, -storage, or config fallback.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
parsed: Parsed cmdlet arguments dict.
|
parsed: Parsed cmdlet arguments dict.
|
||||||
@@ -792,8 +791,8 @@ def resolve_target_dir(
|
|||||||
Returns:
|
Returns:
|
||||||
Path to the resolved directory, or None if invalid.
|
Path to the resolved directory, or None if invalid.
|
||||||
"""
|
"""
|
||||||
# Priority 1: Explicit -path or -output
|
# Priority 1: Explicit -path
|
||||||
target = parsed.get("path") or parsed.get("output")
|
target = parsed.get("path")
|
||||||
if target:
|
if target:
|
||||||
try:
|
try:
|
||||||
p = Path(str(target)).expanduser().resolve()
|
p = Path(str(target)).expanduser().resolve()
|
||||||
|
|||||||
@@ -72,13 +72,6 @@ class Download_File(Cmdlet):
|
|||||||
SharedArgs.PROVIDER,
|
SharedArgs.PROVIDER,
|
||||||
SharedArgs.PATH,
|
SharedArgs.PATH,
|
||||||
SharedArgs.QUERY,
|
SharedArgs.QUERY,
|
||||||
# Prefer -path for output directory to match other cmdlets; keep -output for backwards compatibility.
|
|
||||||
CmdletArg(
|
|
||||||
name="-output",
|
|
||||||
type="string",
|
|
||||||
alias="o",
|
|
||||||
description="(deprecated) Output directory (use -path instead)",
|
|
||||||
),
|
|
||||||
QueryArg(
|
QueryArg(
|
||||||
"clip",
|
"clip",
|
||||||
key="clip",
|
key="clip",
|
||||||
@@ -2782,7 +2775,7 @@ class Download_File(Cmdlet):
|
|||||||
# UX: In piped mode, allow a single positional arg to be the destination directory.
|
# UX: In piped mode, allow a single positional arg to be the destination directory.
|
||||||
# Example: @1-4 | download-file "C:\\Users\\Me\\Downloads\\yoyo"
|
# Example: @1-4 | download-file "C:\\Users\\Me\\Downloads\\yoyo"
|
||||||
if (had_piped_input and raw_url and len(raw_url) == 1
|
if (had_piped_input and raw_url and len(raw_url) == 1
|
||||||
and (not parsed.get("path")) and (not parsed.get("output"))):
|
and (not parsed.get("path"))):
|
||||||
candidate = str(raw_url[0] or "").strip()
|
candidate = str(raw_url[0] or "").strip()
|
||||||
low = candidate.lower()
|
low = candidate.lower()
|
||||||
looks_like_url = low.startswith((
|
looks_like_url = low.startswith((
|
||||||
|
|||||||
@@ -971,8 +971,18 @@ class search_file(Cmdlet):
|
|||||||
p_str = str(p_val or "").strip()
|
p_str = str(p_val or "").strip()
|
||||||
if p_str:
|
if p_str:
|
||||||
if p_str.startswith(("http://", "https://", "magnet:", "torrent:")):
|
if p_str.startswith(("http://", "https://", "magnet:", "torrent:")):
|
||||||
sel_args = ["-url", p_str]
|
h = normalized.get("hash") or normalized.get("file_hash") or normalized.get("hash_hex")
|
||||||
sel_action = ["download-file", "-url", p_str]
|
s_val = normalized.get("store")
|
||||||
|
if h and s_val and "/view_file" in p_str:
|
||||||
|
try:
|
||||||
|
h_norm = normalize_hash(h)
|
||||||
|
except Exception:
|
||||||
|
h_norm = str(h)
|
||||||
|
sel_args = ["-query", f"hash:{h_norm}", "-store", str(s_val)]
|
||||||
|
sel_action = ["get-metadata", "-query", f"hash:{h_norm}", "-store", str(s_val)]
|
||||||
|
else:
|
||||||
|
sel_args = ["-url", p_str]
|
||||||
|
sel_action = ["download-file", "-url", p_str]
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
from SYS.utils import expand_path
|
from SYS.utils import expand_path
|
||||||
|
|||||||
Reference in New Issue
Block a user