diff options
| -rw-r--r-- | backend.go | 6 | ||||
| -rw-r--r-- | localBackend.go | 21 | ||||
| -rw-r--r-- | main.go | 20 | ||||
| -rw-r--r-- | player.go | 4 | ||||
| -rw-r--r-- | spotify.go | 15 | ||||
| -rw-r--r-- | upload.go | 4 | ||||
| -rw-r--r-- | wrms.go | 34 | ||||
| -rw-r--r-- | youtube.go | 9 |
8 files changed, 81 insertions, 32 deletions
@@ -7,7 +7,7 @@ import ( type Backend interface { Play(song *Song, player Player) - Search(keyword string) []Song + Search(map[string]string) []Song OnSongFinished(song *Song) } @@ -15,8 +15,8 @@ type DummyBackend struct{} func (dummy *DummyBackend) Play(song *Song, player Player) {} func (dummy *DummyBackend) OnSongFinished(song *Song) {} -func (dummy *DummyBackend) Search(keyword string) []Song { - s := NewDummySong("Dummy Mc Crashtest", "exactly "+keyword) +func (dummy *DummyBackend) Search(map[string]string) []Song { + s := NewDummySong("Dummy Mc Crashtest", "foo") return []Song{s} } diff --git a/localBackend.go b/localBackend.go index 19be1b4..cb07ab1 100644 --- a/localBackend.go +++ b/localBackend.go @@ -33,7 +33,9 @@ func NewLocalBackend(musicDir string) *LocalBackend { CREATE TABLE songs ( Uri text NOT NULL PRIMARY KEY, Title text, - Artist text + Artist text, + Album text, + Year int ); ` _, err = b.db.Exec(sqlStmt) @@ -54,14 +56,14 @@ func (b *LocalBackend) insert(songs []*Song) { llog.Fatal("Starting insert transaction failed: %q", err) } - stmt, err := tx.Prepare("INSERT INTO songs(Uri, Title, Artist) VALUES(?, ?, ?)") + stmt, err := tx.Prepare("INSERT INTO songs(Uri, Title, Artist, Album, Year) VALUES(?, ?, ?, ?, ?)") if err != nil { llog.Fatal("Preparing inser statement failed: %q", err) } defer stmt.Close() for _, song := range songs { - _, err = stmt.Exec(song.Uri, song.Title, song.Artist) + _, err = stmt.Exec(song.Uri, song.Title, song.Artist, song.Album, song.Year) if err != nil { llog.Fatal("Executing insert statement failed: %q", err) } @@ -110,6 +112,8 @@ func (b *LocalBackend) findSongs() { } s := NewSong(m.Title(), m.Artist(), "local", p) + s.Album = m.Album() + s.Year = m.Year() songs = append(songs, &s) return nil }) @@ -125,8 +129,8 @@ func (b *LocalBackend) Play(song *Song, player Player) { player.PlayUri("file://" + song.Uri) } -func (b *LocalBackend) Search(keyword string) (results []Song) { - pattern := strings.ToLower(keyword) +func (b *LocalBackend) Search(patterns map[string]string) (results []Song) { + pattern := strings.ToLower(patterns["pattern"]) query := fmt.Sprintf("SELECT * FROM songs WHERE Title LIKE '%%%s%%' OR Artist LIKE '%%%s%%'", pattern, pattern) @@ -142,13 +146,16 @@ func (b *LocalBackend) Search(keyword string) (results []Song) { var uri string var title string var artist string + var album string + var year int - err = rows.Scan(&uri, &title, &artist) + err = rows.Scan(&uri, &title, &artist, &album, &year) if err != nil { llog.Warning("Scanning query result failed: %q", err) } - results = append(results, NewSong(title, artist, "local", uri)) + s := NewDetailedSong(title, artist, "local", uri, album, year) + results = append(results, s) } err = rows.Err() @@ -75,18 +75,26 @@ func searchHandler(w http.ResponseWriter, r *http.Request) { return } - pattern := r.URL.Query().Get("pattern") - if pattern == "" { + searchQuery := map[string]string{} + patterns := []string{"pattern", "title", "album", "artist"} + for _, pattern := range patterns { + value := r.URL.Query().Get(pattern) + if value != "" { + searchQuery[pattern] = value + } + } + + if len(searchQuery) == 0 { http.Error(w, "No search pattern provided", http.StatusBadRequest) return } id := wrms.eventId.Add(1) - llog.Debug("Searching for %s", pattern) + llog.Debug("Searching for %v", searchQuery) start := time.Now() - resultsChan := wrms.Player.Search(pattern) + resultsChan := wrms.Search(searchQuery) go func() { for result := range resultsChan { @@ -96,10 +104,10 @@ func searchHandler(w http.ResponseWriter, r *http.Request) { } conn.Send(Event{Event: "finish-search", Id: id}) - llog.Debug("searching for %s took %v", pattern, time.Since(start)) + llog.Debug("searching for %v took %v", searchQuery, time.Since(start)) }() - fmt.Fprintf(w, "Starting search for %s", pattern) + fmt.Fprintf(w, "Starting search for %v", searchQuery) } func genericVoteHandler(w http.ResponseWriter, r *http.Request, vote string) { @@ -14,7 +14,7 @@ import ( type Player interface { Play(*Song) Playing() bool - Search(string) chan []Song + Search(map[string]string) chan []Song PlayUri(string) PlayData(io.Reader) Pause() @@ -232,7 +232,7 @@ func (player *MpvPlayer) _stop() { player.mpv.Store(nil) } -func (player *MpvPlayer) Search(pattern string) chan []Song { +func (player *MpvPlayer) Search(pattern map[string]string) chan []Song { var wg sync.WaitGroup wg.Add(len(player.Backends)) @@ -96,8 +96,16 @@ func (spotify *SpotifyBackend) Play(song *Song, player Player) { player.PlayData(audioFile) } -func (spotify *SpotifyBackend) Search(keyword string) []Song { +func (spotify *SpotifyBackend) Search(patterns map[string]string) []Song { session := spotify.session + keyword := "" + for _, p := range []string{"pattern", "title", "artist", "album"} { + if v, ok := patterns[p]; ok { + keyword = v + break + } + } + resp, err := session.Mercury().Search(keyword, 12, session.Country(), session.Username()) if err != nil { @@ -119,8 +127,9 @@ func (spotify *SpotifyBackend) Search(keyword string) []Song { results := []Song{} for _, track := range res.Tracks.Hits { uriParts := strings.Split(track.Uri, ":") - results = append(results, NewSong(track.Name, track.Artists[0].Name, - "spotify", uriParts[len(uriParts)-1])) + s := NewSong(track.Name, track.Artists[0].Name, "spotify", uriParts[len(uriParts)-1]) + s.Album = track.Album.Name + results = append(results, s) } return results @@ -77,7 +77,7 @@ func (b *UploadBackend) upload(w http.ResponseWriter, r *http.Request) { artist = "Unknown" } - wrms.AddSong(NewSong(title, artist, "upload", fileName)) + wrms.AddSong(NewDetailedSong(title, artist, "upload", fileName, m.Album(), m.Year())) fmt.Fprintf(w, "Added uploaded song %s", string(fileName)) } @@ -95,6 +95,6 @@ func (b *UploadBackend) Play(song *Song, player Player) { player.PlayUri("file://" + path.Join(b.uploadDir, song.Uri)) } -func (b *UploadBackend) Search(keyword string) []Song { +func (b *UploadBackend) Search(map[string]string) []Song { return nil } @@ -19,13 +19,29 @@ type Song struct { Source string `json:"source"` Uri string `json:"uri"` Weight int `json:"weight"` + Album string `json:"album"` + Year int `json:"year"` index int `json:"-"` // used by heap.Interface Upvotes map[uuid.UUID]struct{} `json:"-"` Downvotes map[uuid.UUID]struct{} `json:"-"` } func NewSong(title, artist, source, uri string) Song { - return Song{title, artist, source, uri, 0, 0, map[uuid.UUID]struct{}{}, map[uuid.UUID]struct{}{}} + return Song{ + Title: title, + Artist: artist, + Source: source, + Uri: uri, + Upvotes: map[uuid.UUID]struct{}{}, + Downvotes: map[uuid.UUID]struct{}{}, + } +} + +func NewDetailedSong(title, artist, source, uri, album string, year int) Song { + s := NewSong(title, artist, source, uri) + s.Album = album + s.Year = year + return s } func NewSongFromJson(data []byte) (Song, error) { @@ -165,7 +181,7 @@ type Wrms struct { queue Playlist CurrentSong atomic.Pointer[Song] Player Player - Playing bool + playing bool Config Config eventId atomic.Uint64 } @@ -197,7 +213,7 @@ func (wrms *Wrms) initConn(conn *Connection) error { conn.nextEvent = curEventId + 1 initialCmds := []Event{} - if wrms.Playing { + if wrms.playing { var songs []Song if currentSong := wrms.CurrentSong.Load(); currentSong != nil { songs = []Song{*currentSong} @@ -261,7 +277,7 @@ func (wrms *Wrms) Broadcast(ev Event) { func (wrms *Wrms) AddSong(song Song) { wrms.rwlock.Lock() - startPlayingAgain := wrms.Playing && wrms.CurrentSong.Load() == nil + startPlayingAgain := wrms.playing && wrms.CurrentSong.Load() == nil wrms.Songs = append(wrms.Songs, song) s := &wrms.Songs[len(wrms.Songs)-1] wrms.queue.Add(s) @@ -338,7 +354,7 @@ func (wrms *Wrms) _next() { cmd := "next" // We are playing -> start playing the next song - if wrms.Playing { + if wrms.playing { wrms.Player.Play(next) cmd = "play" } @@ -351,10 +367,10 @@ func (wrms *Wrms) _next() { func (wrms *Wrms) PlayPause() { wrms.rwlock.Lock() // Toggle the playback state - wrms.Playing = !wrms.Playing + wrms.playing = !wrms.playing // Wrms was playing -> pause the player - if !wrms.Playing { + if !wrms.playing { wrms.Player.Pause() wrms.rwlock.Unlock() wrms.Broadcast(newNotification("pause")) @@ -452,3 +468,7 @@ func (wrms *Wrms) AdjustSongWeight(connId uuid.UUID, songUri string, vote string break } } + +func (wrms *Wrms) Search(pattern map[string]string) chan []Song { + return wrms.Player.Search(pattern) +} @@ -27,8 +27,13 @@ type YoutubeDlSearchResult struct { Title string } -func (b *YoutubeBackend) Search(pattern string) []Song { - searchOption := fmt.Sprintf("ytsearch%d:%s", b.searchResults, pattern) +func (b *YoutubeBackend) Search(patterns map[string]string) []Song { + pattern := "" + for _, v := range patterns { + pattern += v + " " + } + + searchOption := fmt.Sprintf("ytsearch%d:%s", b.searchResults, pattern[:len(pattern)-1]) llog.Debug("Search youtube using: youtube-dl -j %s", searchOption) results, err := exec.Command("yt-dlp", "-j", searchOption).Output() |
