mirror of
https://github.com/navidrome/navidrome.git
synced 2025-05-03 03:41:36 +03:00
* Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumDetails.js * Create DoubleRangeField.js * Update and rename DoubleRangeField.js to RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update index.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update en.json * Update en.json * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumSongs.js * Update ContextMenus.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongList.js * Update playlist_track_repository.go * Update 20230113000000_release_year.go * Update PlayButton.js * Update mediafile_repository.go * Update album.go * Update playlist_track_repository.go * Update playlist_track_repository.go * Update SongDatagrid.js * Update 20230113000000_release_year.go * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update AlbumDetails.js * Update AlbumSongs.js * Update AlbumSongs.js * Update RangeFieldDouble.js * Update SongDatagrid.js * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update AlbumSongs.js * Update AlbumSongs.js * Update mapping.go * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumSongs.js * Update en.json * Update SongDatagrid.js * Update SongDatagrid.js * Update metadata.go * Update mapping.go * Update AlbumDetails.js * Update AlbumGridView.js * Update RangeFieldDouble.js * Update mapping.go * Update metadata.go * Update mapping.go * Update AlbumDetails.js * Update 20230113000000_release_year.go * Update AlbumDetails.js * Update en.json * Update configuration.go * Update mapping.go * Update configuration.go * Update mediafile.go * Update metadata.go * Update RangeFieldDouble.js * Update 20230113000000_release_year.go * Update configuration.go * Update mapping.go * Update mediafile.go * Update mapping.go * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update 20230113000000_release_year.go * Update AlbumDetails.js * Update RangeFieldDouble.js * Update mapping.go * Update metadata.go * Update album.go * Update mediafile.go * Update mediafile.go * Update album.go * Update fields.go * Update mediafile_repository.go * Update playlist_track_repository.go * Update AlbumSongs.js * Update SongDatagrid.js * Update PlayButton.js * Update SongList.js * Update ContextMenus.js * Update SongDatagrid.js * Update metadata.go * Update ArtistShow.js * Update mapping.go * Update configuration.go * Update mapping.go * Update metadata.go * Update metadata.go * Update mapping.go * Update metadata.go * Update metadata.go * Update mapping.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update mapping.go * Update metadata.go * Update metadata.go * Update album.go * Update mediafile.go * Update AlbumDetails.js * Update AlbumSongs.js * Update album.go * Update mediafile.go * Update metadata.go * Update mediafile.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update album.go * Update mediafile.go * Update RangeFieldDouble.js * Update AlbumDetails.js * Update AlbumGridView.js * Update en.json * Update AlbumGridView.js * Update RangeFieldDouble.js * Update and rename 20230113000000_release_year.go to 20230113000000_release_date.go * Update album.go * Update mediafile.go * Update fields.go * Update playlist_track_repository.go * Update mediafile_repository.go * Update mapping.go * Update metadata.go * Update mapping.go * Update SongDatagrid.js * Update RangeFieldDouble.js * Update index.js * Update ContextMenus.js * Update PlayButton.js * Create FormatDate.js * Update SongList.js * Update AlbumDetails.js * Update AlbumSongs.js * Update AlbumSongs.js * Update en.json * Update AlbumDetails.js * Update album.go fixed conflict I think? * Update mediafile.go fixed conflict * Format with goimports * Update SongDatagrid.js only show Cat # in desktop view * Update metadata_internal_test.go * Update metadata_test.go * Delete test.mp3 * Add files via upload mp3 test file with Date, Original Date and Release Date * Update metadata_test.go * Update metadata_test.go * Update metadata_test.go * Update metadata_test.go * Update taglib_test.go * Delete test.mp3 * Add files via upload file with replaygain & dates * Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumSongs.js * Update ContextMenus.js * Update FormatDate.js * Update PlayButton.js * Update RangeFieldDouble.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update AlbumSongs.js * Fix formatting * Update mapping.go * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js prettier * Create RangeDoubleField.js rename of RangeFieldDouble.js * Update AlbumGridView.js RangeFieldDouble -> RangeDoubleField * Update mediafile.go AllOrNothing() -> allOrNothing() * Update metadata_internal_test.go getYear -> getDate * Update AlbumDetails.js wrote suggested changes * Update en.json Editions -> Releases & fixed the field name * Update configuration.go Rename Editions -> Releases * Update 20230113000000_release_date.go Editions -> Releases * Update album.go Editions -> Releases * Update mediafile.go Editions -> Releases * Update AlbumDetails.js Editions -> Releases * Update AlbumSongs.js Editions -> Releases * Update RangeDoubleField.js Editions -> Releases * Update SongDatagrid.js Editions -> Releases * Update index.js FormatFullDate and RangeDoubleField * Rename FormatDate.js to FormatFullDate.js * Delete RangeFieldDouble.js * Update mediafile.go AllOrNothing -> allOrNothing * Update mapping.go Editions -> Releases * Update AlbumDetails.js prettier * Update SongDatagrid.js showReleaseRow -> showReleaseDivider * Update AlbumSongs.js showReleaseRow -> showReleaseDivider for clarity * Update and rename 20230113000000_release_date.go to 20230515184510_add_release_date.go - rename the migration file - fixed the import to goose/v3 - additional db fields for original date & year * Update 20230515184510_add_release_date.go * Update fields.go * Update album.go * Update mediafile.go * Update mapping.go * Update AlbumDetails.js * Update en.json * Update AlbumDetails.js * Update AlbumDetails.js now hopefully prettier * Update mapping.go --------- Co-authored-by: Deluan <deluan@navidrome.org>
204 lines
5.7 KiB
Go
204 lines
5.7 KiB
Go
package persistence
|
|
|
|
import (
|
|
. "github.com/Masterminds/squirrel"
|
|
"github.com/deluan/rest"
|
|
"github.com/navidrome/navidrome/log"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/utils"
|
|
)
|
|
|
|
type playlistTrackRepository struct {
|
|
sqlRepository
|
|
sqlRestful
|
|
playlistId string
|
|
playlist *model.Playlist
|
|
playlistRepo *playlistRepository
|
|
}
|
|
|
|
func (r *playlistRepository) Tracks(playlistId string, refreshSmartPlaylist bool) model.PlaylistTrackRepository {
|
|
p := &playlistTrackRepository{}
|
|
p.playlistRepo = r
|
|
p.playlistId = playlistId
|
|
p.ctx = r.ctx
|
|
p.ormer = r.ormer
|
|
p.tableName = "playlist_tracks"
|
|
p.sortMappings = map[string]string{
|
|
"id": "playlist_tracks.id",
|
|
}
|
|
pls, err := r.Get(playlistId)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if refreshSmartPlaylist {
|
|
r.refreshSmartPlaylist(pls)
|
|
}
|
|
p.playlist = pls
|
|
return p
|
|
}
|
|
|
|
func (r *playlistTrackRepository) Count(options ...rest.QueryOptions) (int64, error) {
|
|
return r.count(Select().Where(Eq{"playlist_id": r.playlistId}), r.parseRestOptions(options...))
|
|
}
|
|
|
|
func (r *playlistTrackRepository) Read(id string) (interface{}, error) {
|
|
sel := r.newSelect().
|
|
LeftJoin("annotation on ("+
|
|
"annotation.item_id = media_file_id"+
|
|
" AND annotation.item_type = 'media_file'"+
|
|
" AND annotation.user_id = '"+userId(r.ctx)+"')").
|
|
Columns("starred", "starred_at", "play_count", "play_date", "rating", "f.*", "playlist_tracks.*").
|
|
Join("media_file f on f.id = media_file_id").
|
|
Where(And{Eq{"playlist_id": r.playlistId}, Eq{"id": id}})
|
|
var trk model.PlaylistTrack
|
|
err := r.queryOne(sel, &trk)
|
|
return &trk, err
|
|
}
|
|
|
|
func (r *playlistTrackRepository) GetAll(options ...model.QueryOptions) (model.PlaylistTracks, error) {
|
|
tracks, err := r.playlistRepo.loadTracks(r.newSelect(options...), r.playlistId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mfs := tracks.MediaFiles()
|
|
err = r.loadMediaFileGenres(&mfs)
|
|
if err != nil {
|
|
log.Error(r.ctx, "Error loading genres for playlist", "playlist", r.playlist.Name, "id", r.playlist.ID, err)
|
|
return nil, err
|
|
}
|
|
for i, mf := range mfs {
|
|
tracks[i].MediaFile.Genres = mf.Genres
|
|
}
|
|
return tracks, err
|
|
}
|
|
|
|
func (r *playlistTrackRepository) GetAlbumIDs(options ...model.QueryOptions) ([]string, error) {
|
|
sql := r.newSelect(options...).Columns("distinct mf.album_id").
|
|
Join("media_file mf on mf.id = media_file_id").
|
|
Where(Eq{"playlist_id": r.playlistId})
|
|
var ids []string
|
|
err := r.queryAll(sql, &ids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (r *playlistTrackRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) {
|
|
return r.GetAll(r.parseRestOptions(options...))
|
|
}
|
|
|
|
func (r *playlistTrackRepository) EntityName() string {
|
|
return "playlist_tracks"
|
|
}
|
|
|
|
func (r *playlistTrackRepository) NewInstance() interface{} {
|
|
return &model.PlaylistTrack{}
|
|
}
|
|
|
|
func (r *playlistTrackRepository) isTracksEditable() bool {
|
|
return r.playlistRepo.isWritable(r.playlistId) && !r.playlist.IsSmartPlaylist()
|
|
}
|
|
|
|
func (r *playlistTrackRepository) Add(mediaFileIds []string) (int, error) {
|
|
if !r.isTracksEditable() {
|
|
return 0, rest.ErrPermissionDenied
|
|
}
|
|
|
|
if len(mediaFileIds) > 0 {
|
|
log.Debug(r.ctx, "Adding songs to playlist", "playlistId", r.playlistId, "mediaFileIds", mediaFileIds)
|
|
} else {
|
|
return 0, nil
|
|
}
|
|
|
|
// Get next pos (ID) in playlist
|
|
sql := r.newSelect().Columns("max(id) as max").Where(Eq{"playlist_id": r.playlistId})
|
|
var max int
|
|
err := r.queryOne(sql, &max)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return len(mediaFileIds), r.playlistRepo.addTracks(r.playlistId, max+1, mediaFileIds)
|
|
}
|
|
|
|
func (r *playlistTrackRepository) addMediaFileIds(cond Sqlizer) (int, error) {
|
|
sq := Select("id").From("media_file").Where(cond).OrderBy("album_artist, album, release_date, disc_number, track_number")
|
|
var ids []string
|
|
err := r.queryAll(sq, &ids)
|
|
if err != nil {
|
|
log.Error(r.ctx, "Error getting tracks to add to playlist", err)
|
|
return 0, err
|
|
}
|
|
return r.Add(ids)
|
|
}
|
|
|
|
func (r *playlistTrackRepository) AddAlbums(albumIds []string) (int, error) {
|
|
return r.addMediaFileIds(Eq{"album_id": albumIds})
|
|
}
|
|
|
|
func (r *playlistTrackRepository) AddArtists(artistIds []string) (int, error) {
|
|
return r.addMediaFileIds(Eq{"album_artist_id": artistIds})
|
|
}
|
|
|
|
func (r *playlistTrackRepository) AddDiscs(discs []model.DiscID) (int, error) {
|
|
if len(discs) == 0 {
|
|
return 0, nil
|
|
}
|
|
var clauses Or
|
|
for _, d := range discs {
|
|
clauses = append(clauses, And{Eq{"album_id": d.AlbumID}, Eq{"release_date": d.ReleaseDate}, Eq{"disc_number": d.DiscNumber}})
|
|
}
|
|
return r.addMediaFileIds(clauses)
|
|
}
|
|
|
|
// Get ids from all current tracks
|
|
func (r *playlistTrackRepository) getTracks() ([]string, error) {
|
|
all := r.newSelect().Columns("media_file_id").Where(Eq{"playlist_id": r.playlistId}).OrderBy("id")
|
|
var ids []string
|
|
err := r.queryAll(all, &ids)
|
|
if err != nil {
|
|
log.Error(r.ctx, "Error querying current tracks from playlist", "playlistId", r.playlistId, err)
|
|
return nil, err
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (r *playlistTrackRepository) Delete(ids ...string) error {
|
|
if !r.isTracksEditable() {
|
|
return rest.ErrPermissionDenied
|
|
}
|
|
err := r.delete(And{Eq{"playlist_id": r.playlistId}, Eq{"id": ids}})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return r.playlistRepo.renumber(r.playlistId)
|
|
}
|
|
|
|
func (r *playlistTrackRepository) DeleteAll() error {
|
|
if !r.isTracksEditable() {
|
|
return rest.ErrPermissionDenied
|
|
}
|
|
err := r.delete(Eq{"playlist_id": r.playlistId})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return r.playlistRepo.renumber(r.playlistId)
|
|
}
|
|
|
|
func (r *playlistTrackRepository) Reorder(pos int, newPos int) error {
|
|
if !r.isTracksEditable() {
|
|
return rest.ErrPermissionDenied
|
|
}
|
|
ids, err := r.getTracks()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newOrder := utils.MoveString(ids, pos-1, newPos-1)
|
|
return r.playlistRepo.updatePlaylist(r.playlistId, newOrder)
|
|
}
|
|
|
|
var _ model.PlaylistTrackRepository = (*playlistTrackRepository)(nil)
|