Store MusicFolder as a library in DB

This commit is contained in:
Deluan 2024-05-07 17:29:45 +02:00 committed by Deluan Quintão
parent 081ef85db6
commit 477bcaee58
5 changed files with 136 additions and 22 deletions

View File

@ -0,0 +1,71 @@
package migrations
import (
"context"
"database/sql"
"fmt"
"github.com/navidrome/navidrome/conf"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigrationContext(upAddLibraryTable, downAddLibraryTable)
}
func upAddLibraryTable(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
create table library (
id integer primary key autoincrement,
name text not null unique,
path text not null unique,
remote_path text null default '',
last_scan_at datetime not null default '0000-00-00 00:00:00',
updated_at datetime not null default current_timestamp,
created_at datetime not null default current_timestamp
);`)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, fmt.Sprintf(`
insert into library(id, name, path, last_scan_at) values(1, 'Music Library', '%s', current_timestamp);
delete from property where id like 'LastScan-%%';
`, conf.Server.MusicFolder))
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, `
alter table media_file add column library_id integer not null default 1
references library(id) on delete cascade;
alter table album add column library_id integer not null default 1
references library(id) on delete cascade;
create table if not exists library_artist
(
library_id integer not null default 1
references library(id)
on delete cascade,
artist_id varchar not null default null
references artist(id)
on delete cascade,
constraint library_artist_ux
unique (library_id, artist_id)
);
insert into library_artist(library_id, artist_id) select 1, id from artist;
`)
return err
}
func downAddLibraryTable(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
alter table media_file drop column library_id;
alter table album drop column library_id;
drop table library_artist;
drop table library;
`)
return err
}

View File

@ -3,12 +3,17 @@ package model
import (
"io/fs"
"os"
"time"
)
type Library struct {
ID int32
Name string
Path string
ID int
Name string
Path string
RemotePath string
LastScanAt time.Time
UpdatedAt time.Time
CreatedAt time.Time
}
func (f Library) FS() fs.FS {
@ -18,6 +23,7 @@ func (f Library) FS() fs.FS {
type Libraries []Library
type LibraryRepository interface {
Get(id int32) (*Library, error)
GetAll() (Libraries, error)
Get(id int) (*Library, error)
Put(*Library) error
GetAll(...QueryOptions) (Libraries, error)
}

View File

@ -2,33 +2,57 @@ package persistence
import (
"context"
"time"
"github.com/navidrome/navidrome/conf"
. "github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/model"
"github.com/pocketbase/dbx"
)
type libraryRepository struct {
ctx context.Context
sqlRepository
sqlRestful
}
func NewLibraryRepository(ctx context.Context, _ dbx.Builder) model.LibraryRepository {
return &libraryRepository{ctx}
func NewLibraryRepository(ctx context.Context, db dbx.Builder) model.LibraryRepository {
r := &libraryRepository{}
r.ctx = ctx
r.db = db
r.tableName = "library"
return r
}
func (r *libraryRepository) Get(int32) (*model.Library, error) {
library := hardCoded()
return &library, nil
func (r *libraryRepository) Get(id int) (*model.Library, error) {
sq := r.newSelect().Columns("*").Where(Eq{"id": id})
var res model.Library
err := r.queryOne(sq, &res)
return &res, err
}
func (*libraryRepository) GetAll() (model.Libraries, error) {
return model.Libraries{hardCoded()}, nil
func (r *libraryRepository) Put(l *model.Library) error {
cols := map[string]any{
"name": l.Name,
"path": l.Path,
"remote_path": l.RemotePath,
"last_scan_at": l.LastScanAt,
"updated_at": time.Now(),
}
if l.ID != 0 {
cols["id"] = l.ID
}
sq := Insert(r.tableName).SetMap(cols).
Suffix(`ON CONFLICT(id) DO UPDATE set name = excluded.name, path = excluded.path,
remote_path = excluded.remote_path, last_scan_at = excluded.last_scan_at`)
_, err := r.executeSQL(sq)
return err
}
func hardCoded() model.Library {
library := model.Library{ID: 0, Path: conf.Server.MusicFolder}
library.Name = "Music Library"
return library
func (r *libraryRepository) GetAll(ops ...model.QueryOptions) (model.Libraries, error) {
sq := r.newSelect(ops...).Columns("*")
res := model.Libraries{}
err := r.queryAll(sq, &res)
return res, err
}
var _ model.LibraryRepository = (*libraryRepository)(nil)

View File

@ -16,6 +16,10 @@ import (
func initialSetup(ds model.DataStore) {
_ = ds.WithTx(func(tx model.DataStore) error {
if err := createOrUpdateMusicFolder(ds); err != nil {
return err
}
properties := ds.Property(context.TODO())
_, err := properties.Get(consts.InitialSetupFlagKey)
if err == nil {
@ -112,3 +116,12 @@ func checkExternalCredentials() {
}
}
}
func createOrUpdateMusicFolder(ds model.DataStore) error {
lib := model.Library{ID: 1, Name: "Music Library", Path: conf.Server.MusicFolder}
err := ds.Library(context.TODO()).Put(&lib)
if err != nil {
log.Error("Could not access Library table", err)
}
return err
}

View File

@ -20,7 +20,7 @@ func (api *Router) GetMusicFolders(r *http.Request) (*responses.Subsonic, error)
libraries, _ := api.ds.Library(r.Context()).GetAll()
folders := make([]responses.MusicFolder, len(libraries))
for i, f := range libraries {
folders[i].Id = f.ID
folders[i].Id = int32(f.ID)
folders[i].Name = f.Name
}
response := newResponse()
@ -30,7 +30,7 @@ func (api *Router) GetMusicFolders(r *http.Request) (*responses.Subsonic, error)
func (api *Router) getArtistIndex(r *http.Request, libId int, ifModifiedSince time.Time) (*responses.Indexes, error) {
ctx := r.Context()
folder, err := api.ds.Library(ctx).Get(int32(libId))
folder, err := api.ds.Library(ctx).Get(libId)
if err != nil {
log.Error(ctx, "Error retrieving Library", "id", libId, err)
return nil, err
@ -68,7 +68,7 @@ func (api *Router) getArtistIndex(r *http.Request, libId int, ifModifiedSince ti
func (api *Router) GetIndexes(r *http.Request) (*responses.Subsonic, error) {
p := req.Params(r)
musicFolderId := p.IntOr("musicFolderId", 0)
musicFolderId := p.IntOr("musicFolderId", 1)
ifModifiedSince := p.TimeOr("ifModifiedSince", time.Time{})
res, err := api.getArtistIndex(r, musicFolderId, ifModifiedSince)
@ -83,7 +83,7 @@ func (api *Router) GetIndexes(r *http.Request) (*responses.Subsonic, error) {
func (api *Router) GetArtists(r *http.Request) (*responses.Subsonic, error) {
p := req.Params(r)
musicFolderId := p.IntOr("musicFolderId", 0)
musicFolderId := p.IntOr("musicFolderId", 1)
res, err := api.getArtistIndex(r, musicFolderId, time.Time{})
if err != nil {
return nil, err