fix pipe
This commit is contained in:
@@ -330,9 +330,12 @@ class LocalStorageBackend(StorageBackend):
|
||||
except Exception:
|
||||
word_regex = None
|
||||
else:
|
||||
# Use word boundary for exact terms (backwards compatibility)
|
||||
# Use custom boundary that treats underscores as separators
|
||||
# \b treats _ as a word character, so "foo_bar" wouldn't match "bar" with \b
|
||||
try:
|
||||
word_regex = re.compile(r'\b' + re.escape(term) + r'\b', re.IGNORECASE)
|
||||
# Match if not preceded or followed by alphanumeric chars
|
||||
pattern = r'(?<![a-zA-Z0-9])' + re.escape(term) + r'(?![a-zA-Z0-9])'
|
||||
word_regex = re.compile(pattern, re.IGNORECASE)
|
||||
except Exception:
|
||||
word_regex = None
|
||||
|
||||
@@ -459,71 +462,19 @@ class LocalStorageBackend(StorageBackend):
|
||||
|
||||
if results:
|
||||
debug(f"Returning {len(results)} results from DB")
|
||||
return results
|
||||
else:
|
||||
debug("No results found in DB, falling back to filesystem scan")
|
||||
debug("No results found in DB")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
log(f"⚠️ Database search failed: {e}", file=sys.stderr)
|
||||
debug(f"DB search exception details: {e}")
|
||||
|
||||
# Fallback to filesystem search if database search fails or returns nothing
|
||||
debug("Starting filesystem scan...")
|
||||
recursive = kwargs.get("recursive", True)
|
||||
pattern = "**/*" if recursive else "*"
|
||||
|
||||
# Split query into terms for AND logic
|
||||
terms = [t.strip() for t in query_lower.replace(',', ' ').split() if t.strip()]
|
||||
if not terms:
|
||||
terms = [query_lower]
|
||||
|
||||
count = 0
|
||||
for file_path in search_dir.glob(pattern):
|
||||
if not file_path.is_file():
|
||||
continue
|
||||
lower_name = file_path.name.lower()
|
||||
if lower_name.endswith('.tags') or lower_name.endswith('.metadata') \
|
||||
or lower_name.endswith('.notes') or lower_name.endswith('.tags.txt'):
|
||||
continue
|
||||
|
||||
if not match_all:
|
||||
# Check if ALL terms are present in the filename
|
||||
# For single terms with wildcards, use fnmatch; otherwise use substring matching
|
||||
if len(terms) == 1 and ('*' in terms[0] or '?' in terms[0]):
|
||||
# Wildcard pattern matching for single term
|
||||
from fnmatch import fnmatch
|
||||
if not fnmatch(lower_name, terms[0]):
|
||||
continue
|
||||
else:
|
||||
# Substring matching for all terms (AND logic)
|
||||
if not all(term in lower_name for term in terms):
|
||||
continue
|
||||
|
||||
size_bytes = file_path.stat().st_size
|
||||
path_str = str(file_path)
|
||||
results.append({
|
||||
"name": file_path.stem,
|
||||
"title": file_path.stem,
|
||||
"ext": file_path.suffix.lstrip('.'),
|
||||
"path": path_str,
|
||||
"target": path_str,
|
||||
"origin": "local",
|
||||
"size": size_bytes,
|
||||
"size_bytes": size_bytes,
|
||||
})
|
||||
count += 1
|
||||
|
||||
if limit is not None and len(results) >= limit:
|
||||
break
|
||||
|
||||
debug(f"Filesystem scan found {count} matches")
|
||||
|
||||
return []
|
||||
|
||||
except Exception as exc:
|
||||
log(f"❌ Local search failed: {exc}", file=sys.stderr)
|
||||
raise
|
||||
|
||||
return results
|
||||
|
||||
|
||||
class HydrusStorageBackend(StorageBackend):
|
||||
"""File storage backend for Hydrus client."""
|
||||
|
||||
@@ -163,13 +163,25 @@ class LocalLibraryDB:
|
||||
# Use check_same_thread=False to allow multi-threaded access
|
||||
# This is safe because we're not sharing connections across threads;
|
||||
# each thread will get its own cursor
|
||||
self.connection = sqlite3.connect(str(self.db_path), check_same_thread=False)
|
||||
# Set a generous timeout to avoid "database is locked" errors during heavy concurrency
|
||||
self.connection = sqlite3.connect(str(self.db_path), check_same_thread=False, timeout=60.0)
|
||||
self.connection.row_factory = sqlite3.Row
|
||||
|
||||
# Enable Write-Ahead Logging (WAL) for better concurrency
|
||||
self.connection.execute("PRAGMA journal_mode=WAL")
|
||||
# Enable foreign keys
|
||||
self.connection.execute("PRAGMA foreign_keys = ON")
|
||||
|
||||
self._create_tables()
|
||||
logger.info(f"Database initialized at {self.db_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize database: {e}", exc_info=True)
|
||||
if self.connection:
|
||||
try:
|
||||
self.connection.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.connection = None
|
||||
raise
|
||||
|
||||
def _create_tables(self) -> None:
|
||||
@@ -1492,6 +1504,19 @@ class LocalLibrarySearchOptimizer:
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get playlist ID {playlist_id}: {e}")
|
||||
return None
|
||||
|
||||
def delete_playlist(self, playlist_id: int) -> bool:
|
||||
"""Delete a playlist by ID."""
|
||||
if not self.db:
|
||||
return False
|
||||
try:
|
||||
cursor = self.db.connection.cursor()
|
||||
cursor.execute("DELETE FROM playlists WHERE id = ?", (playlist_id,))
|
||||
self.db.connection.commit()
|
||||
return cursor.rowcount > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete playlist ID {playlist_id}: {e}")
|
||||
return False
|
||||
if not self.db:
|
||||
return []
|
||||
return self.db.search_by_tag(tag, limit)
|
||||
|
||||
Reference in New Issue
Block a user