Fix/Optimized Playlist tracks deletion

This commit is contained in:
Deluan 2021-10-26 14:05:28 -04:00
parent fbd87ba577
commit 5dce499d6d
7 changed files with 57 additions and 25 deletions

View File

@ -111,6 +111,6 @@ type PlaylistTrackRepository interface {
AddAlbums(albumIds []string) (int, error) AddAlbums(albumIds []string) (int, error)
AddArtists(artistIds []string) (int, error) AddArtists(artistIds []string) (int, error)
AddDiscs(discs []DiscID) (int, error) AddDiscs(discs []DiscID) (int, error)
Delete(id string) error Delete(id ...string) error
Reorder(pos int, newPos int) error Reorder(pos int, newPos int) error
} }

View File

@ -89,10 +89,6 @@ func (r *playlistRepository) Put(p *model.Playlist) error {
} }
pls.UpdatedAt = time.Now() pls.UpdatedAt = time.Now()
// Save tracks for later and set it to nil, to avoid trying to save it to the DB
tracks := pls.Tracks
pls.Tracks = nil
id, err := r.put(pls.ID, pls) id, err := r.put(pls.ID, pls)
if err != nil { if err != nil {
return err return err
@ -104,7 +100,7 @@ func (r *playlistRepository) Put(p *model.Playlist) error {
return nil return nil
} }
// Only update tracks if they were specified // Only update tracks if they were specified
if tracks == nil { if len(pls.Tracks) == 0 {
return nil return nil
} }
return r.updateTracks(id, p.MediaFiles()) return r.updateTracks(id, p.MediaFiles())
@ -405,15 +401,24 @@ func (r *playlistRepository) removeOrphans() error {
} }
log.Debug(r.ctx, "Deleted tracks, now reordering", "id", pl.Id, "name", pl.Name, "deleted", n) log.Debug(r.ctx, "Deleted tracks, now reordering", "id", pl.Id, "name", pl.Name, "deleted", n)
// To reorganize the playlist, just add an empty list of new tracks // Renumber the playlist if any track was removed
tracks := r.Tracks(pl.Id) if err := r.renumber(pl.Id); err != nil {
if _, err := tracks.Add(nil); err != nil {
return err return err
} }
} }
return nil return nil
} }
func (r *playlistRepository) renumber(id string) error {
var ids []string
sql := Select("media_file_id").From("playlist_tracks").Where(Eq{"playlist_id": id}).OrderBy("id")
err := r.queryAll(sql, &ids)
if err != nil {
return err
}
return r.updatePlaylist(id, ids)
}
func (r *playlistRepository) isWritable(playlistId string) bool { func (r *playlistRepository) isWritable(playlistId string) bool {
usr := loggedUser(r.ctx) usr := loggedUser(r.ctx)
if usr.IsAdmin { if usr.IsAdmin {

View File

@ -164,18 +164,16 @@ func (r *playlistTrackRepository) getTracks() ([]string, error) {
return ids, nil return ids, nil
} }
func (r *playlistTrackRepository) Delete(id string) error { func (r *playlistTrackRepository) Delete(ids ...string) error {
if !r.isTracksEditable() { if !r.isTracksEditable() {
return rest.ErrPermissionDenied return rest.ErrPermissionDenied
} }
err := r.delete(And{Eq{"playlist_id": r.playlistId}, Eq{"id": id}}) err := r.delete(And{Eq{"playlist_id": r.playlistId}, Eq{"id": ids}})
if err != nil { if err != nil {
return err return err
} }
// To renumber the playlist return r.playlistRepo.renumber(r.playlistId)
_, err = r.Add(nil)
return err
} }
func (r *playlistTrackRepository) Reorder(pos int, newPos int) error { func (r *playlistTrackRepository) Reorder(pos int, newPos int) error {

View File

@ -87,6 +87,14 @@ func (n *Router) addPlaylistTrackRoute(r chi.Router) {
r.Get("/", func(w http.ResponseWriter, r *http.Request) { r.Get("/", func(w http.ResponseWriter, r *http.Request) {
getPlaylist(n.ds)(w, r) getPlaylist(n.ds)(w, r)
}) })
r.With(urlParams).Route("/", func(r chi.Router) {
r.Delete("/", func(w http.ResponseWriter, r *http.Request) {
deleteFromPlaylist(n.ds)(w, r)
})
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
addToPlaylist(n.ds)(w, r)
})
})
r.Route("/{id}", func(r chi.Router) { r.Route("/{id}", func(r chi.Router) {
r.Use(urlParams) r.Use(urlParams)
r.Put("/", func(w http.ResponseWriter, r *http.Request) { r.Put("/", func(w http.ResponseWriter, r *http.Request) {
@ -96,9 +104,6 @@ func (n *Router) addPlaylistTrackRoute(r chi.Router) {
deleteFromPlaylist(n.ds)(w, r) deleteFromPlaylist(n.ds)(w, r)
}) })
}) })
r.With(urlParams).Post("/", func(w http.ResponseWriter, r *http.Request) {
addToPlaylist(n.ds)(w, r)
})
}) })
} }

