feat(server/m7-353): coverart.FindSidecar shared helper + tests
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
// Package coverart finds and fetches album cover artwork.
|
||||||
|
//
|
||||||
|
// Two-layer model: a directory-walking sidecar lookup (cover.jpg / folder.jpg
|
||||||
|
// next to the audio files) and a MusicBrainz Cover Art Archive (MBCAA)
|
||||||
|
// fallback for albums with an MBID but no sidecar. The Enricher orchestrates
|
||||||
|
// the two; the API and Subsonic packages reuse the sidecar helper directly.
|
||||||
|
package coverart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SidecarNames is the lookup order for cover art living next to audio files.
|
||||||
|
// Matches common library conventions: cover.* wins over folder.*; JPEG wins
|
||||||
|
// over PNG when both are present.
|
||||||
|
var SidecarNames = []string{
|
||||||
|
"cover.jpg", "cover.jpeg", "cover.png",
|
||||||
|
"folder.jpg", "folder.jpeg", "folder.png",
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindSidecar returns the absolute path to a sidecar cover image in
|
||||||
|
// albumDir, or "" when none of the conventional names are present as a
|
||||||
|
// regular file. Directories named like sidecar files are skipped.
|
||||||
|
func FindSidecar(albumDir string) string {
|
||||||
|
for _, name := range SidecarNames {
|
||||||
|
candidate := filepath.Join(albumDir, name)
|
||||||
|
info, err := os.Stat(candidate)
|
||||||
|
if err != nil || info.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package coverart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindSidecar_PrefersCoverJpgOverFolderJpg(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
must(t, os.WriteFile(filepath.Join(dir, "folder.jpg"), []byte("f"), 0o644))
|
||||||
|
must(t, os.WriteFile(filepath.Join(dir, "cover.jpg"), []byte("c"), 0o644))
|
||||||
|
got := FindSidecar(dir)
|
||||||
|
want := filepath.Join(dir, "cover.jpg")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindSidecar_PrefersJpegOverPng(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
must(t, os.WriteFile(filepath.Join(dir, "cover.png"), []byte("p"), 0o644))
|
||||||
|
must(t, os.WriteFile(filepath.Join(dir, "cover.jpeg"), []byte("j"), 0o644))
|
||||||
|
got := FindSidecar(dir)
|
||||||
|
want := filepath.Join(dir, "cover.jpeg")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindSidecar_EmptyDir_ReturnsEmpty(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
if got := FindSidecar(dir); got != "" {
|
||||||
|
t.Errorf("expected empty string, got %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindSidecar_DirectoryNamedCoverJpg_Skipped(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
must(t, os.MkdirAll(filepath.Join(dir, "cover.jpg"), 0o755))
|
||||||
|
must(t, os.WriteFile(filepath.Join(dir, "folder.png"), []byte("p"), 0o644))
|
||||||
|
got := FindSidecar(dir)
|
||||||
|
want := filepath.Join(dir, "folder.png")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func must(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("setup: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user