aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-05-21 16:08:23 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2023-05-21 16:14:02 +0200
commiteb846695264d871edffb0e09b6d57a1a85793f48 (patch)
tree54ccd99fbcedabc0947cd1b658dae40ca677e9a0
parentd4dbb47ac776471eb8a9f91a97da40b9b6621a74 (diff)
downloadWRMS-eb846695264d871edffb0e09b6d57a1a85793f48.tar.gz
WRMS-eb846695264d871edffb0e09b6d57a1a85793f48.zip
implement incremental search results
TODO: ensure late search results do not show up for future searches
-rw-r--r--TODO6
-rw-r--r--main.go49
-rw-r--r--web/client.html25
-rw-r--r--wrms.go32
4 files changed, 75 insertions, 37 deletions
diff --git a/TODO b/TODO
index ed29307..1568877 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,6 @@
## Fronted
-- [ ] support incremental search results
+- [X] support incremental search results
+ - [ ] distinguish different search queries
## Web Backend
@@ -8,7 +9,8 @@
## Backends
- [X] paralyze search
- - [ ] report results incrementally
+ - [X] report results incrementally
+ - [ ] distinguish different search queries
- [X] Implement local storage backend
- [X] Implement upload
- [ ] Remove uploaded songs after were played
diff --git a/main.go b/main.go
index 94e11b9..69808bf 100644
--- a/main.go
+++ b/main.go
@@ -32,38 +32,49 @@ func landingPage(w http.ResponseWriter, r *http.Request) {
}
}
+func getConnId(w http.ResponseWriter, r *http.Request) string {
+ uuidCookie, err := r.Cookie("UUID")
+ if err != nil {
+ http.Error(w, "No connection ID cookie set", http.StatusUnauthorized)
+ return ""
+ }
+ return uuidCookie.Value
+}
+
func searchHandler(w http.ResponseWriter, r *http.Request) {
+ connId := getConnId(w, r)
+ if connId == "" {
+ return
+ }
+
+ conn := wrms.GetConn(connId)
+
pattern := r.URL.Query().Get("pattern")
llog.Debug("Searching for %s", pattern)
start := time.Now()
resultsChan := wrms.Player.Search(pattern)
- results := []Song{}
- for result := range resultsChan {
- results = append(results, result...)
- }
-
- llog.Debug("searching for %s took %v", pattern, time.Since(start))
+ go func() {
+ for result := range resultsChan {
+ if len(result) > 0 {
+ conn.Send(Event{"search", result})
+ }
+ }
- data, err := json.Marshal(Event{"search", results})
- if err != nil {
- llog.Error("Encoding the search result failed with %s", err)
- return
- }
+ conn.Send(newNotification("finish-search"))
+ llog.Debug("searching for %s took %v", pattern, time.Since(start))
+ }()
- llog.Info("Search returned: %s", string(data))
- w.Write(data)
+ fmt.Fprintf(w, "Starting search for %s", pattern)
}
func genericVoteHandler(w http.ResponseWriter, r *http.Request, vote string) {
- uuidCookie, err := r.Cookie("UUID")
- if err != nil {
- http.Error(w, "No connection ID cookie set", http.StatusUnauthorized)
+ connId := getConnId(w, r)
+ if connId == "" {
return
}
- connId := uuidCookie.Value
songUri := r.URL.Query().Get("song")
llog.Info("%s song %s via url %s", vote, songUri, r.URL)
@@ -131,8 +142,8 @@ func wsEndpoint(w http.ResponseWriter, r *http.Request) {
cancel()
}()
- conn := Connection{id, make(chan Event), c}
- wrms.Connections = append(wrms.Connections, conn)
+ conn := &Connection{id, make(chan Event), c}
+ wrms.Connections[id] = conn
llog.Info("New websocket connection with id %d", id)
diff --git a/web/client.html b/web/client.html
index f0ec6ed..878bd48 100644
--- a/web/client.html
+++ b/web/client.html
@@ -224,6 +224,12 @@
case "downvoted":
handleVotes("down", cmd.songs)
break;
+ case "search":
+ handleSearch(cmd.songs)
+ break;
+ case "finish-search":
+ handleFinishSearch()
+ break;
}
};
@@ -232,6 +238,10 @@
};
function submitSearch() {
+ // Clear search Results
+ let searchResults = document.getElementById("searchResults");
+ searchResults.innerHTML = "";
+
let searchingIndicator = document.getElementById("searching");
searchingIndicator.style.display = "block";
let searchResultsOverlay = document.getElementById("searchResultsOverlay");
@@ -239,7 +249,7 @@
const form = new FormData(document.getElementById("searchForm"));
const pattern = new URLSearchParams(form).toString();
- new HttpClient().get("/search?" + pattern, showSearchResults);
+ new HttpClient().get("/search?" + pattern, console.log);
return false;
}
@@ -259,20 +269,17 @@
new HttpClient().post("/add", JSON.stringify(song), console.log);
}
- function showSearchResults(responseText) {
- response = JSON.parse(responseText)
- if (!response)
- return
-
+ function handleFinishSearch() {
let searchingIndicator = document.getElementById("searching");
searchingIndicator.style.display = "none";
let searchResultsOverlay = document.getElementById("searchResultsOverlay");
searchResultsOverlay['aria-busy'] = false;
+ }
+ function handleSearch(songs) {
+ let searchResultsOverlay = document.getElementById("searchResultsOverlay");
let searchResults = document.getElementById("searchResults");
- // Clear search Results
- searchResults.innerHTML = "";
- for (const song of response.songs) {
+ for (const song of songs) {
let listItem = document.createElement("li");
listItem.addEventListener("click", function() {
addSong(song);
diff --git a/wrms.go b/wrms.go
index 9cd0ac4..22cd62a 100644
--- a/wrms.go
+++ b/wrms.go
@@ -44,12 +44,20 @@ type Event struct {
Songs []Song `json:"songs"`
}
+func newNotification(notification string) Event {
+ return Event{notification, nil}
+}
+
type Connection struct {
Id uuid.UUID
Events chan Event
C *websocket.Conn
}
+func (c *Connection) Send(ev Event) {
+ c.Events <- ev
+}
+
func (c *Connection) Close() {
llog.Info("Closing connection %s", c.Id)
close(c.Events)
@@ -57,7 +65,7 @@ func (c *Connection) Close() {
}
type Wrms struct {
- Connections []Connection
+ Connections map[uuid.UUID]*Connection
Songs []Song
queue Playlist
CurrentSong Song
@@ -69,10 +77,20 @@ type Wrms struct {
func NewWrms(config Config) *Wrms {
wrms := Wrms{}
wrms.Config = config
+ wrms.Connections = make(map[uuid.UUID]*Connection)
wrms.Player = NewPlayer(&wrms, strings.Split(config.Backends, " "))
return &wrms
}
+func (wrms *Wrms) GetConn(connId string) *Connection {
+ id, err := uuid.Parse(connId)
+ if err != nil {
+ llog.Fatal("Failed to parse connId %s: %v", connId, err)
+ }
+
+ return wrms.Connections[id]
+}
+
func (wrms *Wrms) Close() {
llog.Info("Closing WRMS")
for _, conn := range wrms.Connections {
@@ -80,10 +98,10 @@ func (wrms *Wrms) Close() {
}
}
-func (wrms *Wrms) Broadcast(cmd Event) {
- llog.Info("Broadcasting %v", cmd)
- for i := 0; i < len(wrms.Connections); i++ {
- wrms.Connections[i].Events <- cmd
+func (wrms *Wrms) Broadcast(ev Event) {
+ llog.Info("Broadcasting %v", ev)
+ for _, conn := range wrms.Connections {
+ conn.Send(ev)
}
}
@@ -122,14 +140,14 @@ func (wrms *Wrms) PlayPause() {
wrms.Next()
if wrms.CurrentSong.Uri == "" {
llog.Info("No song left to play")
- wrms.Broadcast(Event{"stop", []Song{}})
+ wrms.Broadcast(newNotification("stop"))
return
}
}
ev = Event{"play", []Song{wrms.CurrentSong}}
wrms.Player.Play(&wrms.CurrentSong)
} else {
- ev = Event{"pause", []Song{}}
+ ev = newNotification("pause")
wrms.Player.Pause()
}