Series drill-down: season posters
The drill-down reads the series with includeSeasonImages=true (Sonarr only sends season posters when asked, as TheTVDB links). A selected season or episode shows its season's poster; without one, or when it cannot be loaded, the series poster. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+26
-1
@@ -257,7 +257,32 @@ func (v *seriesView) renderDetail() {
|
||||
sum = se
|
||||
}
|
||||
}
|
||||
v.detail.Show(seriesRowDetail(v.series, r.ep, r.season, sum, v.seasonMonitored(r.season), time.Now().Format("2006-01-02")), v.cover)
|
||||
v.detail.Show(seriesRowDetail(v.series, r.ep, r.season, sum, v.seasonMonitored(r.season), time.Now().Format("2006-01-02")), v.seasonCover(r.season))
|
||||
}
|
||||
|
||||
// seasonCover is the poster of a season, for its row and its episodes: the
|
||||
// season's own poster where Sonarr has one (a TheTVDB link, fetched without
|
||||
// credentials), else, or when it cannot be loaded, the series poster.
|
||||
func (v *seriesView) seasonCover(season int) coverRef {
|
||||
for _, se := range v.series.Seasons {
|
||||
if se.SeasonNumber != season {
|
||||
continue
|
||||
}
|
||||
for _, img := range se.Images {
|
||||
if img.CoverType != "poster" || img.RemoteURL == "" {
|
||||
continue
|
||||
}
|
||||
remote, series := img.RemoteURL, v.cover
|
||||
return coverRef{key: "season|" + remote, fetch: func(ctx context.Context, large bool) ([]byte, error) {
|
||||
data, err := arr.FetchURL(ctx, remote)
|
||||
if err != nil && series.fetch != nil {
|
||||
return series.fetch(ctx, large)
|
||||
}
|
||||
return data, err
|
||||
}}
|
||||
}
|
||||
}
|
||||
return v.cover
|
||||
}
|
||||
|
||||
// seriesRowDetail is the detail text of the drill-down: the series, then the
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -308,3 +311,37 @@ func TestCutToWidth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeasonPoster(t *testing.T) {
|
||||
seasonSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/broken.jpg" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
w.Write([]byte("SEASON"))
|
||||
}))
|
||||
defer seasonSrv.Close()
|
||||
|
||||
series := coverRef{key: "series", fetch: func(context.Context, bool) ([]byte, error) { return []byte("SERIES"), nil }}
|
||||
v := &seriesView{cover: series, series: sonarr.Series{Seasons: []sonarr.Season{
|
||||
{SeasonNumber: 1, Images: []arr.Image{{CoverType: "poster", RemoteURL: seasonSrv.URL + "/s1.jpg"}}},
|
||||
{SeasonNumber: 2},
|
||||
{SeasonNumber: 3, Images: []arr.Image{{CoverType: "poster", RemoteURL: seasonSrv.URL + "/broken.jpg"}}},
|
||||
}}}
|
||||
fetch := func(season int) string {
|
||||
data, err := v.seasonCover(season).fetch(t.Context(), false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
if got := fetch(1); got != "SEASON" || v.seasonCover(1).key == series.key {
|
||||
t.Errorf("season 1: %q", got)
|
||||
}
|
||||
if v.seasonCover(2).key != series.key {
|
||||
t.Error("a season without a poster shows the series poster")
|
||||
}
|
||||
if got := fetch(3); got != "SERIES" {
|
||||
t.Errorf("a season poster that cannot be loaded falls back to the series poster: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user