dsf
This commit is contained in:
@@ -384,6 +384,8 @@ class Folder(Store):
|
||||
try:
|
||||
shutil.move(str(file_path), str(save_file))
|
||||
debug(f"Local move: {save_file}", file=sys.stderr)
|
||||
# After a move, the original path no longer exists; use destination for subsequent ops.
|
||||
file_path = save_file
|
||||
except Exception:
|
||||
_copy_with_progress(file_path, save_file, label=f"folder:{self._name} move")
|
||||
try:
|
||||
@@ -395,6 +397,7 @@ class Folder(Store):
|
||||
except Exception:
|
||||
pass
|
||||
debug(f"Local move (copy+delete): {save_file}", file=sys.stderr)
|
||||
file_path = save_file
|
||||
else:
|
||||
_copy_with_progress(file_path, save_file, label=f"folder:{self._name} copy")
|
||||
debug(f"Local copy: {save_file}", file=sys.stderr)
|
||||
@@ -418,7 +421,7 @@ class Folder(Store):
|
||||
db.save_metadata(save_file, {
|
||||
'hash': file_hash,
|
||||
'ext': ext_clean,
|
||||
'size': file_path.stat().st_size,
|
||||
'size': save_file.stat().st_size,
|
||||
'duration': duration_value,
|
||||
})
|
||||
|
||||
@@ -441,6 +444,7 @@ class Folder(Store):
|
||||
"""Search local database for files by title tag or filename."""
|
||||
from fnmatch import fnmatch
|
||||
from API.folder import DatabaseAPI
|
||||
import unicodedata
|
||||
|
||||
limit = kwargs.get("limit")
|
||||
try:
|
||||
@@ -453,6 +457,30 @@ class Folder(Store):
|
||||
query = query.lower()
|
||||
query_lower = query # Ensure query_lower is defined for all code paths
|
||||
|
||||
def _normalize_namespace_text(text: str, *, allow_wildcards: bool) -> str:
|
||||
"""Normalize tag namespace values for consistent matching.
|
||||
|
||||
Removes control/format chars (e.g. zero-width spaces) that frequently appear in scraped tags,
|
||||
collapses whitespace, and lowercases.
|
||||
"""
|
||||
s = str(text or "")
|
||||
# Normalize newlines/tabs/etc to spaces early.
|
||||
s = s.replace("\r", " ").replace("\n", " ").replace("\t", " ")
|
||||
# Drop control / format chars (Cc/Cf) while preserving wildcard tokens when requested.
|
||||
cleaned_chars: list[str] = []
|
||||
for ch in s:
|
||||
if allow_wildcards and ch in {"*", "?"}:
|
||||
cleaned_chars.append(ch)
|
||||
continue
|
||||
cat = unicodedata.category(ch)
|
||||
if cat in {"Cc", "Cf"}:
|
||||
continue
|
||||
cleaned_chars.append(ch)
|
||||
s = "".join(cleaned_chars)
|
||||
# Collapse any remaining unicode whitespace runs.
|
||||
s = " ".join(s.split())
|
||||
return s.strip().lower()
|
||||
|
||||
def _normalize_ext_filter(value: str) -> str:
|
||||
v = str(value or "").strip().lower().lstrip('.')
|
||||
v = "".join(ch for ch in v if ch.isalnum())
|
||||
@@ -648,8 +676,9 @@ class Folder(Store):
|
||||
tag_lower = str(tag_val).lower()
|
||||
if not tag_lower.startswith(f"{namespace}:"):
|
||||
continue
|
||||
value = tag_lower[len(namespace)+1:]
|
||||
if fnmatch(value, pattern):
|
||||
value = _normalize_namespace_text(tag_lower[len(namespace) + 1 :], allow_wildcards=False)
|
||||
pat = _normalize_namespace_text(pattern, allow_wildcards=True)
|
||||
if fnmatch(value, pat):
|
||||
matched.add(file_hash)
|
||||
return matched
|
||||
|
||||
@@ -838,8 +867,9 @@ class Folder(Store):
|
||||
for tag in tags:
|
||||
tag_lower = tag.lower()
|
||||
if tag_lower.startswith(f"{namespace}:"):
|
||||
value = tag_lower[len(namespace)+1:]
|
||||
if fnmatch(value, pattern):
|
||||
value = _normalize_namespace_text(tag_lower[len(namespace) + 1 :], allow_wildcards=False)
|
||||
pat = _normalize_namespace_text(pattern, allow_wildcards=True)
|
||||
if fnmatch(value, pat):
|
||||
if ext_hashes is not None and file_hash not in ext_hashes:
|
||||
break
|
||||
file_path = Path(file_path_str)
|
||||
@@ -1636,20 +1666,43 @@ class Folder(Store):
|
||||
"""
|
||||
from API.folder import API_folder_store
|
||||
try:
|
||||
file_path = Path(file_identifier)
|
||||
|
||||
# Delete from database
|
||||
with API_folder_store(Path(self._location)) as db:
|
||||
db.delete_file(file_path)
|
||||
|
||||
# Delete the actual file from disk
|
||||
if file_path.exists():
|
||||
file_path.unlink()
|
||||
debug(f"Deleted file: {file_path}")
|
||||
return True
|
||||
else:
|
||||
debug(f"File not found on disk: {file_path}")
|
||||
return True # Already gone
|
||||
if not self._location:
|
||||
return False
|
||||
|
||||
raw = str(file_identifier or "").strip()
|
||||
if not raw:
|
||||
return False
|
||||
|
||||
store_root = Path(self._location).expanduser()
|
||||
|
||||
# Support deletion by hash (common for store items where `path` is the hash).
|
||||
file_hash = _normalize_hash(raw)
|
||||
resolved_path: Optional[Path] = None
|
||||
with API_folder_store(store_root) as db:
|
||||
if file_hash:
|
||||
resolved_path = db.search_hash(file_hash)
|
||||
else:
|
||||
p = Path(raw)
|
||||
resolved_path = p if p.is_absolute() else (store_root / p)
|
||||
|
||||
if resolved_path is None:
|
||||
debug(f"delete_file: could not resolve identifier: {raw}")
|
||||
return False
|
||||
|
||||
# Delete from database (also cleans up relationship backlinks).
|
||||
db.delete_file(resolved_path)
|
||||
|
||||
# Delete the actual file from disk (best-effort).
|
||||
try:
|
||||
if resolved_path.exists():
|
||||
resolved_path.unlink()
|
||||
debug(f"Deleted file: {resolved_path}")
|
||||
else:
|
||||
debug(f"File not found on disk: {resolved_path}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return True
|
||||
except Exception as exc:
|
||||
debug(f"delete_file failed: {exc}")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user