Trending, most watched and anticipated movies and series from Trakt
With an empty search box, Movies and Series show one of Trakt's public lists: trending now, most watched this week, anticipated, popular, and for movies the US box office. [ ] switch lists, r reloads. Each entry is looked up in Radarr (tmdb:) or Sonarr (tvdb:), so it behaves like a search result: details, poster, tick when in the library, add, releases. Needs a Trakt client ID: new optional trakt.client_id setting (Config view section "Trakt", ARR_TUI_TRAKT_CLIENT_ID), masked like the API keys and checked by Test. ARR_TUI_TRAKT_URL points at another server for development. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+23
-3
@@ -13,6 +13,7 @@ import (
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/arr"
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/config"
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/sabnzbd"
|
||||
"git.stinnesbeck.com/nils/arr-tui/internal/trakt"
|
||||
)
|
||||
|
||||
// settingsValues are the settings as shown in the Config view.
|
||||
@@ -20,6 +21,7 @@ type settingsValues struct {
|
||||
radarrURL, radarrKey, radarrProfile, radarrFolder string
|
||||
sonarrURL, sonarrKey, sonarrProfile, sonarrFolder string
|
||||
sabURL, sabKey string
|
||||
traktID string
|
||||
refresh string // seconds, as typed
|
||||
covers bool
|
||||
coverStyle string
|
||||
@@ -47,6 +49,7 @@ func settingsFrom(c *config.Config) settingsValues {
|
||||
sonarrURL: c.Sonarr.URL, sonarrKey: c.Sonarr.APIKey,
|
||||
sonarrProfile: c.Sonarr.DefaultQualityProfile, sonarrFolder: c.Sonarr.DefaultRootFolder,
|
||||
sabURL: c.SABnzbd.URL, sabKey: c.SABnzbd.APIKey,
|
||||
traktID: c.Trakt.ClientID,
|
||||
refresh: strconv.Itoa(c.UI.RefreshSeconds),
|
||||
covers: c.UI.ShowCovers(), coverStyle: style,
|
||||
notify: c.UI.ShouldNotifyDownloads(), bell: c.UI.Bell,
|
||||
@@ -70,6 +73,7 @@ func applySettings(raw config.Config, v settingsValues, overridden map[string]st
|
||||
set("sonarr.apikey", &raw.Sonarr.APIKey, v.sonarrKey)
|
||||
set("sabnzbd.url", &raw.SABnzbd.URL, v.sabURL)
|
||||
set("sabnzbd.apikey", &raw.SABnzbd.APIKey, v.sabKey)
|
||||
set("trakt.client_id", &raw.Trakt.ClientID, v.traktID)
|
||||
raw.Radarr.DefaultQualityProfile = strings.TrimSpace(v.radarrProfile)
|
||||
raw.Radarr.DefaultRootFolder = strings.TrimSpace(v.radarrFolder)
|
||||
raw.Sonarr.DefaultQualityProfile = strings.TrimSpace(v.sonarrProfile)
|
||||
@@ -161,7 +165,7 @@ func newSettingsView(app *App) *settingsView {
|
||||
|
||||
input := func(label, key string) {
|
||||
in := tview.NewInputField().SetLabel(label)
|
||||
if strings.HasSuffix(key, ".apikey") {
|
||||
if secret(key) {
|
||||
in.SetMaskCharacter('*')
|
||||
}
|
||||
if _, ok := v.overridden[key]; ok {
|
||||
@@ -195,6 +199,8 @@ func newSettingsView(app *App) *settingsView {
|
||||
service("Radarr", "radarr", true)
|
||||
service("Sonarr", "sonarr", true)
|
||||
service("SABnzbd", "sabnzbd", false)
|
||||
f.AddFormItem(newFormHeading("Trakt (optional: trending, most watched, anticipated)"))
|
||||
input("Client ID", "trakt.client_id")
|
||||
// Right below the connection settings it checks.
|
||||
v.testItem = newFormAction("Test", v.test)
|
||||
f.AddFormItem(v.testItem)
|
||||
@@ -295,10 +301,13 @@ func (v *settingsView) edited() {
|
||||
}
|
||||
}
|
||||
|
||||
// secret reports whether a setting is masked like a password.
|
||||
func secret(key string) bool { return strings.HasSuffix(key, ".apikey") || key == "trakt.client_id" }
|
||||
|
||||
// maskKeys hides or shows the API keys.
|
||||
func (v *settingsView) maskKeys(hide bool) {
|
||||
for key, in := range v.inputs {
|
||||
if strings.HasSuffix(key, ".apikey") {
|
||||
if secret(key) {
|
||||
if hide {
|
||||
in.SetMaskCharacter('*')
|
||||
} else {
|
||||
@@ -316,6 +325,7 @@ func (v *settingsView) fill(s settingsValues) {
|
||||
"radarr.url": s.radarrURL, "radarr.apikey": s.radarrKey,
|
||||
"sonarr.url": s.sonarrURL, "sonarr.apikey": s.sonarrKey,
|
||||
"sabnzbd.url": s.sabURL, "sabnzbd.apikey": s.sabKey,
|
||||
"trakt.client_id": s.traktID,
|
||||
}
|
||||
for key, value := range set {
|
||||
v.inputs[key].SetText(value)
|
||||
@@ -366,6 +376,7 @@ func (v *settingsView) read() settingsValues {
|
||||
sonarrURL: text("sonarr.url"), sonarrKey: text("sonarr.apikey"),
|
||||
sonarrProfile: text("sonarr.profile"), sonarrFolder: text("sonarr.folder"),
|
||||
sabURL: text("sabnzbd.url"), sabKey: text("sabnzbd.apikey"),
|
||||
traktID: text("trakt.client_id"),
|
||||
refresh: v.refresh.GetText(),
|
||||
covers: v.covers.IsChecked(), coverStyle: style,
|
||||
notify: v.notify.IsChecked(), bell: v.bell.IsChecked(),
|
||||
@@ -441,6 +452,15 @@ func (v *settingsView) test() {
|
||||
return sabnzbd.NewClient(strings.TrimSpace(s.sabURL), strings.TrimSpace(s.sabKey)).Version(ctx)
|
||||
}},
|
||||
}
|
||||
if strings.TrimSpace(s.traktID) != "" {
|
||||
tests = append(tests, struct {
|
||||
name string
|
||||
run probeFn
|
||||
}{"Trakt", func(ctx context.Context) (string, error) {
|
||||
_, err := trakt.NewClient(s.traktID, "arr-tui").Entries(ctx, trakt.MovieLists[0], 1)
|
||||
return "", err
|
||||
}})
|
||||
}
|
||||
for _, t := range tests {
|
||||
go func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -451,7 +471,7 @@ func (v *settingsView) test() {
|
||||
v.report("[red]✗ " + t.name + ": " + tview.Escape(err.Error()) + "[-]")
|
||||
return
|
||||
}
|
||||
v.report("[green]✓ " + t.name + " " + tview.Escape(version) + " answers[-]")
|
||||
v.report("[green]✓ " + strings.TrimSpace(t.name+" "+tview.Escape(version)) + " answers[-]")
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user