Fix playlist star filter

This commit is contained in:
Deluan Quintão 2025-05-29 20:35:47 -04:00
parent a2d764d5bc
commit 588b6be075
6 changed files with 87 additions and 5 deletions

View File

@ -9,6 +9,8 @@ import (
)
type Playlist struct {
Annotations `structs:"-" hash:"ignore"`
ID string `structs:"id" json:"id"`
Name string `structs:"name" json:"name"`
Comment string `structs:"comment" json:"comment"`
@ -92,6 +94,7 @@ type Playlists []Playlist
type PlaylistRepository interface {
ResourceRepository
AnnotatedRepository
CountAll(options ...QueryOptions) (int64, error)
Exists(id string) (bool, error)
Put(pls *Playlist) error

View File

@ -51,12 +51,15 @@ func NewPlaylistRepository(ctx context.Context, db dbx.Builder) model.PlaylistRe
r := &playlistRepository{}
r.ctx = ctx
r.db = db
r.tableName = "playlist"
r.registerModel(&model.Playlist{}, map[string]filterFunc{
"q": playlistFilter,
"smart": smartPlaylistFilter,
"q": playlistFilter,
"smart": smartPlaylistFilter,
"starred": booleanFilter,
})
r.setSortMappings(map[string]string{
"owner_name": "owner_name",
"starred_at": "starred, starred_at",
})
return r
}
@ -87,7 +90,9 @@ func (r *playlistRepository) userFilter() Sqlizer {
}
func (r *playlistRepository) CountAll(options ...model.QueryOptions) (int64, error) {
sq := Select().Where(r.userFilter())
sq := r.newSelect()
sq = r.withAnnotation(sq, r.tableName+".id")
sq = sq.Where(r.userFilter())
return r.count(sq, options...)
}
@ -198,8 +203,9 @@ func (r *playlistRepository) GetAll(options ...model.QueryOptions) (model.Playli
}
func (r *playlistRepository) selectPlaylist(options ...model.QueryOptions) SelectBuilder {
return r.newSelect(options...).Join("user on user.id = owner_id").
query := r.newSelect(options...).Join("user on user.id = owner_id").
Columns(r.tableName+".*", "user.user_name as owner_name")
return r.withAnnotation(query, r.tableName+".id")
}
func (r *playlistRepository) refreshSmartPlaylist(pls *model.Playlist) bool {

View File

@ -4,6 +4,7 @@ import (
"context"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
@ -110,6 +111,45 @@ var _ = Describe("PlaylistRepository", func() {
Expect(all[0].ID).To(Equal(plsBest.ID))
Expect(all[1].ID).To(Equal(plsCool.ID))
})
It("filters starred playlists", func() {
Expect(repo.SetStar(true, plsBest.ID)).To(Succeed())
all, err := repo.GetAll(model.QueryOptions{Filters: sq.Eq{"starred": true}})
Expect(err).ToNot(HaveOccurred())
Expect(all).To(HaveLen(1))
Expect(all[0].ID).To(Equal(plsBest.ID))
Expect(repo.SetStar(false, plsBest.ID)).To(Succeed())
})
It("counts starred playlists", func() {
Expect(repo.SetStar(true, plsCool.ID)).To(Succeed())
count, err := repo.CountAll(model.QueryOptions{Filters: sq.Eq{"starred": true}})
Expect(err).ToNot(HaveOccurred())
Expect(count).To(Equal(int64(1)))
Expect(repo.SetStar(false, plsCool.ID)).To(Succeed())
})
})
Describe("SetStar", func() {
It("should star a playlist", func() {
Expect(repo.SetStar(true, plsBest.ID)).To(Succeed())
updated, err := repo.Get(plsBest.ID)
Expect(err).ToNot(HaveOccurred())
Expect(updated.Starred).To(BeTrue())
Expect(updated.StarredAt).ToNot(BeNil())
})
It("should unstar a playlist", func() {
Expect(repo.SetStar(false, plsBest.ID)).To(Succeed())
updated, err := repo.Get(plsBest.ID)
Expect(err).ToNot(HaveOccurred())
Expect(updated.Starred).To(BeFalse())
})
})
Context("Smart Playlists", func() {

View File

@ -138,6 +138,18 @@ func (api *Router) setStar(ctx context.Context, star bool, ids ...string) error
event = event.With("artist", id)
continue
}
exist, err = tx.Playlist(ctx).Exists(id)
if err != nil {
return err
}
if exist {
err = tx.Playlist(ctx).SetStar(star, id)
if err != nil {
return err
}
event = event.With("playlist", id)
continue
}
err = tx.MediaFile(ctx).SetStar(star, id)
if err != nil {
return err

View File

@ -60,6 +60,7 @@ const PlaylistsSubMenu = ({ state, setState, sidebarIsOpen, dense }) => {
perPage: config.maxSidebarPlaylists,
},
sort: { field: 'name' },
filter: config.enableFavourites ? { starred: true } : {},
},
})

View File

@ -10,7 +10,13 @@ import { useTranslate } from 'react-admin'
import { useCallback, useState, useEffect } from 'react'
import Lightbox from 'react-image-lightbox'
import 'react-image-lightbox/style.css'
import { CollapsibleComment, DurationField, SizeField } from '../common'
import {
CollapsibleComment,
DurationField,
SizeField,
LoveButton,
} from '../common'
import config from '../config'
import subsonic from '../subsonic'
const useStyles = makeStyles(
@ -68,6 +74,10 @@ const useStyles = makeStyles(
coverLoading: {
opacity: 0.5,
},
loveButton: {
top: theme.spacing(-0.2),
left: theme.spacing(0.5),
},
title: {
overflow: 'hidden',
textOverflow: 'ellipsis',
@ -146,6 +156,16 @@ const PlaylistDetails = (props) => {
className={classes.title}
>
{record.name || translate('ra.page.loading')}
{config.enableFavourites && (
<LoveButton
className={classes.loveButton}
record={record}
resource={'playlist'}
size={isDesktop ? 'default' : 'small'}
aria-label="love"
color="primary"
/>
)}
</Typography>
<Typography component="p" className={classes.stats}>
{record.songCount ? (