fix(scan): accept ebook and audio file targets
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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{{
|
||||
|
||||
Reference in New Issue
Block a user