aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend.go6
-rw-r--r--localBackend.go21
-rw-r--r--main.go20
-rw-r--r--player.go4
-rw-r--r--spotify.go15
-rw-r--r--upload.go4
-rw-r--r--wrms.go34
-rw-r--r--youtube.go9
8 files changed, 81 insertions, 32 deletions
diff --git a/backend.go b/backend.go
index 9f778b2..dd34fcc 100644
--- a/backend.go
+++ b/backend.go
@@ -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()
diff --git a/main.go b/main.go
index fd390bf..e4f7bad 100644
--- a/main.go
+++ b/main.go
@@ -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) {
diff --git a/player.go b/player.go
index 490fb0a..461677c 100644
--- a/player.go
+++ b/player.go
@@ -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))
diff --git a/spotify.go b/spotify.go
index da7f6da..324f645 100644
--- a/spotify.go
+++ b/spotify.go
@@ -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
diff --git a/upload.go b/upload.go
index 2d62f1c..68c6adb 100644
--- a/upload.go
+++ b/upload.go
@@ -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
}
diff --git a/wrms.go b/wrms.go
index 9371d93..056848c 100644
--- a/wrms.go
+++ b/wrms.go
@@ -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)
+}
diff --git a/youtube.go b/youtube.go
index 8b29043..1b5bda6 100644
--- a/youtube.go
+++ b/youtube.go
@@ -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()