From 6260927074d6bb5750f54d2d2e9550ef029a5d9a Mon Sep 17 00:00:00 2001 From: Deluan Date: Fri, 30 Dec 2022 20:14:03 -0500 Subject: [PATCH] Serve artist placeholder directly, instead of using LastFM's CDN --- core/agents/placeholders.go | 9 ++-- server/middlewares.go | 56 ++++++++++++++++++++++++- server/server.go | 12 +++++- server/subsonic/browsing.go | 9 ++-- server/subsonic/responses/responses.go | 5 ++- ui/public/artist-placeholder.webp | Bin 0 -> 546 bytes 6 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 ui/public/artist-placeholder.webp diff --git a/core/agents/placeholders.go b/core/agents/placeholders.go index 45ab5f59..1210b8db 100644 --- a/core/agents/placeholders.go +++ b/core/agents/placeholders.go @@ -3,21 +3,22 @@ package agents import ( "context" + "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/model" ) const PlaceholderAgentName = "placeholder" const ( - placeholderArtistImageSmallUrl = "https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png" - placeholderArtistImageMediumUrl = "https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png" - placeholderArtistImageLargeUrl = "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png" + placeholderArtistImageSmallUrl = consts.URLPathUI + "/artist-placeholder.webp" + placeholderArtistImageMediumUrl = consts.URLPathUI + "/artist-placeholder.webp" + placeholderArtistImageLargeUrl = consts.URLPathUI + "/artist-placeholder.webp" placeholderBiography = "Biography not available" ) type placeholderAgent struct{} -func placeholdersConstructor(ds model.DataStore) Interface { +func placeholdersConstructor(_ model.DataStore) Interface { return &placeholderAgent{} } diff --git a/server/middlewares.go b/server/middlewares.go index effed772..1c374fa2 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -115,7 +115,7 @@ func compressMiddleware() func(http.Handler) http.Handler { ) } -func clientUniqueIdAdder(next http.Handler) http.Handler { +func clientUniqueIDMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() clientUniqueId := r.Header.Get(consts.UIClientUniqueIDHeader) @@ -145,3 +145,57 @@ func clientUniqueIdAdder(next http.Handler) http.Handler { next.ServeHTTP(w, r) }) } + +func serverAddressMiddleware(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if rScheme, rHost := serverAddress(r); rHost != "" { + r.Host = rHost + r.URL.Scheme = rScheme + } + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} + +var ( + xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") + xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Scheme") + xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Proto") +) + +func serverAddress(r *http.Request) (scheme, host string) { + origHost := r.Host + protocol := "http" + if r.TLS != nil { + protocol = "https" + } + xfh := r.Header.Get(xForwardedHost) + if xfh != "" { + i := strings.Index(xfh, ",") + if i == -1 { + i = len(xfh) + } + xfh = xfh[:i] + } + scheme = firstOr( + protocol, + r.Header.Get(xForwardedProto), + r.Header.Get(xForwardedScheme), + r.URL.Scheme, + ) + host = firstOr(r.Host, xfh) + if host != origHost { + log.Trace(r.Context(), "Request host has changed", "origHost", origHost, "host", host, "scheme", scheme, "url", r.URL) + } + return scheme, host +} + +func firstOr(or string, strings ...string) string { + for _, s := range strings { + if s != "" { + return s + } + } + return or +} diff --git a/server/server.go b/server/server.go index d095d586..f96b1c3d 100644 --- a/server/server.go +++ b/server/server.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "path" + "strings" "time" "github.com/go-chi/chi/v5" @@ -94,7 +95,8 @@ func (s *Server) initRoutes() { r.Use(middleware.Recoverer) r.Use(compressMiddleware()) r.Use(middleware.Heartbeat("/ping")) - r.Use(clientUniqueIdAdder) + r.Use(serverAddressMiddleware) + r.Use(clientUniqueIDMiddleware) r.Use(loggerInjector) r.Use(requestLogger) r.Use(robotsTXT(ui.BuildAssets())) @@ -135,3 +137,11 @@ func (s *Server) frontendAssetsHandler() http.Handler { r.Handle("/*", http.StripPrefix(s.appRoot, http.FileServer(http.FS(ui.BuildAssets())))) return r } + +func AbsoluteURL(r *http.Request, url string) string { + if strings.HasPrefix(url, "/") { + appRoot := path.Join(r.Host, conf.Server.BaseURL, url) + url = r.URL.Scheme + "://" + appRoot + } + return url +} diff --git a/server/subsonic/browsing.go b/server/subsonic/browsing.go index 9d17dcd4..044a535b 100644 --- a/server/subsonic/browsing.go +++ b/server/subsonic/browsing.go @@ -10,6 +10,7 @@ import ( "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/model" + "github.com/navidrome/navidrome/server" "github.com/navidrome/navidrome/server/subsonic/filter" "github.com/navidrome/navidrome/server/subsonic/responses" "github.com/navidrome/navidrome/utils" @@ -231,9 +232,9 @@ func (api *Router) GetArtistInfo(r *http.Request) (*responses.Subsonic, error) { response := newResponse() response.ArtistInfo = &responses.ArtistInfo{} response.ArtistInfo.Biography = artist.Biography - response.ArtistInfo.SmallImageUrl = artist.SmallImageUrl - response.ArtistInfo.MediumImageUrl = artist.MediumImageUrl - response.ArtistInfo.LargeImageUrl = artist.LargeImageUrl + response.ArtistInfo.SmallImageUrl = server.AbsoluteURL(r, artist.SmallImageUrl) + response.ArtistInfo.MediumImageUrl = server.AbsoluteURL(r, artist.MediumImageUrl) + response.ArtistInfo.LargeImageUrl = server.AbsoluteURL(r, artist.LargeImageUrl) response.ArtistInfo.LastFmUrl = artist.ExternalUrl response.ArtistInfo.MusicBrainzID = artist.MbzArtistID for _, s := range artist.SimilarArtists { @@ -259,7 +260,7 @@ func (api *Router) GetArtistInfo2(r *http.Request) (*responses.Subsonic, error) similar.AlbumCount = s.AlbumCount similar.Starred = s.Starred similar.UserRating = s.UserRating - similar.ArtistImageUrl = s.ArtistImageUrl + similar.ArtistImageUrl = server.AbsoluteURL(r, s.ArtistImageUrl) response.ArtistInfo2.SimilarArtist = append(response.ArtistInfo2.SimilarArtist, similar) } return response, nil diff --git a/server/subsonic/responses/responses.go b/server/subsonic/responses/responses.go index 9593f670..795e1290 100644 --- a/server/subsonic/responses/responses.go +++ b/server/subsonic/responses/responses.go @@ -78,8 +78,9 @@ type Artist struct { Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"` UserRating int `xml:"userRating,attr,omitempty" json:"userRating,omitempty"` ArtistImageUrl string `xml:"artistImageUrl,attr,omitempty" json:"artistImageUrl,omitempty"` - /* - + /* TODO: + + */ } diff --git a/ui/public/artist-placeholder.webp b/ui/public/artist-placeholder.webp new file mode 100644 index 0000000000000000000000000000000000000000..2729158d2541567d758c20d23e1998c1c8cab2fc GIT binary patch literal 546 zcmWIYbaRtpVqge&bqWXzuu$LwvL~oB%w^QlVbo!?3sjrrws?`^LZuBeItsWrGxHvn zF-m`KUtIF@<}!u=6_$nxLQD=GoQw(>OnbMgkOfN|*4D*%ccynf{d{Ah;D62~hmY)- zE_Bf}sy_Wk{-t`m>`#w?(!5%lvxR>ih!x|McrBZB(D&rhZ3>+y_AcJXwYlOo=Q+z2 zE}s~ERTCvP={~q^8|hi7r+oNMFK0o}$wIj(FZslL8-@k$w*+)DxUc97J&JKZ9#>rK z7@x)YGpp!ip`Pj?u5V3tA9lVnFn5bKe$TzAcDL?3=3SM=A2)}rZ=IUE8z^S{dHQUF z;-f2qj&^@wN|cC9)slAnxCQ8?9;NJH1?{666${r*t+&=Ic@^+i^R9d+(9|mzaQYk+ z9`X$g|Nie5U=WaEeNxQzCZr=`_okX!$+h-h=NecpHP!uFAH$fw#;{9F<(IwivcD0U z^QOGjHk;okzx?+q`P$73t$voe_&(E{b=NTcj7^xvueyb)b2V?ZT{A7u{#duzAlZ@m z+=XSL8SW{MuE}1x6uy1OKi`Uj7MaGgZx}9@*SMO)u~N=L^n?1Rg_;IW3?wB=j_>Dq zBUF9)^Yf#h)?X~Sp?2sq`@Ke`1rw(gys?qfQdseDp&x6lV;i6H>ymT!JEH#ovwi($ Jz9|C;006eG<>3GT literal 0 HcmV?d00001