Library indexing and filesystem scanner with incremental rescans
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.noroutes.net/NoRoutes/vibetunes/internal/database"
|
||||
"git.noroutes.net/NoRoutes/vibetunes/internal/metadata"
|
||||
)
|
||||
|
||||
func openTestDB(t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
db, err := database.Open(filepath.Join(t.TempDir(), "test.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("database.Open: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { db.Close() })
|
||||
return db
|
||||
}
|
||||
|
||||
func makeMediaFile(t *testing.T, db *sql.DB, relPath string) *database.MediaFile {
|
||||
t.Helper()
|
||||
folder, err := database.CreateMusicFolder(db, "Music", "/music", "music")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMusicFolder: %v", err)
|
||||
}
|
||||
mf, err := database.CreateMediaFile(db, folder.ID, relPath, 1000, 0, "hash1", "flac", "song", 180, 900, 44100, 2)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMediaFile: %v", err)
|
||||
}
|
||||
return mf
|
||||
}
|
||||
|
||||
func TestIndexFileCreatesArtistAlbumSong(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
mf := makeMediaFile(t, db, "Artist/Album/01 - Song.flac")
|
||||
|
||||
tags := &metadata.Tags{
|
||||
Title: "Song One",
|
||||
Album: "Great Album",
|
||||
AlbumArtist: "Great Artist",
|
||||
Artists: []string{"Great Artist", "Featured Person"},
|
||||
Genres: []string{"Rock", "Indie"},
|
||||
TrackNumber: 1,
|
||||
Year: 2020,
|
||||
}
|
||||
|
||||
song, err := IndexFile(db, mf, tags)
|
||||
if err != nil {
|
||||
t.Fatalf("IndexFile: %v", err)
|
||||
}
|
||||
if song.Title != "Song One" {
|
||||
t.Errorf("Title = %q", song.Title)
|
||||
}
|
||||
if !song.AlbumID.Valid {
|
||||
t.Fatal("expected song to have an album")
|
||||
}
|
||||
|
||||
album, err := database.GetAlbum(db, song.AlbumID.String)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAlbum: %v", err)
|
||||
}
|
||||
if album.Title != "Great Album" {
|
||||
t.Errorf("Album title = %q", album.Title)
|
||||
}
|
||||
|
||||
names, err := database.ListSongArtistNames(db, song.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSongArtistNames: %v", err)
|
||||
}
|
||||
if len(names) != 2 || names[0] != "Great Artist" || names[1] != "Featured Person" {
|
||||
t.Errorf("song artists = %v", names)
|
||||
}
|
||||
|
||||
genres, err := database.ListGenres(db)
|
||||
if err != nil {
|
||||
t.Fatalf("ListGenres: %v", err)
|
||||
}
|
||||
if len(genres) != 2 {
|
||||
t.Errorf("genres = %v, want 2", genres)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexFileIsIdempotent(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
mf := makeMediaFile(t, db, "Artist/Album/01 - Song.flac")
|
||||
tags := &metadata.Tags{Title: "Song", Album: "Album", AlbumArtist: "Artist", Artists: []string{"Artist"}}
|
||||
|
||||
song1, err := IndexFile(db, mf, tags)
|
||||
if err != nil {
|
||||
t.Fatalf("IndexFile (1st): %v", err)
|
||||
}
|
||||
song2, err := IndexFile(db, mf, tags)
|
||||
if err != nil {
|
||||
t.Fatalf("IndexFile (2nd): %v", err)
|
||||
}
|
||||
if song1.ID != song2.ID {
|
||||
t.Errorf("song ID changed across reindex: %s -> %s", song1.ID, song2.ID)
|
||||
}
|
||||
if song1.AlbumID.String != song2.AlbumID.String {
|
||||
t.Errorf("album ID changed across reindex: %s -> %s", song1.AlbumID.String, song2.AlbumID.String)
|
||||
}
|
||||
|
||||
artists, err := database.ListSongArtistNames(db, song1.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSongArtistNames: %v", err)
|
||||
}
|
||||
if len(artists) != 1 {
|
||||
t.Errorf("reindexing duplicated song_artists rows: %v", artists)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexFileUsesFilenameWhenNoTitle(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
mf := makeMediaFile(t, db, "Unknown/Track.flac")
|
||||
song, err := IndexFile(db, mf, &metadata.Tags{})
|
||||
if err != nil {
|
||||
t.Fatalf("IndexFile: %v", err)
|
||||
}
|
||||
if song.Title != mf.RelativePath {
|
||||
t.Errorf("Title = %q, want fallback to relative path %q", song.Title, mf.RelativePath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListDirectory(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
folder, err := database.CreateMusicFolder(db, "Music", "/music", "music")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMusicFolder: %v", err)
|
||||
}
|
||||
for _, p := range []string{"ArtistA/Album1/01.flac", "ArtistA/Album1/02.flac", "ArtistA/Album2/01.flac", "ArtistB/Album3/01.flac"} {
|
||||
if _, err := database.CreateMediaFile(db, folder.ID, p, 1, 0, "h"+p, "flac", "song", 1, 1, 1, 1); err != nil {
|
||||
t.Fatalf("CreateMediaFile(%s): %v", p, err)
|
||||
}
|
||||
}
|
||||
|
||||
root, err := ListDirectory(db, folder.ID, "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListDirectory(root): %v", err)
|
||||
}
|
||||
if len(root) != 2 {
|
||||
t.Fatalf("root entries = %v, want 2 (ArtistA, ArtistB)", root)
|
||||
}
|
||||
for _, e := range root {
|
||||
if !e.Dir {
|
||||
t.Errorf("root entry %q should be a directory", e.Name)
|
||||
}
|
||||
}
|
||||
|
||||
sub, err := ListDirectory(db, folder.ID, "ArtistA")
|
||||
if err != nil {
|
||||
t.Fatalf("ListDirectory(ArtistA): %v", err)
|
||||
}
|
||||
if len(sub) != 2 {
|
||||
t.Fatalf("ArtistA entries = %v, want 2 (Album1, Album2)", sub)
|
||||
}
|
||||
|
||||
files, err := ListDirectory(db, folder.ID, "ArtistA/Album1")
|
||||
if err != nil {
|
||||
t.Fatalf("ListDirectory(ArtistA/Album1): %v", err)
|
||||
}
|
||||
if len(files) != 2 {
|
||||
t.Fatalf("Album1 entries = %v, want 2 files", files)
|
||||
}
|
||||
for _, e := range files {
|
||||
if e.Dir {
|
||||
t.Errorf("entry %q should be a file", e.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user