quintodrome/persistence/album_repository.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

239 lines
6.4 KiB
Go
Raw Normal View History

package persistence
2020-01-12 14:55:55 -09:00
import (
"context"
2023-12-08 15:29:16 -09:00
"encoding/json"
"fmt"
"strings"
2020-01-12 14:55:55 -09:00
. "github.com/Masterminds/squirrel"
"github.com/deluan/rest"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
2020-01-23 15:44:08 -09:00
"github.com/navidrome/navidrome/model"
"github.com/pocketbase/dbx"
2020-01-12 14:55:55 -09:00
)
type albumRepository struct {
sqlRepository
2020-01-12 14:55:55 -09:00
}
2023-12-08 15:29:16 -09:00
type dbAlbum struct {
*model.Album `structs:",flatten"`
Discs string `structs:"-" json:"discs"`
2023-12-08 15:29:16 -09:00
}
func (a *dbAlbum) PostScan() error {
if a.Discs != "" {
return json.Unmarshal([]byte(a.Discs), &a.Album.Discs)
2023-12-08 15:29:16 -09:00
}
return nil
2023-12-08 15:29:16 -09:00
}
func (a *dbAlbum) PostMapArgs(m map[string]any) error {
if len(a.Album.Discs) == 0 {
m["discs"] = "{}"
return nil
}
b, err := json.Marshal(a.Album.Discs)
if err != nil {
return err
}
m["discs"] = string(b)
return nil
}
type dbAlbums []dbAlbum
func (dba dbAlbums) toModels() model.Albums {
res := make(model.Albums, len(dba))
for i := range dba {
res[i] = *dba[i].Album
}
return res
}
func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumRepository {
2020-01-12 14:55:55 -09:00
r := &albumRepository{}
r.ctx = ctx
r.db = db
2024-09-17 12:59:55 -08:00
r.tableName = "album"
2024-09-09 15:45:02 -08:00
r.registerModel(&model.Album{}, map[string]filterFunc{
2021-07-16 14:18:22 -08:00
"id": idFilter(r.tableName),
2020-07-28 04:49:28 -08:00
"name": fullTextFilter,
"compilation": booleanFilter,
"artist_id": artistFilter,
"year": yearFilter,
"recently_played": recentlyPlayedFilter,
2020-08-14 09:35:28 -08:00
"starred": booleanFilter,
2021-04-07 07:04:36 -08:00
"has_rating": hasRatingFilter,
2024-09-17 12:59:55 -08:00
"genre_id": eqFilter,
2024-09-09 15:45:02 -08:00
})
2023-12-11 16:37:11 -09:00
if conf.Server.PreferSortTags {
r.sortMappings = map[string]string{
2023-12-14 17:45:47 -09:00
"name": "COALESCE(NULLIF(sort_album_name,''),order_album_name)",
"artist": "compilation asc, COALESCE(NULLIF(sort_album_artist_name,''),order_album_artist_name) asc, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc",
2024-09-09 15:45:02 -08:00
"album_artist": "compilation asc, COALESCE(NULLIF(sort_album_artist_name,''),order_album_artist_name) asc, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc",
2023-12-14 17:45:47 -09:00
"max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc",
"random": "random",
2023-12-14 17:45:47 -09:00
"recently_added": recentlyAddedSort(),
2024-09-19 09:05:26 -08:00
"starred_at": "starred, starred_at",
2023-12-14 17:45:47 -09:00
}
} else {
r.sortMappings = map[string]string{
"name": "order_album_name asc, order_album_artist_name asc",
"artist": "compilation asc, order_album_artist_name asc, order_album_name asc",
2024-09-09 15:45:02 -08:00
"album_artist": "compilation asc, order_album_artist_name asc, order_album_name asc",
2023-12-14 17:45:47 -09:00
"max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, order_album_name asc",
"random": "random",
2023-12-14 17:45:47 -09:00
"recently_added": recentlyAddedSort(),
2024-09-19 09:05:26 -08:00
"starred_at": "starred, starred_at",
2023-12-11 16:37:11 -09:00
}
}
2020-01-12 14:55:55 -09:00
return r
}
func recentlyAddedSort() string {
if conf.Server.RecentlyAddedByModTime {
return "updated_at"
}
return "created_at"
}
func recentlyPlayedFilter(string, interface{}) Sqlizer {
2020-07-28 04:49:28 -08:00
return Gt{"play_count": 0}
}
func hasRatingFilter(string, interface{}) Sqlizer {
2021-04-07 07:04:36 -08:00
return Gt{"rating": 0}
}
func yearFilter(_ string, value interface{}) Sqlizer {
return Or{
And{
Gt{"min_year": 0},
LtOrEq{"min_year": value},
GtOrEq{"max_year": value},
},
Eq{"max_year": value},
}
}
func artistFilter(_ string, value interface{}) Sqlizer {
return Like{"all_artist_ids": fmt.Sprintf("%%%s%%", value)}
2020-03-25 14:51:13 -08:00
}
func (r *albumRepository) CountAll(options ...model.QueryOptions) (int64, error) {
2021-07-16 16:20:33 -08:00
sql := r.newSelectWithAnnotation("album.id")
sql = r.withGenres(sql) // Required for filtering by genre
2021-07-16 15:41:49 -08:00
return r.count(sql, options...)
}
func (r *albumRepository) Exists(id string) (bool, error) {
2021-07-16 16:20:33 -08:00
return r.exists(Select().Where(Eq{"album.id": id}))
}
func (r *albumRepository) selectAlbum(options ...model.QueryOptions) SelectBuilder {
2021-07-16 16:20:33 -08:00
sql := r.newSelectWithAnnotation("album.id", options...).Columns("album.*")
if len(options) > 0 && options[0].Filters != nil {
s, _, _ := options[0].Filters.ToSql()
// If there's any reference of genre in the filter, joins with genre
if strings.Contains(s, "genre") {
sql = r.withGenres(sql)
// If there's no filter on genre_id, group the results by media_file.id
if !strings.Contains(s, "genre_id") {
sql = sql.GroupBy("album.id")
}
}
}
return sql
2020-01-12 14:55:55 -09:00
}
2020-01-14 18:22:34 -09:00
func (r *albumRepository) Get(id string) (*model.Album, error) {
2021-07-16 16:20:33 -08:00
sq := r.selectAlbum().Where(Eq{"album.id": id})
var dba dbAlbums
2023-12-08 15:29:16 -09:00
if err := r.queryAll(sq, &dba); err != nil {
2020-01-12 14:55:55 -09:00
return nil, err
}
2023-12-08 15:29:16 -09:00
if len(dba) == 0 {
return nil, model.ErrNotFound
}
res := dba.toModels()
err := loadAllGenres(r, res)
2021-07-16 13:15:34 -08:00
return &res[0], err
}
func (r *albumRepository) Put(m *model.Album) error {
2023-12-08 15:29:16 -09:00
_, err := r.put(m.ID, &dbAlbum{Album: m})
2021-07-16 13:15:34 -08:00
if err != nil {
return err
}
return r.updateGenres(m.ID, m.Genres)
2020-01-12 14:55:55 -09:00
}
2020-01-14 18:22:34 -09:00
func (r *albumRepository) GetAll(options ...model.QueryOptions) (model.Albums, error) {
res, err := r.GetAllWithoutGenres(options...)
if err != nil {
return nil, err
}
err = loadAllGenres(r, res)
return res, err
}
func (r *albumRepository) GetAllWithoutGenres(options ...model.QueryOptions) (model.Albums, error) {
r.resetSeededRandom(options)
2021-07-16 16:20:33 -08:00
sq := r.selectAlbum(options...)
var dba dbAlbums
2023-12-08 15:29:16 -09:00
err := r.queryAll(sq, &dba)
2021-07-16 13:15:34 -08:00
if err != nil {
return nil, err
}
return dba.toModels(), err
2020-01-12 14:55:55 -09:00
}
func (r *albumRepository) purgeEmpty() error {
2020-01-31 13:57:06 -09:00
del := Delete(r.tableName).Where("id not in (select distinct(album_id) from media_file)")
c, err := r.executeSQL(del)
if err == nil {
if c > 0 {
2020-01-31 13:57:06 -09:00
log.Debug(r.ctx, "Purged empty albums", "totalDeleted", c)
}
}
return err
2020-01-17 19:28:11 -09:00
}
2020-01-14 18:22:34 -09:00
func (r *albumRepository) Search(q string, offset int, size int) (model.Albums, error) {
var dba dbAlbums
2023-12-08 15:29:16 -09:00
err := r.doSearch(q, offset, size, &dba, "name")
2023-11-21 17:11:45 -09:00
if err != nil {
return nil, err
}
res := dba.toModels()
err = loadAllGenres(r, res)
2023-12-08 15:29:16 -09:00
return res, err
}
2020-01-13 11:41:14 -09:00
func (r *albumRepository) Count(options ...rest.QueryOptions) (int64, error) {
2024-09-09 15:45:02 -08:00
return r.CountAll(r.parseRestOptions(r.ctx, options...))
}
func (r *albumRepository) Read(id string) (interface{}, error) {
return r.Get(id)
}
func (r *albumRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) {
2024-09-09 15:45:02 -08:00
return r.GetAll(r.parseRestOptions(r.ctx, options...))
}
func (r *albumRepository) EntityName() string {
return "album"
}
func (r *albumRepository) NewInstance() interface{} {
return &model.Album{}
2020-01-12 14:55:55 -09:00
}
2020-01-14 18:22:34 -09:00
var _ model.AlbumRepository = (*albumRepository)(nil)
var _ model.ResourceRepository = (*albumRepository)(nil)