diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-03 15:19:22 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-03 15:19:22 +0300 |
| commit | 687b86cdeef2192272f0945ddd496322a9113f9e (patch) | |
| tree | 4c570f3803d08560d5f58cee74b3637a5530d23f /internal/service | |
| parent | c397aabc09d35f95418351776cd73305a4c28fba (diff) | |
Fix media info and detached playback state
Diffstat (limited to 'internal/service')
| -rw-r--r-- | internal/service/media_browse.go | 31 | ||||
| -rw-r--r-- | internal/service/media_test.go | 44 |
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() |
