Remove tracks from playlist

This commit is contained in:
Deluan 2020-05-16 18:10:08 -04:00 committed by Deluan Quintão
parent 5c95eed517
commit 12cf2f1104
6 changed files with 72 additions and 9 deletions

View File

@ -2,8 +2,6 @@ package model
import (
"time"
"github.com/deluan/rest"
)
type Playlist struct {
@ -41,7 +39,8 @@ type PlaylistTrack struct {
type PlaylistTracks []PlaylistTrack
type PlaylistTrackRepository interface {
rest.Repository
ResourceRepository
Add(mediaFileIds []string) error
Update(mediaFileIds []string) error
Delete(id string) error
}

View File

@ -123,12 +123,16 @@ func (r *playlistTrackRepository) Update(mediaFileIds []string) error {
}
}
return r.updateStats()
}
func (r *playlistTrackRepository) updateStats() error {
// Get total playlist duration and count
statsSql := Select("sum(duration) as duration", "count(*) as count").From("media_file").
Join("playlist_tracks f on f.media_file_id = media_file.id").
Where(Eq{"playlist_id": r.playlistId})
var res struct{ Duration, Count float32 }
err = r.queryOne(statsSql, &res)
err := r.queryOne(statsSql, &res)
if err != nil {
return err
}
@ -142,5 +146,12 @@ func (r *playlistTrackRepository) Update(mediaFileIds []string) error {
return err
}
func (r *playlistTrackRepository) Delete(id string) error {
err := r.delete(And{Eq{"playlist_id": r.playlistId}, Eq{"id": id}})
if err != nil {
return err
}
return r.updateStats()
}
var _ model.PlaylistTrackRepository = (*playlistTrackRepository)(nil)
var _ model.ResourceRepository = (*playlistTrackRepository)(nil)

View File

@ -109,6 +109,9 @@ func (app *Router) addPlaylistTrackRoute(r chi.Router) {
r.Route("/{id}", func(r chi.Router) {
r.Use(UrlParams)
r.Get("/", wrapper(rest.Get))
r.Delete("/", func(w http.ResponseWriter, r *http.Request) {
deleteFromPlaylist(app.ds)(w, r)
})
})
r.With(UrlParams).Post("/", func(w http.ResponseWriter, r *http.Request) {
addToPlaylist(app.ds)(w, r)

View File

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/utils"
)
@ -13,6 +14,29 @@ type addTracksPayload struct {
Ids []string `json:"ids"`
}
func deleteFromPlaylist(ds model.DataStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
playlistId := utils.ParamString(r, ":playlistId")
id := r.URL.Query().Get(":id")
tracksRepo := ds.Playlist(r.Context()).Tracks(playlistId)
err := tracksRepo.Delete(id)
if err == model.ErrNotFound {
log.Warn("Track not found in playlist", "playlistId", playlistId, "id", id)
http.Error(w, "not found", http.StatusNotFound)
return
}
if err != nil {
log.Error("Error deleting track from playlist", "playlistId", playlistId, "id", id, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_, err = w.Write([]byte("{}"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
func addToPlaylist(ds model.DataStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
playlistId := utils.ParamString(r, ":playlistId")
@ -32,7 +56,7 @@ func addToPlaylist(ds model.DataStore) http.HandlerFunc {
// Must return an object with an ID, to satisfy ReactAdmin `create` call
_, err = w.Write([]byte(fmt.Sprintf(`{"id":"%s"}`, playlistId)))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}

View File

@ -1,12 +1,17 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useGetOne } from 'react-admin'
import PlaylistDetails from './PlaylistDetails'
import { Title } from '../common'
import PlaylistSongs from './PlaylistSongs'
import PlaylistActions from './PlaylistActions'
import PlaylistSongBulkActions from './PlaylistSongBulkActions'
const PlaylistShow = (props) => {
const { data: record, loading, error } = useGetOne('playlist', props.id)
const viewVersion = useSelector((s) => s.admin.ui && s.admin.ui.viewVersion)
const { data: record, loading, error } = useGetOne('playlist', props.id, {
v: viewVersion,
})
if (loading) {
return null
@ -29,8 +34,7 @@ const PlaylistShow = (props) => {
exporter={false}
perPage={-1}
pagination={null}
bulkActionButtons={false}
// bulkActionButtons={<AlbumSongBulkActions />}
bulkActionButtons={<PlaylistSongBulkActions playlistId={props.id} />}
/>
</>
)

View File

@ -0,0 +1,22 @@
import React, { Fragment, useEffect } from 'react'
import { BulkDeleteButton, useUnselectAll } from 'react-admin'
import PropTypes from 'prop-types'
const PlaylistSongBulkActions = ({ playlistId, ...rest }) => {
const unselectAll = useUnselectAll()
useEffect(() => {
unselectAll('playlistTrack')
// eslint-disable-next-line
}, [])
return (
<Fragment>
<BulkDeleteButton {...rest} resource={`playlist/${playlistId}/tracks`} />
</Fragment>
)
}
PlaylistSongBulkActions.propTypes = {
playlistId: PropTypes.string.isRequired,
}
export default PlaylistSongBulkActions