Speed up library search on large libraries
search_library_courses was walking via list_library_directory, which computes a full recursive file count (rglob) and thumbnail lookup for every course at every level - turning a search into an O(every file in every course) scan regardless of how few results actually match. Give search its own lightweight directory-only walk and defer the expensive per-course lookups until after a name match is confirmed, so cost now scales with matches found rather than total library size. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+41
-12
@@ -735,23 +735,52 @@ def get_library_root() -> str:
|
|||||||
def search_library_courses(dir_path: str, query: str) -> List[Dict[str, Any]]:
|
def search_library_courses(dir_path: str, query: str) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Recursively search the library for courses whose name contains `query`
|
Recursively search the library for courses whose name contains `query`
|
||||||
(case-insensitive). Walks via list_library_directory, so it reuses the
|
(case-insensitive), respecting hidden paths exactly like normal browsing.
|
||||||
exact same course/directory detection and hidden-path filtering as
|
|
||||||
normal browsing - a hidden course or folder never shows up in results.
|
Deliberately doesn't reuse list_library_directory for the walk itself:
|
||||||
|
that function computes media_count (a full recursive file count via
|
||||||
|
rglob) and thumbnail presence for *every* course at each level, which is
|
||||||
|
fine for showing one directory's worth of courses but turns a full-
|
||||||
|
library search into an O(every file in every course) scan. Here, the
|
||||||
|
directory walk only touches directory entries (cheap - iterdir, no file
|
||||||
|
stats), and the expensive per-course lookups only run for the handful of
|
||||||
|
courses whose name actually matches.
|
||||||
"""
|
"""
|
||||||
query_lower = query.lower()
|
query_lower = query.lower()
|
||||||
|
hidden_set = set(get_hidden_paths())
|
||||||
results: List[Dict[str, Any]] = []
|
results: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
def walk(path: str):
|
def walk(directory: Path):
|
||||||
level = list_library_directory(path)
|
try:
|
||||||
for item in level['items']:
|
entries = sorted(
|
||||||
if item['type'] == 'course':
|
(p for p in directory.iterdir() if p.is_dir() and not p.name.startswith('.')),
|
||||||
if query_lower in item['name'].lower():
|
key=lambda p: p.name.lower()
|
||||||
results.append(item)
|
)
|
||||||
else:
|
except (PermissionError, OSError):
|
||||||
walk(item['path'])
|
return
|
||||||
|
|
||||||
walk(dir_path)
|
for entry in entries:
|
||||||
|
if os.path.abspath(str(entry)) in hidden_set:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if _looks_like_course(entry):
|
||||||
|
if query_lower in entry.name.lower():
|
||||||
|
media_count = len([
|
||||||
|
f for f in entry.rglob('*')
|
||||||
|
if f.is_file() and f.suffix.lower() in VIDEO_EXTENSIONS | AUDIO_EXTENSIONS
|
||||||
|
])
|
||||||
|
results.append({
|
||||||
|
'type': 'course',
|
||||||
|
'name': entry.name,
|
||||||
|
'path': str(entry),
|
||||||
|
'media_files': media_count,
|
||||||
|
'hidden': False,
|
||||||
|
'has_thumbnail': find_course_thumbnail(str(entry)) is not None
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
walk(entry)
|
||||||
|
|
||||||
|
walk(Path(dir_path))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user