From c94e894bebf5aadbfd25337eff1db609bf2c631a Mon Sep 17 00:00:00 2001 From: luke-1993 Date: Sun, 22 Oct 2023 18:06:19 +0100 Subject: [PATCH] * add dockerfile * set default value for 'ReportRealPath' to True * move deleteSong logic from subsonic apis to native-apis --- core/players.go | 1 + docker-compose.yml | 18 ++++ server/nativeapi/native_api.go | 12 +++ server/nativeapi/song.go | 177 +++++++++++++++++++++++++++++++++ server/subsonic/stream.go | 165 ------------------------------ 5 files changed, 208 insertions(+), 165 deletions(-) create mode 100644 docker-compose.yml create mode 100644 server/nativeapi/song.go diff --git a/core/players.go b/core/players.go index 3323516c6..c3bd9ab39 100644 --- a/core/players.go +++ b/core/players.go @@ -45,6 +45,7 @@ func (p *players) Register(ctx context.Context, id, client, userAgent, ip string UserId: user.ID, Client: client, ScrobbleEnabled: true, + ReportRealPath: true, } log.Info(ctx, "Registering new player", "id", plr.ID, "client", client, "username", userName(ctx), "type", userAgent) } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..8faab10b6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' +services: + navidrome: + image: navidrome:latest + restart: unless-stopped + container_name: "${NAME}" + user: "501:20" + ports: + - "${PORT}:4533" + environment: + ND_SCANSCHEDULE: 0 + ND_LOGLEVEL: debug + ND_SESSIONTIMEOUT: 24h + # must add this so navidrome can respond to the path navidrome is to be accessed on via traefik + #ND_BASEURL: /navidrome + volumes: + - "/Users/lukepurnell/subbox/${USER}/navidrome/data:/data" + - "/Users/lukepurnell/subbox/${USER}/nav_music:/music:ro" diff --git a/server/nativeapi/native_api.go b/server/nativeapi/native_api.go index 2475862d3..b608570e5 100644 --- a/server/nativeapi/native_api.go +++ b/server/nativeapi/native_api.go @@ -53,6 +53,7 @@ func (n *Router) routes() http.Handler { n.addPlaylistRoute(r) n.addPlaylistTrackRoute(r) + n.addDeleteSongRoute(r) // Keepalive endpoint to be used to keep the session valid (ex: while playing songs) r.Get("/keepalive/*", func(w http.ResponseWriter, r *http.Request) { @@ -145,3 +146,14 @@ func (n *Router) addPlaylistTrackRoute(r chi.Router) { }) }) } + +func (n *Router) addDeleteSongRoute(r chi.Router) { + r.Route("/song", func(r chi.Router) { + r.Route("/{id}", func(r chi.Router) { + r.Use(server.URLParamsMiddleware) + r.Delete("/", func(w http.ResponseWriter, r *http.Request) { + deleteSong(n.ds)(w, r) + }) + }) + }) +} diff --git a/server/nativeapi/song.go b/server/nativeapi/song.go new file mode 100644 index 000000000..2e5ccff39 --- /dev/null +++ b/server/nativeapi/song.go @@ -0,0 +1,177 @@ +package nativeapi + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + "github.com/navidrome/navidrome/log" + "github.com/navidrome/navidrome/model" + "github.com/navidrome/navidrome/utils" +) + +type BeetsItem struct { + Results []struct { + Artpath string `json:"artpath"` + Year int `json:"year"` + Albumtype string `json:"albumtype"` + R128TrackGain any `json:"r128_track_gain"` + Day int `json:"day"` + Disc int `json:"disc"` + Albumartist string `json:"albumartist"` + MbReleasegroupid string `json:"mb_releasegroupid"` + InitialKey any `json:"initial_key"` + AcoustidID string `json:"acoustid_id"` + MbAlbumid string `json:"mb_albumid"` + Month int `json:"month"` + Track int `json:"track"` + Style string `json:"style"` + RgAlbumGain any `json:"rg_album_gain"` + Album string `json:"album"` + DiscogsArtistid int `json:"discogs_artistid"` + Tracktotal int `json:"tracktotal"` + MbAlbumartistid string `json:"mb_albumartistid"` + Country string `json:"country"` + Channels int `json:"channels"` + Arranger string `json:"arranger"` + Comp int `json:"comp"` + Bitrate int `json:"bitrate"` + Length float64 `json:"length"` + Lyricist string `json:"lyricist"` + RgAlbumPeak any `json:"rg_album_peak"` + MbArtistid string `json:"mb_artistid"` + Trackdisambig string `json:"trackdisambig"` + OriginalMonth int `json:"original_month"` + AcoustidFingerprint string `json:"acoustid_fingerprint"` + OriginalDay int `json:"original_day"` + ComposerSort string `json:"composer_sort"` + AlbumartistCredit string `json:"albumartist_credit"` + MbWorkid string `json:"mb_workid"` + MbReleasetrackid string `json:"mb_releasetrackid"` + Mtime float64 `json:"mtime"` + Work string `json:"work"` + AlbumartistSort string `json:"albumartist_sort"` + DiscogsLabelid int `json:"discogs_labelid"` + Bpm int `json:"bpm"` + Language string `json:"language"` + DataSource string `json:"data_source"` + ArtistCredit string `json:"artist_credit"` + Format string `json:"format"` + Composer string `json:"composer"` + Disctotal int `json:"disctotal"` + Title string `json:"title"` + Grouping string `json:"grouping"` + Added float64 `json:"added"` + Media string `json:"media"` + Artist string `json:"artist"` + Albumdisambig string `json:"albumdisambig"` + Isrc string `json:"isrc"` + DiscogsAlbumid int `json:"discogs_albumid"` + Disctitle string `json:"disctitle"` + Lyrics string `json:"lyrics"` + Albumstatus string `json:"albumstatus"` + Albumtypes string `json:"albumtypes"` + Releasegroupdisambig string `json:"releasegroupdisambig"` + Comments string `json:"comments"` + Encoder string `json:"encoder"` + Catalognum string `json:"catalognum"` + R128AlbumGain any `json:"r128_album_gain"` + Label string `json:"label"` + Bitdepth int `json:"bitdepth"` + RgTrackGain any `json:"rg_track_gain"` + ID int `json:"id"` + Script string `json:"script"` + ArtistSort string `json:"artist_sort"` + TrackAlt string `json:"track_alt"` + Genre string `json:"genre"` + OriginalYear int `json:"original_year"` + WorkDisambig string `json:"work_disambig"` + AlbumID int `json:"album_id"` + MbTrackid string `json:"mb_trackid"` + Asin string `json:"asin"` + RgTrackPeak any `json:"rg_track_peak"` + Samplerate int `json:"samplerate"` + Size int `json:"size"` + } `json:"results"` +} + +func deleteSong(ds model.DataStore) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // todo: tests, use proper url parsing lib + id := utils.ParamString(r, ":id") + ctx := r.Context() + ids := strings.Split(id, ",") + for _, id := range ids { + println(id) + mf, err := ds.MediaFile(ctx).Get(id) + if err != nil { + log.Error(err) + } + println(mf.Artist) + println(mf.Title) + // todo set this base from env variable + //baseUrl := "http://127.0.0.1:8337" + baseUrl := "http://host.docker.internal:8337" + queryEndPoint := "/item/query/" + queryStr := fmt.Sprintf("artist:%s/title:%s/album:%s", mf.Artist, mf.Title, mf.Album) + url := baseUrl + queryEndPoint + queryStr + fmt.Printf("query url: %s\n", url) + resp, err := http.Get(url) // nolint + if err != nil { + log.Error(err) + return + } + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Error(err) + return + } + sb := string(body) + var beetsItem BeetsItem + err = json.Unmarshal([]byte(sb), &beetsItem) + if err != nil { + log.Error(err) + return + } + length := len(beetsItem.Results) + if length != 1 { + log.Error("following query string matched n entries", "n", length, "queryStr", queryStr) + return + } + item := beetsItem.Results[0] + log.Info("deleting: ", item.Artist, " : ", item.Title, " id: ", item.ID) + + deleteStr := fmt.Sprintf("/item/%d", item.ID) + + del_url := baseUrl + deleteStr + // Create request + req, err := http.NewRequest(http.MethodDelete, del_url, nil) + log.Info("delete request: ", req) + if err != nil { + log.Error(err) + return + } + // Fetch Request + client := &http.Client{} + del_resp, del_err := client.Do(req) + if del_err != nil { + log.Error(err) + return + } + defer del_resp.Body.Close() + + // read the response body + del_body, err := io.ReadAll(del_resp.Body) + if err != nil { + log.Error(err) + return + } + + // print the response body + fmt.Println(string(del_body)) + log.Info("del body: ", del_body) + } + } +} diff --git a/server/subsonic/stream.go b/server/subsonic/stream.go index 9c7d47670..d0cbe2086 100644 --- a/server/subsonic/stream.go +++ b/server/subsonic/stream.go @@ -2,7 +2,6 @@ package subsonic import ( "context" - "encoding/json" "fmt" "io" "net/http" @@ -18,91 +17,6 @@ import ( "github.com/navidrome/navidrome/utils/req" ) -type BeetsItem struct { - Results []struct { - Artpath string `json:"artpath"` - Year int `json:"year"` - Albumtype string `json:"albumtype"` - R128TrackGain any `json:"r128_track_gain"` - Day int `json:"day"` - Disc int `json:"disc"` - Albumartist string `json:"albumartist"` - MbReleasegroupid string `json:"mb_releasegroupid"` - InitialKey any `json:"initial_key"` - AcoustidID string `json:"acoustid_id"` - MbAlbumid string `json:"mb_albumid"` - Month int `json:"month"` - Track int `json:"track"` - Style string `json:"style"` - RgAlbumGain any `json:"rg_album_gain"` - Album string `json:"album"` - DiscogsArtistid int `json:"discogs_artistid"` - Tracktotal int `json:"tracktotal"` - MbAlbumartistid string `json:"mb_albumartistid"` - Country string `json:"country"` - Channels int `json:"channels"` - Arranger string `json:"arranger"` - Comp int `json:"comp"` - Bitrate int `json:"bitrate"` - Length float64 `json:"length"` - Lyricist string `json:"lyricist"` - RgAlbumPeak any `json:"rg_album_peak"` - MbArtistid string `json:"mb_artistid"` - Trackdisambig string `json:"trackdisambig"` - OriginalMonth int `json:"original_month"` - AcoustidFingerprint string `json:"acoustid_fingerprint"` - OriginalDay int `json:"original_day"` - ComposerSort string `json:"composer_sort"` - AlbumartistCredit string `json:"albumartist_credit"` - MbWorkid string `json:"mb_workid"` - MbReleasetrackid string `json:"mb_releasetrackid"` - Mtime float64 `json:"mtime"` - Work string `json:"work"` - AlbumartistSort string `json:"albumartist_sort"` - DiscogsLabelid int `json:"discogs_labelid"` - Bpm int `json:"bpm"` - Language string `json:"language"` - DataSource string `json:"data_source"` - ArtistCredit string `json:"artist_credit"` - Format string `json:"format"` - Composer string `json:"composer"` - Disctotal int `json:"disctotal"` - Title string `json:"title"` - Grouping string `json:"grouping"` - Added float64 `json:"added"` - Media string `json:"media"` - Artist string `json:"artist"` - Albumdisambig string `json:"albumdisambig"` - Isrc string `json:"isrc"` - DiscogsAlbumid int `json:"discogs_albumid"` - Disctitle string `json:"disctitle"` - Lyrics string `json:"lyrics"` - Albumstatus string `json:"albumstatus"` - Albumtypes string `json:"albumtypes"` - Releasegroupdisambig string `json:"releasegroupdisambig"` - Comments string `json:"comments"` - Encoder string `json:"encoder"` - Catalognum string `json:"catalognum"` - R128AlbumGain any `json:"r128_album_gain"` - Label string `json:"label"` - Bitdepth int `json:"bitdepth"` - RgTrackGain any `json:"rg_track_gain"` - ID int `json:"id"` - Script string `json:"script"` - ArtistSort string `json:"artist_sort"` - TrackAlt string `json:"track_alt"` - Genre string `json:"genre"` - OriginalYear int `json:"original_year"` - WorkDisambig string `json:"work_disambig"` - AlbumID int `json:"album_id"` - MbTrackid string `json:"mb_trackid"` - Asin string `json:"asin"` - RgTrackPeak any `json:"rg_track_peak"` - Samplerate int `json:"samplerate"` - Size int `json:"size"` - } `json:"results"` -} - func (api *Router) serveStream(ctx context.Context, w http.ResponseWriter, r *http.Request, stream *core.Stream, id string) { if stream.Seekable() { http.ServeContent(w, r, stream.Name(), stream.ModTime(), stream) @@ -166,85 +80,6 @@ func (api *Router) Stream(w http.ResponseWriter, r *http.Request) (*responses.Su return nil, nil } -func (api *Router) Delete(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { - // todo: tests, use proper url parsing lib - println("hello from go. Deleting...") - id, err := requiredParamString(r, "id") - ctx := r.Context() - ids := strings.Split(id, ",") - for _, id := range ids { - println(id) - mf, err := api.ds.MediaFile(ctx).Get(id) - if err != nil { - log.Error(err) - } - println(mf.Artist) - println(mf.Title) - // todo set this base from env variable - //baseUrl := "http://127.0.0.1:8337" - baseUrl := "http://host.docker.internal:8337" - queryEndPoint := "/item/query/" - queryStr := fmt.Sprintf("artist:%s/title:%s/album:%s", mf.Artist, mf.Title, mf.Album) - url := baseUrl + queryEndPoint + queryStr - fmt.Printf("query url: %s\n", url) - resp, err := http.Get(url) // nolint - if err != nil { - log.Error(err) - return nil, err - } - body, err := io.ReadAll(resp.Body) - if err != nil { - log.Error(err) - return nil, err - } - sb := string(body) - var beetsItem BeetsItem - err = json.Unmarshal([]byte(sb), &beetsItem) - if err != nil { - log.Error(err) - return nil, err - } - length := len(beetsItem.Results) - if length != 1 { - log.Error("following query string matched n entries", "n", length, "queryStr", queryStr) - return nil, err - } - item := beetsItem.Results[0] - log.Info("deleting: ", item.Artist, " : ", item.Title, " id: ", item.ID) - - deleteStr := fmt.Sprintf("/item/%d", item.ID) - - del_url := baseUrl + deleteStr - // Create request - req, err := http.NewRequest(http.MethodDelete, del_url, nil) - log.Info("delete request: ", req) - if err != nil { - log.Error(err) - return nil, err - } - // Fetch Request - client := &http.Client{} - del_resp, del_err := client.Do(req) - if del_err != nil { - log.Error(err) - return nil, del_err - } - defer del_resp.Body.Close() - - // read the response body - del_body, err := io.ReadAll(del_resp.Body) - if err != nil { - log.Error(err) - return nil, del_err - } - - // print the response body - fmt.Println(string(del_body)) - log.Info("del body: ", del_body) - } - return nil, err -} - func (api *Router) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { ctx := r.Context() username, _ := request.UsernameFrom(ctx)