fix(jellycompat): populate episode season parent metadata
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
||||
type compatEpisodeTarget struct {
|
||||
Item upstreamListItem
|
||||
SeriesImages seriesImageSet
|
||||
SeasonID string
|
||||
SeasonName string
|
||||
}
|
||||
|
||||
type libraryMembershipChecker interface {
|
||||
@@ -166,7 +168,11 @@ func (h *ItemsHandler) fetchCompatEpisodeTargetsByContentIDs(ctx context.Context
|
||||
}
|
||||
|
||||
access := h.resolveAccessFilter(ctx, session)
|
||||
fromClause := "episodes e JOIN media_items si ON e.series_id = si.content_id"
|
||||
fromClause := strings.Join([]string{
|
||||
"episodes e",
|
||||
"JOIN media_items si ON e.series_id = si.content_id",
|
||||
"LEFT JOIN seasons se ON se.content_id = e.season_id",
|
||||
}, " ")
|
||||
conditions := []string{"e.content_id = ANY($1)"}
|
||||
args := []any{normalized}
|
||||
argIdx := 2
|
||||
@@ -208,6 +214,8 @@ func (h *ItemsHandler) fetchCompatEpisodeTargetsByContentIDs(ctx context.Context
|
||||
e.updated_at,
|
||||
e.season_number,
|
||||
e.episode_number,
|
||||
COALESCE(e.season_id, ''),
|
||||
COALESCE(se.title, ''),
|
||||
si.content_id,
|
||||
si.title,
|
||||
si.genres,
|
||||
@@ -249,6 +257,8 @@ func (h *ItemsHandler) fetchCompatEpisodeTargetsByContentIDs(ctx context.Context
|
||||
updatedAt time.Time
|
||||
seasonNumber int
|
||||
episodeNumber int
|
||||
seasonID string
|
||||
seasonName string
|
||||
seriesID string
|
||||
seriesTitle string
|
||||
genres []string
|
||||
@@ -275,6 +285,8 @@ func (h *ItemsHandler) fetchCompatEpisodeTargetsByContentIDs(ctx context.Context
|
||||
&updatedAt,
|
||||
&seasonNumber,
|
||||
&episodeNumber,
|
||||
&seasonID,
|
||||
&seasonName,
|
||||
&seriesID,
|
||||
&seriesTitle,
|
||||
&genres,
|
||||
@@ -335,6 +347,8 @@ func (h *ItemsHandler) fetchCompatEpisodeTargetsByContentIDs(ctx context.Context
|
||||
BackdropThumbhash: seriesBackdropTH,
|
||||
UpdatedAt: seriesUpdatedAt,
|
||||
},
|
||||
SeasonID: seasonID,
|
||||
SeasonName: seasonName,
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
@@ -446,6 +460,7 @@ func (h *ItemsHandler) fetchCompatEpisodeTargetsByContentIDsFallback(ctx context
|
||||
BackdropThumbhash: series.BackdropThumbhash,
|
||||
UpdatedAt: series.UpdatedAt,
|
||||
},
|
||||
SeasonID: episode.SeasonID,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2170,6 +2170,18 @@ func (h *ItemsHandler) rememberCompatEpisodeImages(dto baseItemDTO, stillURL str
|
||||
}
|
||||
|
||||
func (h *ItemsHandler) applyCompatEpisodeTarget(dto *baseItemDTO, target compatEpisodeTarget) {
|
||||
if target.SeasonID != "" && h.codec != nil {
|
||||
encodedSeasonID := h.codec.EncodeStringID(EncodedIDSeason, target.SeasonID)
|
||||
if dto.SeasonID == "" {
|
||||
dto.SeasonID = encodedSeasonID
|
||||
}
|
||||
if dto.ParentID == "" {
|
||||
dto.ParentID = encodedSeasonID
|
||||
}
|
||||
}
|
||||
if dto.SeasonName == "" && target.SeasonName != "" {
|
||||
dto.SeasonName = target.SeasonName
|
||||
}
|
||||
h.mapper.applySeriesImages(dto, target.SeriesImages)
|
||||
h.rememberCompatEpisodeImages(*dto, firstNonEmpty(target.Item.StillURL, target.Item.PosterURL), target.SeriesImages)
|
||||
}
|
||||
|
||||
@@ -152,3 +152,65 @@ func TestHydrateProgressItems_NoFieldsLeavesDetailFieldsNil(t *testing.T) {
|
||||
t.Errorf("MediaSources should be nil with no Fields; got %v", got[0].dto.MediaSources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydrateProgressItems_EpisodeIncludesSeasonParentMetadata(t *testing.T) {
|
||||
codec := NewResourceIDCodec()
|
||||
episodeID := "episode-1"
|
||||
seasonID := "season-1"
|
||||
seriesID := "series-1"
|
||||
|
||||
itemRepo := &countingItemRepo{
|
||||
itemsByID: map[string]*models.MediaItem{
|
||||
seriesID: {ContentID: seriesID, Type: "series", Title: "Test Series"},
|
||||
},
|
||||
}
|
||||
episodeRepo := &countingEpisodeRepo{
|
||||
episodesByID: map[string]*models.Episode{
|
||||
episodeID: {
|
||||
ContentID: episodeID,
|
||||
SeriesID: seriesID,
|
||||
SeasonID: seasonID,
|
||||
Title: "Episode Four",
|
||||
SeasonNumber: 1,
|
||||
EpisodeNumber: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
h := &ItemsHandler{
|
||||
content: &panicOnGetItemDetailContent{},
|
||||
userData: &mockUserDataService{},
|
||||
itemRepo: itemRepo,
|
||||
episodeRepo: episodeRepo,
|
||||
codec: codec,
|
||||
mapper: newMapper(codec, &config.Config{}),
|
||||
}
|
||||
|
||||
got, err := h.hydrateProgressItems(
|
||||
context.Background(),
|
||||
&Session{StreamAppUserID: 1, ProfileID: "profile-1"},
|
||||
[]upstreamProgress{{MediaItemID: episodeID, PositionSeconds: 120, DurationSeconds: 1800}},
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("hydrateProgressItems returned error: %v", err)
|
||||
}
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("expected 1 hydrated item; got %d (%v)", len(got), got)
|
||||
}
|
||||
|
||||
wantSeasonID := codec.EncodeStringID(EncodedIDSeason, seasonID)
|
||||
if got[0].dto.SeasonID != wantSeasonID {
|
||||
t.Fatalf("SeasonId = %q, want %q", got[0].dto.SeasonID, wantSeasonID)
|
||||
}
|
||||
if got[0].dto.ParentID != wantSeasonID {
|
||||
t.Fatalf("ParentId = %q, want season id %q", got[0].dto.ParentID, wantSeasonID)
|
||||
}
|
||||
if got[0].dto.IndexNumber == nil || *got[0].dto.IndexNumber != 4 {
|
||||
t.Fatalf("IndexNumber = %v, want 4", got[0].dto.IndexNumber)
|
||||
}
|
||||
if got[0].dto.ParentIndexNumber == nil || *got[0].dto.ParentIndexNumber != 1 {
|
||||
t.Fatalf("ParentIndexNumber = %v, want 1", got[0].dto.ParentIndexNumber)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user