View File

@ -84,20 +84,34 @@ func handleExportPlaylist(ds model.DataStore) http.HandlerFunc {
func deleteFromPlaylist(ds model.DataStore) http.HandlerFunc { func deleteFromPlaylist(ds model.DataStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
playlistId := utils.ParamString(r, ":playlistId") playlistId := utils.ParamString(r, ":playlistId")
id := r.URL.Query().Get(":id") ids := r.URL.Query()["id"]
tracksRepo := ds.Playlist(r.Context()).Tracks(playlistId) err := ds.WithTx(func(tx model.DataStore) error {
err := tracksRepo.Delete(id) tracksRepo := tx.Playlist(r.Context()).Tracks(playlistId)
if err == model.ErrNotFound { return tracksRepo.Delete(ids...)
log.Warn("Track not found in playlist", "playlistId", playlistId, "id", id) })
if len(ids) == 1 && err == model.ErrNotFound {
log.Warn(r.Context(), "Track not found in playlist", "playlistId", playlistId, "id", ids[0])
http.Error(w, "not found", http.StatusNotFound) http.Error(w, "not found", http.StatusNotFound)
return return
} }
if err != nil { if err != nil {
log.Error("Error deleting track from playlist", "playlistId", playlistId, "id", id, err) log.Error(r.Context(), "Error deleting tracks from playlist", "playlistId", playlistId, "ids", ids, err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
_, err = w.Write([]byte("{}")) var resp []byte
if len(ids) == 1 {
resp = []byte(`{"id":"` + ids[0] + `"}`)
} else {
resp, err = json.Marshal(&struct {
Ids []string `json:"ids"`
}{Ids: ids})
if err != nil {
log.Error(r.Context(), "Error marshaling delete response", "playlistId", playlistId, "ids", ids, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
_, err = w.Write(resp)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }

View File

@ -69,7 +69,6 @@ func (c *PlaylistsController) create(ctx context.Context, playlistId, name strin
var pls *model.Playlist var pls *model.Playlist
var err error var err error
// If playlistID is present, override tracks
if playlistId != "" { if playlistId != "" {
pls, err = tx.Playlist(ctx).Get(playlistId) pls, err = tx.Playlist(ctx).Get(playlistId)
if err != nil { if err != nil {

View File

@ -19,6 +19,14 @@ const mapResource = (resource, params) => {
} }
} }
const callDeleteMany = (resource, params) => {
const ids = params.ids.map((id) => `id=${id}`)
const idsParam = ids.join('&')
return httpClient(`${REST_URL}/${resource}?${idsParam}`, {
method: 'DELETE',
}).then((response) => ({ data: response.json.ids || [] }))
}
const wrapperDataProvider = { const wrapperDataProvider = {
...dataProvider, ...dataProvider,
getList: (resource, params) => { getList: (resource, params) => {
@ -55,6 +63,9 @@ const wrapperDataProvider = {
}, },
deleteMany: (resource, params) => { deleteMany: (resource, params) => {
const [r, p] = mapResource(resource, params) const [r, p] = mapResource(resource, params)
if (r.endsWith('/tracks')) {
return callDeleteMany(r, p)
}
return dataProvider.deleteMany(r, p) return dataProvider.deleteMany(r, p)
}, },
addToPlaylist: (playlistId, data) => { addToPlaylist: (playlistId, data) => {