mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-04 13:07:36 +03:00
Remove tracks from playlist
This commit is contained in:
parent
5c95eed517
commit
12cf2f1104
6 changed files with 72 additions and 9 deletions
|
@ -2,8 +2,6 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/deluan/rest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Playlist struct {
|
type Playlist struct {
|
||||||
|
@ -41,7 +39,8 @@ type PlaylistTrack struct {
|
||||||
type PlaylistTracks []PlaylistTrack
|
type PlaylistTracks []PlaylistTrack
|
||||||
|
|
||||||
type PlaylistTrackRepository interface {
|
type PlaylistTrackRepository interface {
|
||||||
rest.Repository
|
ResourceRepository
|
||||||
Add(mediaFileIds []string) error
|
Add(mediaFileIds []string) error
|
||||||
Update(mediaFileIds []string) error
|
Update(mediaFileIds []string) error
|
||||||
|
Delete(id string) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// Get total playlist duration and count
|
||||||
statsSql := Select("sum(duration) as duration", "count(*) as count").From("media_file").
|
statsSql := Select("sum(duration) as duration", "count(*) as count").From("media_file").
|
||||||
Join("playlist_tracks f on f.media_file_id = media_file.id").
|
Join("playlist_tracks f on f.media_file_id = media_file.id").
|
||||||
Where(Eq{"playlist_id": r.playlistId})
|
Where(Eq{"playlist_id": r.playlistId})
|
||||||
var res struct{ Duration, Count float32 }
|
var res struct{ Duration, Count float32 }
|
||||||
err = r.queryOne(statsSql, &res)
|
err := r.queryOne(statsSql, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,5 +146,12 @@ func (r *playlistTrackRepository) Update(mediaFileIds []string) error {
|
||||||
return err
|
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.PlaylistTrackRepository = (*playlistTrackRepository)(nil)
|
||||||
var _ model.ResourceRepository = (*playlistTrackRepository)(nil)
|
|
||||||
|
|
|
@ -109,6 +109,9 @@ func (app *Router) addPlaylistTrackRoute(r chi.Router) {
|
||||||
r.Route("/{id}", func(r chi.Router) {
|
r.Route("/{id}", func(r chi.Router) {
|
||||||
r.Use(UrlParams)
|
r.Use(UrlParams)
|
||||||
r.Get("/", wrapper(rest.Get))
|
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) {
|
r.With(UrlParams).Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
addToPlaylist(app.ds)(w, r)
|
addToPlaylist(app.ds)(w, r)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/deluan/navidrome/log"
|
||||||
"github.com/deluan/navidrome/model"
|
"github.com/deluan/navidrome/model"
|
||||||
"github.com/deluan/navidrome/utils"
|
"github.com/deluan/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
@ -13,6 +14,29 @@ type addTracksPayload struct {
|
||||||
Ids []string `json:"ids"`
|
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 {
|
func addToPlaylist(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")
|
||||||
|
@ -32,7 +56,7 @@ func addToPlaylist(ds model.DataStore) http.HandlerFunc {
|
||||||
// Must return an object with an ID, to satisfy ReactAdmin `create` call
|
// Must return an object with an ID, to satisfy ReactAdmin `create` call
|
||||||
_, err = w.Write([]byte(fmt.Sprintf(`{"id":"%s"}`, playlistId)))
|
_, err = w.Write([]byte(fmt.Sprintf(`{"id":"%s"}`, playlistId)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
import { useGetOne } from 'react-admin'
|
import { useGetOne } from 'react-admin'
|
||||||
import PlaylistDetails from './PlaylistDetails'
|
import PlaylistDetails from './PlaylistDetails'
|
||||||
import { Title } from '../common'
|
import { Title } from '../common'
|
||||||
import PlaylistSongs from './PlaylistSongs'
|
import PlaylistSongs from './PlaylistSongs'
|
||||||
import PlaylistActions from './PlaylistActions'
|
import PlaylistActions from './PlaylistActions'
|
||||||
|
import PlaylistSongBulkActions from './PlaylistSongBulkActions'
|
||||||
|
|
||||||
const PlaylistShow = (props) => {
|
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) {
|
if (loading) {
|
||||||
return null
|
return null
|
||||||
|
@ -29,8 +34,7 @@ const PlaylistShow = (props) => {
|
||||||
exporter={false}
|
exporter={false}
|
||||||
perPage={-1}
|
perPage={-1}
|
||||||
pagination={null}
|
pagination={null}
|
||||||
bulkActionButtons={false}
|
bulkActionButtons={<PlaylistSongBulkActions playlistId={props.id} />}
|
||||||
// bulkActionButtons={<AlbumSongBulkActions />}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
22
ui/src/playlist/PlaylistSongBulkActions.js
Normal file
22
ui/src/playlist/PlaylistSongBulkActions.js
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue