Skip to content

Commit

Permalink
Clarify search results (#77)
Browse files Browse the repository at this point in the history
* feature: #76, clarify search results

* add: search now pro-actively searches until no more results are returned. It still queries in batches of 20, and updates the list(s) as results are available.

* add: search result counts in the column titles

* Stupid git.

---------

Signed-off-by: Sean Russell <[email protected]>
  • Loading branch information
xxxserxxx authored Oct 28, 2024
1 parent 3ea5da2 commit 4a8428b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 53 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,12 @@ The default is `▉▊▋▌▍▎▏▎▍▌▋▊▉`. Set only one of these

### Search Controls

The search page performs a server-side search for text in IDv3 metadata fields.
The search results are filtered into three columns: artist, album, and song. 20
results (in each column) are fetched at a time; use `n` to load more results.
The search tab performs a server-side search for text in metadata name fields. The search results are filtered into three columns: artist, album, and song, where each entry matches the query in name or title.

In any of the columns:

- `/`: Focus search field.
- `Enter` / `a`: Adds the selected item recursively to the queue.
- `n`: Load more search results.
- Left/right arrow keys (``, ``) navigate between the columns
- Up/down arrow keys (``, ``) navigate the selected column list

Expand Down
4 changes: 1 addition & 3 deletions help_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ artist, album, or song column
Left previous column
Right next column
Enter/a recursively add item to quue
/ start search (20 results per)
n load more results
/ start search
search field
Enter search for text
Esc cancel search
Expand Down
116 changes: 70 additions & 46 deletions page_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package main

import (
"fmt"
"sort"
"strings"

"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
Expand All @@ -27,10 +29,6 @@ type SearchPage struct {
albums []*subsonic.Album
songs []*subsonic.SubsonicEntity

artistOffset int
albumOffset int
songOffset int

// external refs
ui *Ui
logger logger.LoggerInterface
Expand All @@ -46,23 +44,23 @@ func (ui *Ui) createSearchPage() *SearchPage {
searchPage.artistList = tview.NewList().
ShowSecondaryText(false)
searchPage.artistList.Box.
SetTitle(" artist ").
SetTitle(" artist matches ").
SetTitleAlign(tview.AlignLeft).
SetBorder(true)

// album list
searchPage.albumList = tview.NewList().
ShowSecondaryText(false)
searchPage.albumList.Box.
SetTitle(" album ").
SetTitle(" album matches ").
SetTitleAlign(tview.AlignLeft).
SetBorder(true)

// song list
searchPage.songList = tview.NewList().
ShowSecondaryText(false)
searchPage.songList.Box.
SetTitle(" song ").
SetTitle(" song matches ").
SetTitleAlign(tview.AlignLeft).
SetBorder(true)

Expand Down Expand Up @@ -112,9 +110,6 @@ func (ui *Ui) createSearchPage() *SearchPage {
case '/':
searchPage.ui.app.SetFocus(searchPage.searchField)
return nil
case 'n':
searchPage.search()
return nil
}

return event
Expand Down Expand Up @@ -148,9 +143,6 @@ func (ui *Ui) createSearchPage() *SearchPage {
case '/':
searchPage.ui.app.SetFocus(searchPage.searchField)
return nil
case 'n':
searchPage.search()
return nil
}

return event
Expand Down Expand Up @@ -185,67 +177,99 @@ func (ui *Ui) createSearchPage() *SearchPage {
case '/':
searchPage.ui.app.SetFocus(searchPage.searchField)
return nil
case 'n':
searchPage.search()
return nil
}

return event
})
search := make(chan string, 5)
searchPage.searchField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyUp, tcell.KeyESC:
searchPage.aproposFocus()
case tcell.KeyEnter:
search <- ""
searchPage.artistList.Clear()
searchPage.artists = make([]*subsonic.Artist, 0)
searchPage.albumList.Clear()
searchPage.albums = make([]*subsonic.Album, 0)
searchPage.songList.Clear()
searchPage.songs = make([]*subsonic.SubsonicEntity, 0)

searchPage.artistOffset = 0
searchPage.albumOffset = 0
searchPage.songOffset = 0
searchPage.search()
searchPage.aproposFocus()
queryStr := searchPage.searchField.GetText()
search <- queryStr
default:
return event
}
return nil
})
go searchPage.search(search)

return &searchPage
}

func (s *SearchPage) search() {
if len(s.searchField.GetText()) == 0 {
return
}
query := s.searchField.GetText()
func (s *SearchPage) search(search chan string) {
var query string
var artOff, albOff, songOff int
more := make(chan bool, 5)
for {
// quit searching if we receive an interrupt
select {
case query = <-search:
artOff = 0
albOff = 0
songOff = 0
s.logger.Printf("searching for %q [%d, %d, %d]", query, artOff, albOff, songOff)
for len(more) > 0 {
<-more
}
if query == "" {
continue
}
case <-more:
s.logger.Printf("fetching more %q [%d, %d, %d]", query, artOff, albOff, songOff)
}
res, err := s.ui.connection.Search(query, artOff, albOff, songOff)
if err != nil {
s.logger.PrintError("SearchPage.search", err)
return
}
// Quit searching if there are no more results
if len(res.SearchResults.Artist) == 0 &&
len(res.SearchResults.Album) == 0 &&
len(res.SearchResults.Song) == 0 {
continue
}

res, err := s.ui.connection.Search(query, s.artistOffset, s.albumOffset, s.songOffset)
if err != nil {
s.logger.PrintError("SearchPage.search", err)
return
}
query = strings.ToLower(query)
s.ui.app.QueueUpdate(func() {
for _, artist := range res.SearchResults.Artist {
if strings.Contains(strings.ToLower(artist.Name), query) {
s.artistList.AddItem(tview.Escape(artist.Name), "", 0, nil)
s.artists = append(s.artists, &artist)
}
}
s.artistList.Box.SetTitle(fmt.Sprintf(" artist matches (%d) ", len(s.artists)))
for _, album := range res.SearchResults.Album {
if strings.Contains(strings.ToLower(album.Name), query) {
s.albumList.AddItem(tview.Escape(album.Name), "", 0, nil)
s.albums = append(s.albums, &album)
}
}
s.albumList.Box.SetTitle(fmt.Sprintf(" album matches (%d) ", len(s.albums)))
for _, song := range res.SearchResults.Song {
if strings.Contains(strings.ToLower(song.Title), query) {
s.songList.AddItem(tview.Escape(song.Title), "", 0, nil)
s.songs = append(s.songs, &song)
}
}
s.songList.Box.SetTitle(fmt.Sprintf(" song matches (%d) ", len(s.songs)))
})

for _, artist := range res.SearchResults.Artist {
s.artistList.AddItem(tview.Escape(artist.Name), "", 0, nil)
s.artists = append(s.artists, &artist)
artOff += len(res.SearchResults.Artist)
albOff += len(res.SearchResults.Album)
songOff += len(res.SearchResults.Song)
more <- true
}
for _, album := range res.SearchResults.Album {
s.albumList.AddItem(tview.Escape(album.Name), "", 0, nil)
s.albums = append(s.albums, &album)
}
for _, song := range res.SearchResults.Song {
s.songList.AddItem(tview.Escape(song.Title), "", 0, nil)
s.songs = append(s.songs, &song)
}

s.artistOffset += len(res.SearchResults.Artist)
s.albumOffset += len(res.SearchResults.Album)
s.songOffset += len(res.SearchResults.Song)
}

func (s *SearchPage) addArtistToQueue(entity subsonic.Ider) {
Expand Down

0 comments on commit 4a8428b

Please sign in to comment.