Skip to content

Commit

Permalink
Remove tracks from playlist
Browse files Browse the repository at this point in the history
  • Loading branch information
deluan committed May 16, 2020
1 parent 5c95eed commit 12cf2f1
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 9 deletions.
5 changes: 2 additions & 3 deletions model/playlist.go
Expand Up @@ -2,8 +2,6 @@ package model

import (
"time"

"github.com/deluan/rest"
)

type Playlist struct {
Expand Down Expand Up @@ -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
}
15 changes: 13 additions & 2 deletions persistence/playlist_track_repository.go
Expand Up @@ -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
}
Expand All @@ -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)
3 changes: 3 additions & 0 deletions server/app/app.go
Expand Up @@ -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)
Expand Down
26 changes: 25 additions & 1 deletion server/app/playlists.go
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/http"

"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/utils"
)
Expand All @@ -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")
Expand All @@ -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)
}
}
}
10 changes: 7 additions & 3 deletions ui/src/playlist/PlaylistShow.js
@@ -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
Expand All @@ -29,8 +34,7 @@ const PlaylistShow = (props) => {
exporter={false}
perPage={-1}
pagination={null}
bulkActionButtons={false}
// bulkActionButtons={<AlbumSongBulkActions />}
bulkActionButtons={<PlaylistSongBulkActions playlistId={props.id} />}
/>
</>
)
Expand Down
22 changes: 22 additions & 0 deletions ui/src/playlist/PlaylistSongBulkActions.js
@@ -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

0 comments on commit 12cf2f1

Please sign in to comment.