diff --git a/server/app/app.go b/server/app/app.go index 1c53b8091..b8bc478ea 100644 --- a/server/app/app.go +++ b/server/app/app.go @@ -104,24 +104,12 @@ func (app *Router) RX(r chi.Router, pathPrefix string, constructor rest.Reposito type restHandler = func(rest.RepositoryConstructor, ...rest.Logger) http.HandlerFunc func (app *Router) addPlaylistTrackRoute(r chi.Router) { - // Add a middleware to capture the playlistId - wrapper := func(f restHandler) http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - c := func(ctx context.Context) rest.Repository { - plsRepo := app.ds.Resource(ctx, model.Playlist{}) - plsId := chi.URLParam(req, "playlistId") - return plsRepo.(model.PlaylistRepository).Tracks(plsId) - } - - f(c).ServeHTTP(res, req) - } - } - r.Route("/playlist/{playlistId}/tracks", func(r chi.Router) { - r.Get("/", wrapper(rest.GetAll)) + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + getPlaylist(app.ds)(w, r) + }) r.Route("/{id}", func(r chi.Router) { r.Use(UrlParams) - r.Get("/", wrapper(rest.Get)) r.Put("/", func(w http.ResponseWriter, r *http.Request) { reorderItem(app.ds)(w, r) }) diff --git a/server/app/playlists.go b/server/app/playlists.go index e1348efe3..6aa4747b1 100644 --- a/server/app/playlists.go +++ b/server/app/playlists.go @@ -1,16 +1,76 @@ package app import ( + "context" "encoding/json" "fmt" "net/http" "strconv" + "strings" "github.com/deluan/navidrome/log" "github.com/deluan/navidrome/model" "github.com/deluan/navidrome/utils" + "github.com/deluan/rest" + "github.com/go-chi/chi" ) +func getPlaylist(ds model.DataStore) http.HandlerFunc { + // Add a middleware to capture the playlistId + wrapper := func(handler restHandler) http.HandlerFunc { + return func(res http.ResponseWriter, req *http.Request) { + constructor := func(ctx context.Context) rest.Repository { + plsRepo := ds.Playlist(ctx) + plsId := chi.URLParam(req, "playlistId") + return plsRepo.(model.PlaylistRepository).Tracks(plsId) + } + + handler(constructor).ServeHTTP(res, req) + } + } + + return func(w http.ResponseWriter, r *http.Request) { + accept := r.Header.Get("accept") + if strings.ToLower(accept) == "audio/x-mpegurl" { + handleExportPlaylist(ds)(w, r) + return + } + wrapper(rest.GetAll)(w, r) + } +} + +func handleExportPlaylist(ds model.DataStore) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + plsRepo := ds.Playlist(ctx) + plsId := chi.URLParam(r, "playlistId") + pls, err := plsRepo.Get(plsId) + if err == model.ErrNotFound { + log.Warn("Playlist not found", "playlistId", plsId) + http.Error(w, "not found", http.StatusNotFound) + return + } + if err != nil { + log.Error("Error retrieving the playlist", "playlistId", plsId, err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "audio/x-mpegurl") + + // TODO: Move this and the import playlist logic to `core` + w.Write([]byte("#EXTM3U\n")) + for _, t := range pls.Tracks { + header := fmt.Sprintf("#EXTINF:%.f,%s - %s\n", t.Duration, t.Artist, t.Title) + line := t.Path + "\n" + _, err := w.Write([]byte(header + line)) + if err != nil { + log.Error(ctx, "Error sending playlist", "name", pls.Name) + } + } + } +} + func deleteFromPlaylist(ds model.DataStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { playlistId := utils.ParamString(r, ":playlistId")