clone

a desktop tv and program guide for over-the-air broadcasts: what's on air, what your tuner gets, live tv, recordings

Describe Jellyfin channels' programs as fully as the antenna's

FelineStateMachine committed b183242
The Jellyfin client now asks for each video's people, content rating and
genres too, so the guide shows a Jellyfin channel's programs as it does
Schedules Direct's: the cast with their characters, the rating (R,
TV-14), genres, when it first aired, and up to 1000 characters of the
overview rather than 300. The community rating is left out: it's on a
10-point scale, not the guide's 4 stars.
commit
b1832427144bb0a1813f8fc378b73fa1c9b6e867
parent
8e08183a288c2d63c3b0c35dd9f26e3675166dde
author
FelineStateMachine <226808055+FelineStateMachine@users.noreply.github.com>
kind
feat risk score 1.6 of 3, Jev's estimate
10 files changed, +90 -19
Minternal/jellyfin/items.go34++++++++++++++++++++++++++----
Minternal/jellyfin/jellyfin_test.go7++++--
Minternal/jellyfin/jellyfintest/jellyfintest.go27+++++++++++++++++++-----
Minternal/service/service.go4++++
Minternal/vchan/folder_test.go3++-
Minternal/vchan/jellyfin.go4++--
Minternal/vchan/jellyfin_test.go3++-
Minternal/vchan/loop.go17++++++++++++++-
Minternal/vchan/vchan.go7++++++
Minternal/vchan/youtube_test.go3++-
M internal/jellyfin/items.go +29 -5
@@ -57,8 +57,13 @@ type Video struct {
5757 Premiere string // first aired, "2006-01-02", or "" when unknown5858 Year int5959 Overview string60- Runtime time.Duration61- Audio bool60+ // Cast lists the actors, as "Name (Role)", leads first; Rating is the61+ // content rating ("TV-14"); Genres as the library has them.62+ Cast []string63+ Rating string64+ Genres []string65+ Runtime time.Duration66+ Audio bool6267 // Image is artwork on the server, which loads without signing in: an6368 // episode's still or its series' backdrop, a movie's backdrop or poster.6469 Image string
@@ -113,7 +118,13 @@ type dto struct {
113118 BackdropImageTags []string114119 ParentBackdropItemID string `json:"ParentBackdropItemId"`115120 ParentBackdropImageTags []string116- MediaSources []struct {121+ OfficialRating string122+ People []struct {123+ Name string124+ Role string125+ Type string // "Actor", "GuestStar", "Director"...126+ }127+ MediaSources []struct {117128 ID string `json:"Id"`118129 RunTimeTicks int64119130 MediaStreams []mediaStream
@@ -321,7 +332,7 @@ func (c *Client) contents(ctx context.Context, id, kind string) ([]dto, error) {
321332 }322333 323334 func videoQuery(q url.Values) url.Values {324- q.Set("fields", "Overview,MediaSources")335+ q.Set("fields", "Overview,MediaSources,People,OfficialRating,Genres")325336 q.Set("excludeLocationTypes", "Virtual")326337 q.Set("enableUserData", "false")327338 return q
@@ -353,7 +364,20 @@ func (c *Client) video(d dto) Video {
353364 v := Video{354365 ID: d.ID, Source: d.ID, Type: d.Type, Name: d.Name, Series: d.SeriesName, SeriesID: d.SeriesID,355366 Season: d.ParentIndexNumber, Episode: d.IndexNumber, Year: d.ProductionYear, Overview: d.Overview,356- Runtime: ticks(d.RunTimeTicks), Audio: true, Image: c.artwork(d),367+ Runtime: ticks(d.RunTimeTicks), Audio: true, Image: c.artwork(d), Rating: d.OfficialRating, Genres: d.Genres,368+ }369+ // Actors come in billing order, guest stars after them.370+ for _, kind := range []string{"Actor", "GuestStar"} {371+ for _, p := range d.People {372+ if p.Type != kind || p.Name == "" || len(v.Cast) == 6 {373+ continue374+ }375+ if p.Role != "" {376+ v.Cast = append(v.Cast, p.Name+" ("+p.Role+")")377+ } else {378+ v.Cast = append(v.Cast, p.Name)379+ }380+ }357381 }358382 if len(d.PremiereDate) >= len("2006-01-02") {359383 v.Premiere = d.PremiereDate[:len("2006-01-02")]
M internal/jellyfin/jellyfin_test.go +5 -2
@@ -28,7 +28,8 @@ func library() []jellyfintest.Item {
2828 {ID: "fut-1-9", Type: "Episode", Name: "Not Downloaded", Parent: "fut-1", Season: 1, Episode: 9, Missing: true},2929 {ID: "btas", Type: "Series", Name: "Batman: The Animated Series", Parent: "lib-shows", Year: 1992},3030 {ID: "btas-1-1", Type: "Episode", Name: "On Leather Wings", Parent: "btas", Season: 1, Episode: 1, Runtime: 22 * time.Minute},31- {ID: "thing82", Type: "Movie", Name: "The Thing", Parent: "lib-movies", Year: 1982, Runtime: 109 * time.Minute, Image: true},31+ {ID: "thing82", Type: "Movie", Name: "The Thing", Parent: "lib-movies", Year: 1982, Runtime: 109 * time.Minute, Image: true,32+ Rating: "R", Genres: []string{"Horror"}, Cast: []string{"Kurt Russell/MacReady", "Wilford Brimley/Blair", "Keith David/"}},3233 {ID: "thing11", Type: "Movie", Name: "The Thing", Parent: "lib-movies", Year: 2011, Runtime: 103 * time.Minute},3334 {ID: "giant", Type: "Movie", Name: "The Iron Giant", Parent: "lib-movies", Year: 1999, Runtime: 86 * time.Minute},3435 {ID: "sat", Type: "BoxSet", Name: "Saturday Morning", Children: []string{"btas", "giant"}},
@@ -232,7 +233,9 @@ func TestVideos(t *testing.T) {
232233 if v := got["fut-1-2"]; v.Audio || !strings.Contains(v.Image, "/Items/fut/Images/Primary") {233234 t.Errorf("silent episode without a still = %+v", v)234235 }235- if v := got["thing82"]; v.Type != "Movie" || v.Year != 1982 || !strings.Contains(v.Image, "/Items/thing82/Images/Primary") {236+ if v := got["thing82"]; v.Type != "Movie" || v.Year != 1982 || !strings.Contains(v.Image, "/Items/thing82/Images/Primary") ||237+ v.Rating != "R" || !slices.Equal(v.Genres, []string{"Horror"}) ||238+ !slices.Equal(v.Cast, []string{"Kurt Russell (MacReady)", "Wilford Brimley (Blair)", "Keith David"}) {236239 t.Errorf("movie = %+v", v)237240 }238241 if v := got["giant"]; v.Image != "" {
M internal/jellyfin/jellyfintest/jellyfintest.go +21 -6
M internal/service/service.go +4 -0
M internal/vchan/folder_test.go +2 -1
M internal/vchan/jellyfin.go +2 -2
M internal/vchan/jellyfin_test.go +2 -1
M internal/vchan/loop.go +16 -1
M internal/vchan/vchan.go +7 -0
M internal/vchan/youtube_test.go +2 -1