This commit is contained in:
truewhile
2026-09-20 13:33:45 +08:00
parent 0c4668c156
commit 9b2b592003
9 changed files with 306 additions and 24 deletions
+63
View File
@@ -0,0 +1,63 @@
package service
import (
"net/url"
"strconv"
"strings"
)
// imageCacheKeyURL normalizes an upstream image URL before it is hashed into a
// disk cache key. Credential query parameters are dropped: a remote Emby token
// is per-account, so removing it cannot make two different images collide, but
// it stops a token rotation from invalidating every cached poster at once.
func imageCacheKeyURL(raw string) string {
u, err := url.Parse(strings.TrimSpace(raw))
if err != nil || u.Host == "" {
return raw
}
q := u.Query()
dropped := false
for key := range q {
switch strings.ToLower(key) {
case "api_key", "apikey", "x-emby-token", "x-mediabrowser-token":
q.Del(key)
dropped = true
}
}
if !dropped {
return raw
}
u.RawQuery = q.Encode()
return u.String()
}
// upstreamImageFetchURL forwards the client's thumbnail request to a configured
// remote Emby mount. The remote server can produce the thumbnail itself from
// maxWidth/maxHeight/quality, so the proxy transfers a few dozen KB instead of
// the full-size original and skips the local decode+scale entirely (measured at
// ~400ms for a single 2892×4096 poster).
//
// Other upstreams (TMDb, Douban, adult sites, ...) do not honour these
// parameters, so their originals are still fetched and scaled locally.
func (p *ImageProxy) upstreamImageFetchURL(raw string, opts imageResizeOptions) string {
if p == nil || !opts.active() {
return raw
}
u, err := url.Parse(raw)
if err != nil || u.Host == "" {
return raw
}
if !p.isAllowedRemoteHost(u.Hostname()) {
return raw
}
q := u.Query()
if opts.MaxWidth > 0 {
q.Set("maxWidth", strconv.Itoa(opts.MaxWidth))
}
if opts.MaxHeight > 0 {
q.Set("maxHeight", strconv.Itoa(opts.MaxHeight))
}
q.Set("quality", strconv.Itoa(opts.encodingQuality()))
u.RawQuery = q.Encode()
return u.String()
}