Series drill-down: poster and details of the selected season or episode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
This commit is contained in:
+56
-12
@@ -37,7 +37,8 @@ type seriesView struct {
|
||||
root *tview.Flex
|
||||
header *tview.TextView
|
||||
table *tview.Table
|
||||
detail *tview.TextView
|
||||
detail *detailPane // poster, series and the selected season or episode
|
||||
cover coverRef
|
||||
|
||||
episodes []sonarr.Episode
|
||||
expanded map[int]bool
|
||||
@@ -51,14 +52,16 @@ func (a *App) openSeries(c *sonarr.Client, s sonarr.Series, onChange func()) {
|
||||
app: a, c: c, series: s, onChange: onChange,
|
||||
header: tview.NewTextView().SetDynamicColors(true),
|
||||
table: tview.NewTable().SetSelectable(true, false).SetFixed(1, 0),
|
||||
detail: tview.NewTextView().SetDynamicColors(true).SetWordWrap(true),
|
||||
detail: newDetailPane(a),
|
||||
expanded: map[int]bool{},
|
||||
}
|
||||
v.table.SetBorder(true)
|
||||
v.cover = coverFor(c.Client, s.Images, true)
|
||||
v.root = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(v.header, 1, 0, false).
|
||||
AddItem(v.table, 0, 1, true).
|
||||
AddItem(v.detail, 2, 0, false)
|
||||
AddItem(tview.NewFlex().
|
||||
AddItem(v.table, 0, 3, true).
|
||||
AddItem(v.detail, 0, 2, false), 0, 1, true)
|
||||
|
||||
v.table.SetSelectionChangedFunc(func(int, int) { v.renderDetail() })
|
||||
v.table.SetSelectedFunc(func(int, int) { v.enter() })
|
||||
@@ -86,7 +89,8 @@ func (a *App) openSeries(c *sonarr.Client, s sonarr.Series, onChange func()) {
|
||||
})
|
||||
|
||||
v.render()
|
||||
a.openFull(v.root, "Enter expand/releases · i releases · s search · t monitor · r reload · Esc back", nil)
|
||||
// The pane goes away with the view; stop restyling it then.
|
||||
a.openFull(v.root, "Enter expand/releases · i releases · s search · t monitor · r reload · Esc back", func() { a.forgetPane(v.detail) })
|
||||
v.reload()
|
||||
}
|
||||
|
||||
@@ -243,14 +247,54 @@ func episodeStatus(e sonarr.Episode, today string) string {
|
||||
|
||||
func (v *seriesView) renderDetail() {
|
||||
r, ok := v.selected()
|
||||
switch {
|
||||
case !ok:
|
||||
v.detail.SetText("")
|
||||
case r.ep == nil:
|
||||
v.detail.SetText("[gray]Enter expands the season · t toggles monitoring for the whole season · i searches for releases")
|
||||
default:
|
||||
v.detail.SetText(fmt.Sprintf("[gray]S%02dE%02d[-] %s", r.season, r.ep.EpisodeNumber, tview.Escape(r.ep.Title)))
|
||||
if !ok {
|
||||
v.detail.Show(seriesRowDetail(v.series, nil, 0, seasonSummary{}, false, ""), v.cover)
|
||||
return
|
||||
}
|
||||
var sum seasonSummary
|
||||
for _, se := range summarizeSeasons(v.episodes) {
|
||||
if se.number == r.season {
|
||||
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)
|
||||
}
|
||||
|
||||
// seriesRowDetail is the detail text of the drill-down: the series, then the
|
||||
// selected season (ep == nil) or episode. The plot is the episode's, or the
|
||||
// series' while a season is selected.
|
||||
func seriesRowDetail(s sonarr.Series, ep *sonarr.Episode, season int, sum seasonSummary, seasonMonitored bool, today string) detailText {
|
||||
head := "[::b]" + tview.Escape(s.Title) + "[::-]"
|
||||
if s.Year > 0 {
|
||||
head += fmt.Sprintf(" (%d)", s.Year)
|
||||
}
|
||||
head += "\n"
|
||||
if s.Network != "" {
|
||||
head += tview.Escape(s.Network) + " · "
|
||||
}
|
||||
head += tview.Escape(s.Status) + "\n\n"
|
||||
|
||||
plot := tview.Escape(s.Overview)
|
||||
switch {
|
||||
case ep == nil && sum.total == 0:
|
||||
// Nothing selected yet (still loading).
|
||||
case ep == nil:
|
||||
head += "[::b]" + seasonLabel(season) + "[::-]\n"
|
||||
head += fmt.Sprintf("%d of %d episodes downloaded\n", sum.have, sum.total)
|
||||
head += monitoredLine(seasonMonitored)
|
||||
head += "[gray]Enter expands · t monitors the whole season · i searches for releases[-]\n"
|
||||
default:
|
||||
head += fmt.Sprintf("[::b]S%02dE%02d %s[::-]\n", ep.SeasonNumber, ep.EpisodeNumber, tview.Escape(ep.Title))
|
||||
if ep.AirDate != "" {
|
||||
head += "Aired: " + ep.AirDate + "\n"
|
||||
}
|
||||
head += "Status: " + episodeStatus(*ep, today) + "[-]\n"
|
||||
head += monitoredLine(ep.Monitored)
|
||||
if ep.Overview != "" {
|
||||
plot = tview.Escape(ep.Overview)
|
||||
}
|
||||
}
|
||||
return detailText{head: head, plot: plot}
|
||||
}
|
||||
|
||||
// enter expands or collapses a season, or opens the release search for an episode.
|
||||
|
||||
Reference in New Issue
Block a user