From 90792cb4e045ba4f3eb39e1d7957c2b7aaee1c56 Mon Sep 17 00:00:00 2001 From: RXWatcher <14085001+RXWatcher@users.noreply.github.com> Date: Sun, 24 May 2026 13:52:48 +0200 Subject: [PATCH] refactor(scanner): replace movieLibrary bool with typed walkMode Lets walkLogicalTree dispatch on multiple library shapes (video, movie, audiobook, podcast) without proliferating boolean flags. Behavior for existing video and movie libraries is unchanged; audiobook and podcast modes will be consumed by the upcoming audiobook.go and podcast.go parsers in later tasks of this sub-plan. walkModeFor() derives the mode from a media_folders.type string; unknown types default to walkModeVideo to preserve prior behavior for any caller still passing a raw type. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/scanner/scanner.go | 69 +++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index 6da29586..ac117e2f 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -232,6 +232,45 @@ func isPodcastLibraryType(libraryType string) bool { } } +// walkMode tells walkLogicalTree which file extensions to surface and +// which library-specific filename heuristics (sample/extra skipping) +// to apply. +type walkMode int + +const ( + walkModeVideo walkMode = iota // bare video walk: video extensions, no movie skipping + walkModeMovie // movie library: video extensions + sample/extra skipping + walkModeAudiobook // audiobook library: audio extensions, no skipping + walkModePodcast // podcast library: audio extensions, no skipping +) + +// walkModeFor derives a walkMode from a media_folders.type string. +// Unknown types default to walkModeVideo so existing call sites that +// pass arbitrary types preserve their prior behavior. +func walkModeFor(folderType string) walkMode { + switch { + case isMovieLibraryType(folderType): + return walkModeMovie + case isAudiobookLibraryType(folderType): + return walkModeAudiobook + case isPodcastLibraryType(folderType): + return walkModePodcast + default: + return walkModeVideo + } +} + +// acceptsExt reports whether the given lowercased extension belongs to +// the file types this walk mode is looking for. +func (m walkMode) acceptsExt(ext string) bool { + switch m { + case walkModeAudiobook, walkModePodcast: + return audioExtensions[ext] + default: + return videoExtensions[ext] + } +} + func canonicalWalkPath(path string) (string, error) { resolved, err := filepath.EvalSymlinks(path) if err != nil { @@ -252,7 +291,7 @@ func walkLogicalTree( ctx context.Context, logicalPath string, physicalPath string, - movieLibrary bool, + mode walkMode, visitedPhysicalDirs map[string]struct{}, filePaths *[]string, ) error { @@ -280,22 +319,22 @@ func walkLogicalTree( return nil } if targetInfo.IsDir() { - return walkLogicalTree(ctx, logicalPath, resolved, movieLibrary, visitedPhysicalDirs, filePaths) + return walkLogicalTree(ctx, logicalPath, resolved, mode, visitedPhysicalDirs, filePaths) } - if movieLibrary && shouldSkipMovieSupplementalFile(logicalPath) { + if mode == walkModeMovie && shouldSkipMovieSupplementalFile(logicalPath) { return nil } - if videoExtensions[strings.ToLower(filepath.Ext(logicalPath))] { + if mode.acceptsExt(strings.ToLower(filepath.Ext(logicalPath))) { *filePaths = append(*filePaths, logicalPath) } return nil } if !info.IsDir() { - if movieLibrary && shouldSkipMovieSupplementalFile(logicalPath) { + if mode == walkModeMovie && shouldSkipMovieSupplementalFile(logicalPath) { return nil } - if videoExtensions[strings.ToLower(filepath.Ext(logicalPath))] { + if mode.acceptsExt(strings.ToLower(filepath.Ext(logicalPath))) { *filePaths = append(*filePaths, logicalPath) } return nil @@ -314,7 +353,7 @@ func walkLogicalTree( if isIgnoredDirectoryPath(logicalPath) { return nil } - if movieLibrary && shouldSkipMovieSupplementalDir(logicalPath) { + if mode == walkModeMovie && shouldSkipMovieSupplementalDir(logicalPath) { return nil } @@ -345,31 +384,31 @@ func walkLogicalTree( continue } if targetInfo.IsDir() { - if err := walkLogicalTree(ctx, logicalChild, resolved, movieLibrary, visitedPhysicalDirs, filePaths); err != nil { + if err := walkLogicalTree(ctx, logicalChild, resolved, mode, visitedPhysicalDirs, filePaths); err != nil { return err } continue } - if movieLibrary && shouldSkipMovieSupplementalFile(logicalChild) { + if mode == walkModeMovie && shouldSkipMovieSupplementalFile(logicalChild) { continue } - if videoExtensions[strings.ToLower(filepath.Ext(entry.Name()))] { + if mode.acceptsExt(strings.ToLower(filepath.Ext(entry.Name()))) { *filePaths = append(*filePaths, logicalChild) } continue } if entry.IsDir() { - if err := walkLogicalTree(ctx, logicalChild, physicalChild, movieLibrary, visitedPhysicalDirs, filePaths); err != nil { + if err := walkLogicalTree(ctx, logicalChild, physicalChild, mode, visitedPhysicalDirs, filePaths); err != nil { return err } continue } - if movieLibrary && shouldSkipMovieSupplementalFile(logicalChild) { + if mode == walkModeMovie && shouldSkipMovieSupplementalFile(logicalChild) { continue } - if videoExtensions[strings.ToLower(filepath.Ext(entry.Name()))] { + if mode.acceptsExt(strings.ToLower(filepath.Ext(entry.Name()))) { *filePaths = append(*filePaths, logicalChild) } } @@ -380,7 +419,7 @@ func walkLogicalTree( func collectLogicalFilePaths(ctx context.Context, walkRoots []string, libraryType string) ([]string, error) { filePaths := make([]string, 0) visitedPhysicalDirs := make(map[string]struct{}) - movieLibrary := isMovieLibraryType(libraryType) + mode := walkModeFor(libraryType) for _, rootPath := range walkRoots { if ctx != nil { @@ -392,7 +431,7 @@ func collectLogicalFilePaths(ctx context.Context, walkRoots []string, libraryTyp if cleanRoot == "" || cleanRoot == "." { continue } - if err := walkLogicalTree(ctx, cleanRoot, cleanRoot, movieLibrary, visitedPhysicalDirs, &filePaths); err != nil { + if err := walkLogicalTree(ctx, cleanRoot, cleanRoot, mode, visitedPhysicalDirs, &filePaths); err != nil { return nil, err } }