fix(scan): accept ebook and audio file targets

This commit is contained in:
rxwatcher
2026-07-22 13:43:57 +02:00
parent bb53bd0652
commit b1545ae9f9
2 changed files with 55 additions and 16 deletions
+9 -3
View File
@@ -143,7 +143,7 @@ func normalizeTrigger(trigger string) string {
// ResolveVanishedPath resolves a change for a path that no longer exists on
// disk (a file deleted by an upgrade/replacement, or a removed directory) to a
// reconciling scan target. Paths with a supported video extension map to a
// reconciling scan target. Paths with a supported media extension map to a
// subtree scan of their parent directory; other paths map to a subtree scan of
// the path itself. The scoped scan marks the vanished files missing so stale
// versions stop being offered for playback.
@@ -177,7 +177,7 @@ func (r *Resolver) ResolveVanishedPath(ctx context.Context, path, trigger string
trigger = normalizeTrigger(trigger)
scope := cleanPath
if scanner.SupportsVideoFile(cleanPath) {
if supportsMediaFile(cleanPath) {
scope = filepath.Dir(cleanPath)
}
if filepath.Clean(scope) == filepath.Clean(matchedRoot) {
@@ -353,12 +353,18 @@ func ClassifyPath(targetPath, matchedRoot string) (string, error) {
if !info.Mode().IsRegular() {
return "", &RequestError{Status: http.StatusBadRequest, Code: "bad_request", Message: "Path must be a file or directory"}
}
if !scanner.SupportsVideoFile(targetPath) {
if !supportsMediaFile(targetPath) {
return "", &RequestError{Status: http.StatusBadRequest, Code: "bad_request", Message: "Unsupported media file extension"}
}
return ModeFile, nil
}
func supportsMediaFile(path string) bool {
return scanner.SupportsVideoFile(path) ||
scanner.SupportsAudioFile(path) ||
scanner.SupportsEbookFile(path)
}
func PathWithinRoot(targetPath, rootPath string) bool {
cleanTarget := filepath.Clean(targetPath)
cleanRoot := filepath.Clean(rootPath)
+46 -13
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"github.com/Silo-Server/silo-server/internal/catalog"
@@ -109,26 +110,31 @@ func TestResolverRejectsMissingSubtreeAtLibraryRoot(t *testing.T) {
}
}
func TestResolverResolvesVanishedFileToParentSubtree(t *testing.T) {
func TestResolverResolvesVanishedMediaFileToParentSubtree(t *testing.T) {
root := t.TempDir()
movieDir := filepath.Join(root, "Movie (2026)")
if err := os.Mkdir(movieDir, 0o755); err != nil {
t.Fatal(err)
}
vanished := filepath.Join(movieDir, "Movie (2026).mkv")
repo := &fakeFolderRepo{folders: []*models.MediaFolder{{
ID: 20,
Name: "Movies",
Name: "Media",
Enabled: true,
Paths: []string{root},
}}}
target, err := NewResolver(repo).ResolveVanishedPath(context.Background(), vanished, "autoscan")
if err != nil {
t.Fatalf("ResolveVanishedPath returned error: %v", err)
}
if target.Folder == nil || target.Folder.ID != 20 || target.Mode != ModeSubtree || target.Path != movieDir {
t.Fatalf("unexpected target: %#v", target)
for _, name := range []string{"Movie.mkv", "Book.epub", "Audiobook.m4b"} {
t.Run(name, func(t *testing.T) {
mediaDir := filepath.Join(root, strings.TrimSuffix(name, filepath.Ext(name)))
if err := os.Mkdir(mediaDir, 0o755); err != nil {
t.Fatal(err)
}
vanished := filepath.Join(mediaDir, name)
target, err := NewResolver(repo).ResolveVanishedPath(context.Background(), vanished, "autoscan")
if err != nil {
t.Fatalf("ResolveVanishedPath returned error: %v", err)
}
if target.Folder == nil || target.Folder.ID != 20 || target.Mode != ModeSubtree || target.Path != mediaDir {
t.Fatalf("unexpected target: %#v", target)
}
})
}
}
@@ -283,6 +289,33 @@ func TestResolverClassifiesVideoFile(t *testing.T) {
}
}
func TestResolverClassifiesAudioAndEbookFiles(t *testing.T) {
root := t.TempDir()
repo := &fakeFolderRepo{folders: []*models.MediaFolder{{
ID: 25,
Name: "Media",
Enabled: true,
Paths: []string{root},
}}}
for _, name := range []string{"Book.epub", "Book.fb2.zip", "Audiobook.m4b"} {
t.Run(name, func(t *testing.T) {
filePath := filepath.Join(root, name)
if err := os.WriteFile(filePath, []byte("test"), 0o644); err != nil {
t.Fatal(err)
}
target, err := NewResolver(repo).Resolve(context.Background(), Request{Path: filePath})
if err != nil {
t.Fatalf("Resolve returned error: %v", err)
}
if target.Mode != ModeFile || target.Path != filepath.Clean(filePath) {
t.Fatalf("unexpected target: %#v", target)
}
})
}
}
func TestResolverRejectsDisabledLibrary(t *testing.T) {
root := t.TempDir()
repo := &fakeFolderRepo{folders: []*models.MediaFolder{{