Details pane: director and cast from Trakt
With a Trakt client ID, the details of a movie or series show the director(s) or creators and up to 8 cast members with their roles below the plot, in every view. The title is found on Trakt by its TMDb/TVDB (else IMDb) id, then its people are read. Casts are cached and, like posters, only loaded once the selection rests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+49
-2
@@ -11,6 +11,7 @@ import (
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/arr"
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/radarr"
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/sonarr"
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/trakt"
|
||||
)
|
||||
|
||||
// movieLabel is the one-line list entry for a movie.
|
||||
@@ -29,6 +30,9 @@ func movieLabel(m radarr.Movie) string {
|
||||
// state) and the plot below it.
|
||||
type detailText struct {
|
||||
head, plot string
|
||||
// people names the title for its cast, loaded from Trakt when it is shown
|
||||
// (zero: none).
|
||||
people trakt.Ref
|
||||
}
|
||||
|
||||
// String joins both parts, for places that show them as one text.
|
||||
@@ -39,6 +43,49 @@ func (d detailText) String() string {
|
||||
return d.head + "\n" + d.plot
|
||||
}
|
||||
|
||||
func movieRef(m radarr.Movie) trakt.Ref {
|
||||
return trakt.Ref{Kind: trakt.Movies, TMDB: m.TMDBID, IMDB: m.IMDBID}
|
||||
}
|
||||
|
||||
func seriesRef(s sonarr.Series) trakt.Ref {
|
||||
return trakt.Ref{Kind: trakt.Shows, TVDB: s.TVDBID, TMDB: s.TMDBID, IMDB: s.IMDBID}
|
||||
}
|
||||
|
||||
// peopleText renders the cast and directors (creators) of a title for the
|
||||
// details pane, at most maxCast actors.
|
||||
func peopleText(p trakt.People, kind trakt.Kind) string {
|
||||
const maxCast = 8
|
||||
var b strings.Builder
|
||||
if len(p.Leaders) > 0 {
|
||||
label := "Director"
|
||||
if kind == trakt.Shows {
|
||||
label = "Created by"
|
||||
} else if len(p.Leaders) > 1 {
|
||||
label = "Directors"
|
||||
}
|
||||
names := make([]string, len(p.Leaders))
|
||||
for i, l := range p.Leaders {
|
||||
names[i] = tview.Escape(l.Name)
|
||||
}
|
||||
fmt.Fprintf(&b, "[::b]%s[::-] %s\n", label, strings.Join(names, ", "))
|
||||
}
|
||||
if len(p.Cast) > 0 {
|
||||
b.WriteString("[::b]Cast[::-]\n")
|
||||
for i, c := range p.Cast {
|
||||
if i == maxCast {
|
||||
fmt.Fprintf(&b, "[gray]and %d more[-]\n", len(p.Cast)-maxCast)
|
||||
break
|
||||
}
|
||||
b.WriteString(tview.Escape(c.Name))
|
||||
if c.Role != "" {
|
||||
b.WriteString(" [gray]as " + tview.Escape(c.Role) + "[-]")
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
return strings.TrimRight(b.String(), "\n")
|
||||
}
|
||||
|
||||
// movieDetails renders the detail pane text for a movie.
|
||||
func movieDetails(m radarr.Movie) detailText {
|
||||
var b strings.Builder
|
||||
@@ -73,7 +120,7 @@ func movieDetails(m radarr.Movie) detailText {
|
||||
case m.InLibrary():
|
||||
b.WriteString("[yellow]In library, not downloaded[-]\n")
|
||||
}
|
||||
return detailText{head: b.String(), plot: tview.Escape(m.Overview)}
|
||||
return detailText{head: b.String(), plot: tview.Escape(m.Overview), people: movieRef(m)}
|
||||
}
|
||||
|
||||
// profileIndex returns the index of the profile named name, or 0.
|
||||
@@ -149,7 +196,7 @@ func seriesDetails(s sonarr.Series) detailText {
|
||||
fmt.Fprintf(&b, "[yellow]In library, %d/%d episodes downloaded[-]\n", st.EpisodeFileCount, st.EpisodeCount)
|
||||
}
|
||||
}
|
||||
return detailText{head: b.String(), plot: tview.Escape(s.Overview)}
|
||||
return detailText{head: b.String(), plot: tview.Escape(s.Overview), people: seriesRef(s)}
|
||||
}
|
||||
|
||||
func plural(n int) string {
|
||||
|
||||
Reference in New Issue
Block a user