summaryrefslogtreecommitdiff
path: root/internal/service
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-03 15:19:22 +0300
committerPaul Buetow <paul@buetow.org>2026-05-03 15:19:22 +0300
commit687b86cdeef2192272f0945ddd496322a9113f9e (patch)
tree4c570f3803d08560d5f58cee74b3637a5530d23f /internal/service
parentc397aabc09d35f95418351776cd73305a4c28fba (diff)
Fix media info and detached playback state
Diffstat (limited to 'internal/service')
-rw-r--r--internal/service/media_browse.go31
-rw-r--r--internal/service/media_test.go44
2 files changed, 73 insertions, 2 deletions
diff --git a/internal/service/media_browse.go b/internal/service/media_browse.go
index 0e3a7de..de5b14e 100644
--- a/internal/service/media_browse.go
+++ b/internal/service/media_browse.go
@@ -277,6 +277,9 @@ func (s *mediaService) GetSetCover(ctx context.Context, setID int64, folder stri
FileSize: info.Size(),
}, nil
}
+ if fr, ok := folderCoverFile(baseDir); ok {
+ return fr, nil
+ }
media, err := s.store.ListMedia(ctx, repository.MediaFilter{SetID: &setID})
if err != nil {
@@ -413,9 +416,11 @@ func (s *mediaService) BrowseSet(ctx context.Context, setID, userID int64, paren
items = append(items, fc.files[0])
} else {
subPath := filepath.Join(parent, name)
- coverPath := filepath.Join(filepath.Clean(filepath.Join(s.mediaRoot, set.RootPath, filepath.FromSlash(subPath))), ".cover.jpg")
+ folderDir := filepath.Clean(filepath.Join(s.mediaRoot, set.RootPath, filepath.FromSlash(subPath)))
+ coverPath := filepath.Join(folderDir, ".cover.jpg")
_, err := os.Stat(coverPath)
- hasCover := err == nil || randomFolderThumbnail(media, filepath.ToSlash(subPath)) != ""
+ _, hasDirectCover := folderCoverFile(folderDir)
+ hasCover := err == nil || hasDirectCover || randomFolderThumbnail(media, filepath.ToSlash(subPath)) != ""
folders = append(folders, BrowseFolder{Name: name, HasCover: hasCover})
}
}
@@ -452,3 +457,25 @@ func randomFolderThumbnail(media []model.Media, folder string) string {
}
return candidates[mrand.Intn(len(candidates))]
}
+
+func folderCoverFile(dir string) (*FileResult, bool) {
+ entries, err := os.ReadDir(dir)
+ if err != nil {
+ return nil, false
+ }
+ for _, entry := range entries {
+ if entry.IsDir() {
+ continue
+ }
+ switch strings.ToLower(entry.Name()) {
+ case "cover.jpg", "cover.jpeg", "cover.png", "cover.gif", "folder.jpg", "folder.jpeg", "folder.png", "folder.gif":
+ info, err := entry.Info()
+ if err != nil {
+ return nil, false
+ }
+ path := filepath.Join(dir, entry.Name())
+ return &FileResult{Path: path, FileName: entry.Name(), FileSize: info.Size()}, true
+ }
+ }
+ return nil, false
+}
diff --git a/internal/service/media_test.go b/internal/service/media_test.go
index 4163066..b14568c 100644
--- a/internal/service/media_test.go
+++ b/internal/service/media_test.go
@@ -1963,6 +1963,50 @@ func TestMediaService_FolderThumbnailFallback(t *testing.T) {
})
}
+func TestMediaService_GetSetCoverPrefersFolderCoverImage(t *testing.T) {
+ ctx := context.Background()
+ tmpDir := t.TempDir()
+ coverDir := filepath.Join(tmpDir, "library", "Book")
+ if err := os.MkdirAll(coverDir, 0o755); err != nil {
+ t.Fatalf("mkdir cover dir: %v", err)
+ }
+ coverPath := filepath.Join(coverDir, "cover.jpg")
+ if err := os.WriteFile(coverPath, []byte("cover"), 0o644); err != nil {
+ t.Fatalf("write cover: %v", err)
+ }
+ wrongThumb := filepath.Join(tmpDir, "wrong.jpg")
+ if err := os.WriteFile(wrongThumb, []byte("wrong"), 0o644); err != nil {
+ t.Fatalf("write wrong thumb: %v", err)
+ }
+
+ store := &repository.MockStore{
+ SetRepo: repository.MockSetRepo{
+ GetSetByIDFunc: func(ctx context.Context, id int64) (*model.Set, error) {
+ return &model.Set{ID: 1, RootPath: "library"}, nil
+ },
+ },
+ UserRepo: repository.MockUserRepo{
+ GetUserByIDFunc: func(ctx context.Context, id int64) (*model.User, error) {
+ return &model.User{ID: id, IsAdmin: true}, nil
+ },
+ },
+ MediaRepo: repository.MockMediaRepo{
+ ListMediaFunc: func(ctx context.Context, filter repository.MediaFilter) ([]model.Media, error) {
+ return []model.Media{{ID: 1, SetID: 1, RelPath: "Book/book.m4b", ThumbnailPath: wrongThumb, Type: model.MediaTypeAudio}}, nil
+ },
+ },
+ }
+ svc := NewMediaService(store, newMockClock(), tmpDir, nil, nil)
+
+ fr, err := svc.GetSetCover(ctx, 1, "Book", 1)
+ if err != nil {
+ t.Fatalf("get set cover: %v", err)
+ }
+ if fr.Path != coverPath {
+ t.Fatalf("expected direct cover %q, got %q", coverPath, fr.Path)
+ }
+}
+
func TestMediaService_GetThumbnail(t *testing.T) {
ctx := context.Background()
tmpDir := t.TempDir()