mirror of
https://github.com/navidrome/navidrome.git
synced 2026-11-07 02:38:50 +01:00
After an album-ID copy (e.g. a partial track move), two album rows can reference the same uploaded image file. Replacing or deleting the cover on one of them removed the shared file out from under the other. Add AlbumRepository.CountByImage (unfiltered by library) and skip the file removal in the upload/delete handlers when the filename is referenced by more than one row; the purge GC reaps the file when the last reference goes.
89 lines
2.3 KiB
Go
89 lines
2.3 KiB
Go
package nativeapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/deluan/rest"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/navidrome/navidrome/consts"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/server"
|
|
)
|
|
|
|
func (api *Router) addAlbumRoute(r chi.Router) {
|
|
constructor := func(ctx context.Context) rest.Repository {
|
|
return api.ds.Resource(ctx, model.Album{})
|
|
}
|
|
r.Route("/album", func(r chi.Router) {
|
|
r.Get("/", rest.GetAll(constructor))
|
|
r.Route("/{id}", func(r chi.Router) {
|
|
r.Use(server.URLParamsMiddleware)
|
|
r.Get("/", rest.Get(constructor))
|
|
r.Post("/image", api.uploadAlbumImage())
|
|
r.Delete("/image", api.deleteAlbumImage())
|
|
})
|
|
})
|
|
}
|
|
|
|
func (api *Router) uploadAlbumImage() http.HandlerFunc {
|
|
return handleImageUpload(func(ctx context.Context, reader io.Reader, ext string) error {
|
|
albumID := chi.URLParamFromCtx(ctx, "id")
|
|
al, err := api.ds.Album(ctx).Get(albumID)
|
|
if err != nil {
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
return model.ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
oldPath, err := api.albumImagePathToRemove(ctx, al)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filename, err := api.imgUpload.SetImage(ctx, consts.EntityAlbum, al.ID, al.Name, oldPath, reader, ext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return api.ds.Album(ctx).UpdateImage(al.ID, filename)
|
|
})
|
|
}
|
|
|
|
// albumImagePathToRemove returns the album's current image path, or "" when the file is
|
|
// shared with another album row (post album-ID copy) and must be left on disk.
|
|
func (api *Router) albumImagePathToRemove(ctx context.Context, al *model.Album) (string, error) {
|
|
path := al.UploadedImagePath()
|
|
if path == "" {
|
|
return "", nil
|
|
}
|
|
refs, err := api.ds.Album(ctx).CountByImage(al.UploadedImage)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if refs > 1 {
|
|
return "", nil
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func (api *Router) deleteAlbumImage() http.HandlerFunc {
|
|
return handleImageDelete(func(ctx context.Context) error {
|
|
albumID := chi.URLParamFromCtx(ctx, "id")
|
|
al, err := api.ds.Album(ctx).Get(albumID)
|
|
if err != nil {
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
return model.ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
oldPath, err := api.albumImagePathToRemove(ctx, al)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := api.imgUpload.RemoveImage(ctx, oldPath); err != nil {
|
|
return err
|
|
}
|
|
return api.ds.Album(ctx).UpdateImage(al.ID, "")
|
|
})
|
|
}
|