Scanner improvements.

This commit is contained in:
2026-02-14 05:03:04 +00:00
parent 47b690b4da
commit 38651241d7
2 changed files with 66 additions and 4 deletions
+20 -1
View File
@@ -764,7 +764,7 @@ function formatMatchedPeople(people: PersonMatch[]): MatchedPerson[] {
let searchTimeout: ReturnType<typeof setTimeout> | null = null;
const MAX_RESULTS = 100;
// Watch for detail page entry to focus the first interactive element
// Watch for detail page entry/exit to manage focus
watch(selectedItem, (item, oldItem) => {
if (item && !oldItem) {
// Skip auto-focus if we have a specific episode to focus on (from search)
@@ -773,6 +773,25 @@ watch(selectedItem, (item, oldItem) => {
}
// Entering detail page - focus Play button (row 2, col 0) after transition
focusAt(2, 0, 150);
} else if (!item && oldItem) {
// Leaving detail page (browser back, Escape, etc.) - restore focus to the item card
const page = currentView.value === 'series' ? 'series' : 'movies';
restoreFocusForPage(page);
}
});
// Focus search input by default on initial movies page load
let initialFocusDone = false;
watch([mediaIndex, currentView, searchQuery, selectedItem], ([index, view, query, item]) => {
if (!initialFocusDone && index && view === 'movies' && !query && !item) {
initialFocusDone = true;
// Focus search input on first movies page load
setTimeout(() => {
const searchInput = document.querySelector('.search-input') as HTMLInputElement;
if (searchInput) {
searchInput.focus();
}
}, 100);
}
});
+46 -3
View File
@@ -174,15 +174,30 @@ async def _discover_downloads(task_id: str) -> List[ParsedContent]:
)
)
# Media container folder names (indicates the parent is a single media item)
MEDIA_CONTAINER_DIRS = {"BDMV", "VIDEO_TS", "HVDVD_TS"}
VIDEO_EXTENSIONS = {
".mkv",
".mp4",
".avi",
".m4v",
".mov",
".wmv",
".flv",
".webm",
".ts",
".m2ts",
}
async def _walk(directory: Path) -> None:
nonlocal dirs_visited
ap = AsyncPath(directory)
if not await ap.is_dir():
return
has_child_dirs = False
child_dirs: list[Path] = []
child_files: list[Path] = []
is_media_container = False
try:
for item_async in ap.iterdir():
@@ -193,7 +208,9 @@ async def _discover_downloads(task_id: str) -> List[ParsedContent]:
continue
if await AsyncPath(item).is_dir():
has_child_dirs = True
# Check if this is a BluRay/DVD structure
if item.name.upper() in MEDIA_CONTAINER_DIRS:
is_media_container = True
child_dirs.append(item)
else:
child_files.append(item)
@@ -201,7 +218,20 @@ async def _discover_downloads(task_id: str) -> List[ParsedContent]:
logger.debug("Cannot list directory: %s", directory)
return
if has_child_dirs:
# If directory contains BDMV/VIDEO_TS, treat entire directory as a single download
if is_media_container:
relpath = make_relative_path(str(directory), media_root_str)
try:
stat_info = await ap.stat()
mtime = int(stat_info.st_mtime)
except OSError:
return
if relpath not in _seen_mtimes or _seen_mtimes[relpath] != mtime:
_seen_mtimes[relpath] = mtime
downloads.append(await parse_download(directory))
return
if child_dirs:
# Branch directory — log it and recurse into subdirectories
dirs_visited += 1
rel = make_relative_path(str(directory), media_root_str) or str(directory)
@@ -212,6 +242,19 @@ async def _discover_downloads(task_id: str) -> List[ParsedContent]:
await _walk(child)
# Yield control periodically so WS messages flush
await asyncio.sleep(0)
# Also process any video files directly in this directory
for child_file in child_files:
if child_file.suffix.lower() in VIDEO_EXTENSIONS:
relpath = make_relative_path(str(child_file), media_root_str)
try:
stat_info = await AsyncPath(child_file).stat()
mtime = int(stat_info.st_mtime)
except OSError:
continue
if relpath in _seen_mtimes and _seen_mtimes[relpath] == mtime:
continue
_seen_mtimes[relpath] = mtime
downloads.append(await parse_download(child_file))
else:
# Leaf directory — treat the directory itself as a download
relpath = make_relative_path(str(directory), media_root_str)