Release list: filter by quality, codec, HDR, DV and language
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+32
-4
@@ -42,6 +42,7 @@ type releasesView struct {
|
||||
searched bool // the indexer search has finished
|
||||
sortMode int
|
||||
hideRejected bool
|
||||
attr attrFilter
|
||||
}
|
||||
|
||||
// openReleases starts an interactive search and shows the results as they arrive.
|
||||
@@ -71,6 +72,11 @@ func (a *App) openReleases(title string, fetch func(context.Context) ([]arr.Rele
|
||||
case ev.Key() == tcell.KeyRune && ev.Rune() == 's':
|
||||
v.sortMode = (v.sortMode + 1) % numSortModes
|
||||
v.render()
|
||||
case ev.Key() == tcell.KeyRune && ev.Rune() == 'F':
|
||||
v.app.openAttrFilter(v.attr, releaseLanguages(v.all), func(f attrFilter) {
|
||||
v.attr = f
|
||||
v.render()
|
||||
})
|
||||
case ev.Key() == tcell.KeyRune && ev.Rune() == 'f':
|
||||
v.hideRejected = !v.hideRejected
|
||||
v.render()
|
||||
@@ -83,7 +89,7 @@ func (a *App) openReleases(title string, fetch func(context.Context) ([]arr.Rele
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
||||
v.cancel = cancel
|
||||
v.render() // shows the "searching" placeholder
|
||||
a.openFull(v.root, "Enter/g grab · s sort · f hide rejected · Esc back", v.cancel)
|
||||
a.openFull(v.root, "Enter/g grab · s sort · f hide rejected · F filter · Esc back", v.cancel)
|
||||
|
||||
go func() {
|
||||
releases, err := fetch(ctx)
|
||||
@@ -125,7 +131,7 @@ func (v *releasesView) render() {
|
||||
if r, ok := v.selected(); ok {
|
||||
selGUID = r.GUID
|
||||
}
|
||||
v.shown = visibleReleases(v.all, v.hideRejected, v.sortMode)
|
||||
v.shown = visibleReleases(v.all, v.hideRejected, v.sortMode, v.attr)
|
||||
|
||||
v.table.Clear()
|
||||
for i, c := range []string{"", "Title", "Quality", "Size", "Age", "Indexer", "Score"} {
|
||||
@@ -158,7 +164,7 @@ func (v *releasesView) render() {
|
||||
case len(v.shown) == 0:
|
||||
msg := "No releases found. Are indexers configured?"
|
||||
if len(v.all) > 0 {
|
||||
msg = "All releases are hidden by the filter (f)"
|
||||
msg = "All releases are hidden by the filters (f, F)"
|
||||
}
|
||||
v.table.SetCell(1, 1, tview.NewTableCell("[gray]"+msg).SetSelectable(false))
|
||||
}
|
||||
@@ -187,6 +193,9 @@ func (v *releasesView) renderHeader() {
|
||||
if v.hideRejected {
|
||||
s += " · [yellow]rejected hidden[-]"
|
||||
}
|
||||
if v.attr.active() {
|
||||
s += " · [yellow]" + v.attr.String() + "[-]"
|
||||
}
|
||||
if v.torrents > 0 {
|
||||
s += fmt.Sprintf(" · [gray]%d torrent release%s hidden[-]", v.torrents, plural(v.torrents))
|
||||
}
|
||||
@@ -265,12 +274,15 @@ func (v *releasesView) doGrab(r arr.Release) {
|
||||
}
|
||||
|
||||
// visibleReleases applies the filter and sort order, returning a new slice.
|
||||
func visibleReleases(all []arr.Release, hideRejected bool, mode int) []arr.Release {
|
||||
func visibleReleases(all []arr.Release, hideRejected bool, mode int, af attrFilter) []arr.Release {
|
||||
out := make([]arr.Release, 0, len(all))
|
||||
for _, r := range all {
|
||||
if hideRejected && isRejected(r) {
|
||||
continue
|
||||
}
|
||||
if af.active() && !af.matchFile(r.Attrs()) {
|
||||
continue
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
slices.SortStableFunc(out, func(a, b arr.Release) int {
|
||||
@@ -336,3 +348,19 @@ func formatReleaseAge(minutes float64) string {
|
||||
}
|
||||
return fmt.Sprintf("%dd", int(minutes/(24*60)))
|
||||
}
|
||||
|
||||
// releaseLanguages lists the languages the releases announce, sorted.
|
||||
func releaseLanguages(all []arr.Release) []string {
|
||||
seen := map[string]bool{}
|
||||
var out []string
|
||||
for _, r := range all {
|
||||
for _, l := range r.Attrs().Languages {
|
||||
if !seen[l] {
|
||||
seen[l] = true
|
||||
out = append(out, l)
|
||||
}
|
||||
}
|
||||
}
|
||||
slices.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user