Add the Navidrome client module that connects to a Navidrome server via the Subsonic API, fetches artist and album data, and syncs it into the local SQLite database. - Add go-subsonic dependency for Subsonic API communication - Create internal/navidrome/client.go with NavidromeClient wrapper - NewClient constructor with token-based auth - Ping health check with HTTP status validation - GetArtists fetches all artists via getArtists endpoint - GetArtistAlbums fetches albums per artist via getArtist endpoint - Create internal/navidrome/sync.go with sync orchestration - SyncArtists upserts artists into artist_settings table - SyncAlbums fetches and stores albums for monitored artists - Add local_albums table (migration 003) with FK to artist_settings - Add LocalAlbum CRUD operations in internal/database/local_albums.go - Full test coverage: 19 tests across client and sync packages - All tests pass, go vet and go fmt clean
104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
package database
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
)
|
|
|
|
// SaveLocalAlbum inserts or replaces a local_albums row.
|
|
func SaveLocalAlbum(db *DB, album *LocalAlbum) error {
|
|
_, err := db.Conn().Exec(
|
|
"INSERT OR REPLACE INTO local_albums (id, artist_id, title) VALUES (?, ?, ?)",
|
|
album.ID, album.ArtistID, album.Title,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("save local album: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SaveLocalAlbumTx inserts or replaces a local_albums row within an existing transaction.
|
|
func SaveLocalAlbumTx(tx *sql.Tx, album *LocalAlbum) error {
|
|
_, err := tx.Exec(
|
|
"INSERT OR REPLACE INTO local_albums (id, artist_id, title) VALUES (?, ?, ?)",
|
|
album.ID, album.ArtistID, album.Title,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("save local album: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteLocalAlbumsByArtistTx removes all local_albums rows for a given artist within an existing transaction.
|
|
func DeleteLocalAlbumsByArtistTx(tx *sql.Tx, artistID string) error {
|
|
_, err := tx.Exec(
|
|
"DELETE FROM local_albums WHERE artist_id = ?",
|
|
artistID,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("delete local albums for artist %s: %w", artistID, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteLocalAlbumsByArtist removes all local_albums rows for a given artist.
|
|
func DeleteLocalAlbumsByArtist(db *DB, artistID string) error {
|
|
_, err := db.Conn().Exec(
|
|
"DELETE FROM local_albums WHERE artist_id = ?",
|
|
artistID,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("delete local albums for artist %s: %w", artistID, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetLocalAlbumsByArtist returns all local_albums for a given artist.
|
|
func GetLocalAlbumsByArtist(db *DB, artistID string) ([]LocalAlbum, error) {
|
|
rows, err := db.Conn().Query(
|
|
"SELECT id, artist_id, title FROM local_albums WHERE artist_id = ?",
|
|
artistID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query local albums for artist %s: %w", artistID, err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var results []LocalAlbum
|
|
for rows.Next() {
|
|
var a LocalAlbum
|
|
if err := rows.Scan(&a.ID, &a.ArtistID, &a.Title); err != nil {
|
|
return nil, fmt.Errorf("scan local album: %w", err)
|
|
}
|
|
results = append(results, a)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate local albums: %w", err)
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
// GetAllLocalAlbums returns all rows from local_albums.
|
|
func GetAllLocalAlbums(db *DB) ([]LocalAlbum, error) {
|
|
rows, err := db.Conn().Query(
|
|
"SELECT id, artist_id, title FROM local_albums",
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query all local albums: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var results []LocalAlbum
|
|
for rows.Next() {
|
|
var a LocalAlbum
|
|
if err := rows.Scan(&a.ID, &a.ArtistID, &a.Title); err != nil {
|
|
return nil, fmt.Errorf("scan local album: %w", err)
|
|
}
|
|
results = append(results, a)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate local albums: %w", err)
|
|
}
|
|
return results, nil
|
|
}
|