finish work on sse
This commit is contained in:
		
							parent
							
								
									599a7284b9
								
							
						
					
					
						commit
						912a42c450
					
				
					 4 changed files with 193 additions and 244 deletions
				
			
		
							
								
								
									
										8
									
								
								sse.go
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								sse.go
									
										
									
									
									
								
							|  | @ -100,6 +100,7 @@ func serveSSE(c echo.Context) error { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		w.Flush() | 		w.Flush() | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
| 	defer mpdConn.Close() | 	defer mpdConn.Close() | ||||||
| 
 | 
 | ||||||
|  | @ -125,6 +126,7 @@ func serveSSE(c echo.Context) error { | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.Logger().Error(err) | 				c.Logger().Error(err) | ||||||
| 			} | 			} | ||||||
|  | 			//c.Logger().Print("status " + string(jsonStatus)) | ||||||
| 			// Only send new event if different from last time | 			// Only send new event if different from last time | ||||||
| 			if !bytes.Equal(jsonStatus, lastJsonStatus) { | 			if !bytes.Equal(jsonStatus, lastJsonStatus) { | ||||||
| 				statusEvent := Event{ | 				statusEvent := Event{ | ||||||
|  | @ -145,10 +147,11 @@ func serveSSE(c echo.Context) error { | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.Logger().Error(err) | 				c.Logger().Error(err) | ||||||
| 			} | 			} | ||||||
|  | 			//c.Logger().Print("current_song " + string(jsonCurrentSong)) | ||||||
| 			// Only send new event if different from last time | 			// Only send new event if different from last time | ||||||
| 			if !bytes.Equal(jsonCurrentSong, lastJsonCurrentSong) { | 			if !bytes.Equal(jsonCurrentSong, lastJsonCurrentSong) { | ||||||
| 				currentSongEvent := Event{ | 				currentSongEvent := Event{ | ||||||
| 					Event: []byte("status"), | 					Event: []byte("currentsong"), | ||||||
| 					Data:  []byte(string(jsonCurrentSong)), | 					Data:  []byte(string(jsonCurrentSong)), | ||||||
| 				} | 				} | ||||||
| 				if err := currentSongEvent.MarshalTo(w); err != nil { | 				if err := currentSongEvent.MarshalTo(w); err != nil { | ||||||
|  | @ -165,10 +168,11 @@ func serveSSE(c echo.Context) error { | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.Logger().Error(err) | 				c.Logger().Error(err) | ||||||
| 			} | 			} | ||||||
|  | 			//c.Logger().Print("queue " + string(jsonQueue)) | ||||||
| 			// Only send new event if different from last time | 			// Only send new event if different from last time | ||||||
| 			if !bytes.Equal(jsonQueue, lastJsonQueue) { | 			if !bytes.Equal(jsonQueue, lastJsonQueue) { | ||||||
| 				queueEvent := Event{ | 				queueEvent := Event{ | ||||||
| 					Event: []byte("status"), | 					Event: []byte("queue"), | ||||||
| 					Data:  []byte(string(jsonQueue)), | 					Data:  []byte(string(jsonQueue)), | ||||||
| 				} | 				} | ||||||
| 				if err := queueEvent.MarshalTo(w); err != nil { | 				if err := queueEvent.MarshalTo(w); err != nil { | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|     <div id="control-admin"> |     <div id="control-admin"> | ||||||
|       <button id="connection-state">❌ Disconnected</button> <!-- ❌ Cross Mark --> |       <button id="connection-state">❌ Disconnected</button> <!-- ❌ Cross Mark --> | ||||||
|       <button>Config</button> |       <button>Config</button> | ||||||
|       <button id="control-update-db" disabled="disabled"><span class="loader"></span> Update DB</button> |       <button id="control-update-db" data-jobid="" disabled="disabled"><span class="loader"></span> Update DB</button> | ||||||
|     </div><!--/#control-admin--> |     </div><!--/#control-admin--> | ||||||
|     <div> |     <div> | ||||||
|       <div class="spaced"> |       <div class="spaced"> | ||||||
|  | @ -61,7 +61,7 @@ | ||||||
|       <div> |       <div> | ||||||
|         <label for="control-track">Now playing:</label> |         <label for="control-track">Now playing:</label> | ||||||
|         <!--<input type="text" id="control-track" name="track" disabled="disabled" />--> |         <!--<input type="text" id="control-track" name="track" disabled="disabled" />--> | ||||||
|         <div class="marquee" id="control-track"> |         <div class="marquee" id="control-track" data-songid=""> | ||||||
|           <span></span> |           <span></span> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  | @ -226,5 +226,6 @@ | ||||||
|   </footer> |   </footer> | ||||||
| </main> | </main> | ||||||
| <script src="index.js"></script> | <script src="index.js"></script> | ||||||
|  | <script src="sse.js"></script> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|  |  | ||||||
							
								
								
									
										246
									
								
								static/index.js
									
										
									
									
									
								
							
							
						
						
									
										246
									
								
								static/index.js
									
										
									
									
									
								
							|  | @ -9,7 +9,6 @@ const dialog_save_playlist = document.getElementById("save-playlist"); | ||||||
| const control_playlist_name = document.getElementById("control-playlist-name"); | const control_playlist_name = document.getElementById("control-playlist-name"); | ||||||
| const dialog_save_playlist_submit = document.querySelector("#save-playlist form button"); | const dialog_save_playlist_submit = document.querySelector("#save-playlist form button"); | ||||||
| const dialog_save_playlist_close = document.querySelector("#save-playlist .close"); | const dialog_save_playlist_close = document.querySelector("#save-playlist .close"); | ||||||
| 
 |  | ||||||
| const connection_state = document.getElementById("connection-state"); | const connection_state = document.getElementById("connection-state"); | ||||||
| const control_update_db = document.getElementById("control-update-db"); | const control_update_db = document.getElementById("control-update-db"); | ||||||
| const control_previous = document.getElementById("control-previous"); | const control_previous = document.getElementById("control-previous"); | ||||||
|  | @ -137,6 +136,7 @@ tab_search.addEventListener("click", () => { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| tab_playlists.addEventListener("click", () => { | tab_playlists.addEventListener("click", () => { | ||||||
|  |   refreshPlaylists(); | ||||||
|   if (!tab_playlists.classList.contains("active")) { |   if (!tab_playlists.classList.contains("active")) { | ||||||
|     tab_browser.classList.remove("active"); |     tab_browser.classList.remove("active"); | ||||||
|     tab_search.classList.remove("active") |     tab_search.classList.remove("active") | ||||||
|  | @ -215,48 +215,16 @@ control_delete_playlist.addEventListener("click", () => { | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| tab_browser.addEventListener("click", () => { |  | ||||||
|   if (!tab_browser.classList.contains("active")) { |  | ||||||
|     tab_browser.classList.add("active"); |  | ||||||
|     tab_search.classList.remove("active") |  | ||||||
|     tab_playlists.classList.remove("active") |  | ||||||
|     document.getElementById("file-browser").style.display = "block"; |  | ||||||
|     document.getElementById("search").style.display = "none"; |  | ||||||
|     document.getElementById("playlist-browser").style.display = "none"; |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| tab_search.addEventListener("click", () => { |  | ||||||
|   if (!tab_search.classList.contains("active")) { |  | ||||||
|     tab_browser.classList.remove("active"); |  | ||||||
|     tab_search.classList.add("active") |  | ||||||
|     tab_playlists.classList.remove("active") |  | ||||||
|     document.getElementById("file-browser").style.display = "none"; |  | ||||||
|     document.getElementById("search").style.display = "block"; |  | ||||||
|     document.getElementById("playlist-browser").style.display = "none"; |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| tab_playlists.addEventListener("click", () => { |  | ||||||
|   refreshPlaylists(); |  | ||||||
|   if (!tab_playlists.classList.contains("active")) { |  | ||||||
|     tab_browser.classList.remove("active"); |  | ||||||
|     tab_search.classList.remove("active") |  | ||||||
|     tab_playlists.classList.add("active") |  | ||||||
|     document.getElementById("file-browser").style.display = "none"; |  | ||||||
|     document.getElementById("search").style.display = "none"; |  | ||||||
|     document.getElementById("playlist-browser").style.display = "block"; |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // Add API calls to controls
 | // Add API calls to controls
 | ||||||
| 
 | 
 | ||||||
| control_update_db.addEventListener("click", () => { | control_update_db.addEventListener("click", (event) => { | ||||||
|   console.log("Issuing database update") |   console.log("Issuing database update"); | ||||||
|   fetch(`${API_URL}/update_db`).then(async r => { |   fetch(`${API_URL}/update_db`).then(async r => { | ||||||
|     if (r.status === 200) { |     if (r.status === 200) { | ||||||
|  |       // const idText = await r.text();
 | ||||||
|       console.log(await r.text()); |       console.log(await r.text()); | ||||||
|       event.target.disabled = true; |       // event.target.dataset.jobid = idText.split(" ").pop();
 | ||||||
|  |       // event.target.disabled = true;
 | ||||||
|     } else { |     } else { | ||||||
|       console.error(`API returned ${r.status}: ${r.statusText}`); |       console.error(`API returned ${r.status}: ${r.statusText}`); | ||||||
|     } |     } | ||||||
|  | @ -391,205 +359,3 @@ control_volume.addEventListener("change", event => { | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
| // Server-Sent-Events
 |  | ||||||
| 
 |  | ||||||
| if (typeof (EventSource) !== "undefined") { |  | ||||||
|   const sse = new EventSource("/sse"); |  | ||||||
|   sse.addEventListener("status", (event) => { |  | ||||||
|     const status = JSON.parse(event.data); |  | ||||||
|     console.log("test: " + event.data); |  | ||||||
| 
 |  | ||||||
|     connection_state.innerHTML = "❌ Disconnected";  // ✅ Check Mark Button
 |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   sse.onmessage = function (event) { |  | ||||||
|     console.log("sse message: " + event.data); |  | ||||||
|   }; |  | ||||||
| } else { |  | ||||||
|   console.log("Sorry, your browser does not support server-sent events..."); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Websocket logic
 |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| 
 |  | ||||||
| // Create WebSocket connection.
 |  | ||||||
| const socket = new WebSocket(`${document.location.protocol === "https:" ? "wss" : "ws"}://${document.location.host}/ws`); |  | ||||||
| 
 |  | ||||||
| // Connection opened
 |  | ||||||
| socket.addEventListener("open", () => { |  | ||||||
|   socket.send("Hello Server!"); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // Listen for messages and update UI state
 |  | ||||||
| socket.addEventListener("message", event => { |  | ||||||
|   // Print out mpd response
 |  | ||||||
|   console.log(`DEBUG: ${event.data}`);  // DEBUG
 |  | ||||||
| 
 |  | ||||||
|   const msg = JSON.parse(event.data); |  | ||||||
| 
 |  | ||||||
|   if ("status" in msg) { |  | ||||||
|     if (msg.mpd_status == null) { |  | ||||||
| 
 |  | ||||||
|     } else { |  | ||||||
|       // print error if present
 |  | ||||||
|       if ("error" in msg.mpd_status) { |  | ||||||
|         console.error(msg.mpd_status.error); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update "Update DB" button
 |  | ||||||
|       if ("updating_db" in msg.mpd_status) { |  | ||||||
|         control_update_db.disabled = true; |  | ||||||
|       } else { |  | ||||||
|         if (control_update_db.disabled) { |  | ||||||
|           console.log("Database update done.") |  | ||||||
|         } |  | ||||||
|         control_update_db.disabled = false; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update play/pause button
 |  | ||||||
|       if ("state" in msg.mpd_status && msg.mpd_status.state !== "play") {  // TODO: only update DOM if necessary
 |  | ||||||
|         control_play_pause.innerHTML = "⏵︎";  // Play
 |  | ||||||
|       } else { |  | ||||||
|         control_play_pause.innerHTML = "⏸︎";  // Pause
 |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update playback time
 |  | ||||||
|       if ("time" in msg.mpd_status) { |  | ||||||
|         const [elapsed, duration] = msg.mpd_status.time.split(":", 2) |  | ||||||
|         control_progress.value = elapsed; |  | ||||||
|         control_progress.max = duration; |  | ||||||
|         // triggers the update of control_time element
 |  | ||||||
|         const e = new Event("input"); |  | ||||||
|         control_progress.dispatchEvent(e); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update repeat state
 |  | ||||||
|       if ("repeat" in msg.mpd_status) { |  | ||||||
|         if (msg.mpd_status.repeat === "1") { |  | ||||||
|           control_repeat.innerHTML = "🔴 repeat"; // 🔴 Red Circle
 |  | ||||||
|           control_repeat.dataset.state = "on"; |  | ||||||
|         } else { |  | ||||||
|           control_repeat.innerHTML = "🔘 repeat"; // 🔘 Radio Button
 |  | ||||||
|           control_repeat.dataset.state = "off"; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update shuffle state
 |  | ||||||
|       if ("random" in msg.mpd_status) { |  | ||||||
|         if (msg.mpd_status.random === "1") { |  | ||||||
|           control_shuffle.innerHTML = "🔴 shuffle"; // 🔴 Red Circle
 |  | ||||||
|           control_shuffle.dataset.state = "on"; |  | ||||||
|         } else { |  | ||||||
|           control_shuffle.innerHTML = "🔘 shuffle"; // 🔘 Radio Button
 |  | ||||||
|           control_shuffle.dataset.state = "off"; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update crossfade state
 |  | ||||||
|       if ("xfade" in msg.mpd_status) { |  | ||||||
|         control_xfade.value = msg.mpd_status.xfade; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // update volume
 |  | ||||||
|       if ("volume" in msg.mpd_status) { |  | ||||||
|         control_volume.value = msg.mpd_status.volume; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // update song info
 |  | ||||||
|   if ("current_song" in msg && msg.mpd_current_song != null) { |  | ||||||
|     let track; |  | ||||||
|     if ("Artist" in msg.mpd_current_song && "Title" in msg.mpd_current_song) { |  | ||||||
|       track = `${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}` |  | ||||||
|     } else { |  | ||||||
|       track = msg.mpd_current_song.file; |  | ||||||
|     } |  | ||||||
|     if (control_track.innerHTML !== `<span>${track}</span>`) { |  | ||||||
|       control_track.innerHTML = `<span>${track}</span>`; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // update queue
 |  | ||||||
|   if ("queue" in msg && msg.mpd_queue != null) { |  | ||||||
|     const tbody = document.createElement("tbody"); |  | ||||||
|     msg.mpd_queue.forEach(song => { |  | ||||||
|       const tr = document.createElement("tr"); |  | ||||||
|       tr.dataset.song_id = song.Id; |  | ||||||
|       if ("songid" in msg.mpd_status && msg.mpd_status.songid === song.Id) { |  | ||||||
|         tr.classList.add("playing"); |  | ||||||
|       } |  | ||||||
|       const pos = document.createElement("td"); |  | ||||||
|       pos.innerText = song.Pos; |  | ||||||
|       const artist = document.createElement("td"); |  | ||||||
|       if ("Artist" in song) { |  | ||||||
|         artist.innerText = song.Artist; |  | ||||||
|       } |  | ||||||
|       const track = document.createElement("td"); |  | ||||||
|       if ("Title" in song) { |  | ||||||
|         track.innerText = song.Title; |  | ||||||
|       } else { |  | ||||||
|         track.innerText = song.file; |  | ||||||
|       } |  | ||||||
|       const album = document.createElement("td"); |  | ||||||
|       // TODO: Do songs have album info attached to them?
 |  | ||||||
|       album.innerText = ""; |  | ||||||
|       const length = document.createElement("td"); |  | ||||||
|       length.innerText = secondsToTrackTime(song.duration); |  | ||||||
|       const actions = document.createElement("td"); |  | ||||||
|       const moveUp = document.createElement("button"); |  | ||||||
|       moveUp.classList.add("borderless"); |  | ||||||
|       if (parseInt(song.Pos) !== 0) { |  | ||||||
|         moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
 |  | ||||||
|         moveUp.addEventListener("click", event => { moveTrackInQueue(event, -1) }); |  | ||||||
|       } else { |  | ||||||
|         moveUp.innerHTML = " "; |  | ||||||
|       } |  | ||||||
|       const moveDown = document.createElement("button"); |  | ||||||
|       moveDown.classList.add("borderless"); |  | ||||||
|       if (parseInt(song.Pos) !== msg.mpd_queue.length - 1) { |  | ||||||
|         moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
 |  | ||||||
|         moveDown.addEventListener("click", event => {moveTrackInQueue(event, 1)}); |  | ||||||
|       } else { |  | ||||||
|         moveDown.innerHTML = " "; |  | ||||||
|       } |  | ||||||
|       const remove = document.createElement("button"); |  | ||||||
|       remove.classList.add("borderless"); |  | ||||||
|       remove.innerHTML = "❌"; // ❌ Cross mark
 |  | ||||||
|       remove.addEventListener("click", removeTrackFromQueue); |  | ||||||
|       actions.appendChild(moveUp); |  | ||||||
|       actions.appendChild(moveDown); |  | ||||||
|       actions.appendChild(remove); |  | ||||||
|       tr.appendChild(pos); |  | ||||||
|       tr.appendChild(artist); |  | ||||||
|       tr.appendChild(track); |  | ||||||
|       tr.appendChild(album); |  | ||||||
|       tr.appendChild(length); |  | ||||||
|       tr.appendChild(actions); |  | ||||||
|       tbody.appendChild(tr); |  | ||||||
|     }); |  | ||||||
|     const currentQueue = document.querySelector("#queue tbody") |  | ||||||
|     if (currentQueue.innerHTML !== tbody.innerHTML) { |  | ||||||
|       console.log("Updating queue") |  | ||||||
|       currentQueue.outerHTML = tbody.outerHTML; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if ("error" in msg) { |  | ||||||
|     console.error(`MPD Error: ${msg.mpd_error}`) |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // Request MPD status every second
 |  | ||||||
| window.setInterval(() => { |  | ||||||
|   if (socket.readyState === socket.OPEN) { |  | ||||||
|     socket.send("#status"); |  | ||||||
|     connection_state.innerHTML = "✅ Connected";  // ✅ Check Mark Button
 |  | ||||||
|   } else { |  | ||||||
|     connection_state.innerHTML = "❌ Disconnected";  // ❌ Cross Mark
 |  | ||||||
|   } |  | ||||||
| }, 1000); |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
|  |  | ||||||
							
								
								
									
										178
									
								
								static/sse.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								static/sse.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,178 @@ | ||||||
|  | // Server-Sent-Events
 | ||||||
|  | 
 | ||||||
|  | if (typeof (EventSource) !== "undefined") { | ||||||
|  |   const sse = new EventSource("/sse"); | ||||||
|  |   sse.addEventListener("status", handleStatus); | ||||||
|  |   sse.addEventListener("currentsong", handleCurrentSong); | ||||||
|  |   sse.addEventListener("queue", handleQueue); | ||||||
|  |   sse.onmessage = (event) => { | ||||||
|  |     console.log("sse message: " + event.data); | ||||||
|  |   }; | ||||||
|  |   sse.onerror = (err) => { | ||||||
|  |     console.error("EventSource failed:", err); | ||||||
|  |     connection_state.innerHTML = "❌ Disconnected";  // ❌ Cross Mark
 | ||||||
|  |   }; | ||||||
|  |   sse.onopen = () => { | ||||||
|  |     console.log("EventSource connected"); | ||||||
|  |     connection_state.innerHTML = "✅ Connected";  // ✅ Check Mark Button
 | ||||||
|  |   }; | ||||||
|  | } else { | ||||||
|  |   console.error("Sorry, your browser does not support server-sent events..."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleStatus(event) { | ||||||
|  |   const status = JSON.parse(event.data); | ||||||
|  | 
 | ||||||
|  |   // print error if present
 | ||||||
|  |   if ("error" in status) { | ||||||
|  |     console.error(status.error); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update "Update DB" button
 | ||||||
|  |   if ("updating_db" in status) { | ||||||
|  |     control_update_db.disabled = true; | ||||||
|  |   } else { | ||||||
|  |     if (control_update_db.disabled) { | ||||||
|  |       console.log("Database update done.") | ||||||
|  |     } | ||||||
|  |     control_update_db.disabled = false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update play/pause button
 | ||||||
|  |   // TODO: only update DOM if necessary
 | ||||||
|  |   if ("state" in status && status.state !== "play") { | ||||||
|  |     control_play_pause.innerHTML = "⏵︎";  // Play
 | ||||||
|  |   } else { | ||||||
|  |     control_play_pause.innerHTML = "⏸︎";  // Pause
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if ("songid" in status) { | ||||||
|  |     control_track.dataset.songid = status.songid; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update playback time
 | ||||||
|  |   if ("time" in status) { | ||||||
|  |     const [elapsed, duration] = status.time.split(":", 2) | ||||||
|  |     control_progress.value = elapsed; | ||||||
|  |     control_progress.max = duration; | ||||||
|  |     // triggers the update of control_time element
 | ||||||
|  |     const e = new Event("input"); | ||||||
|  |     control_progress.dispatchEvent(e); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update repeat state
 | ||||||
|  |   if ("repeat" in status) { | ||||||
|  |     if (status.repeat === "1") { | ||||||
|  |       control_repeat.innerHTML = "🔴 repeat"; // 🔴 Red Circle
 | ||||||
|  |       control_repeat.dataset.state = "on"; | ||||||
|  |     } else { | ||||||
|  |       control_repeat.innerHTML = "🔘 repeat"; // 🔘 Radio Button
 | ||||||
|  |       control_repeat.dataset.state = "off"; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update shuffle state
 | ||||||
|  |   if ("random" in status) { | ||||||
|  |     if (status.random === "1") { | ||||||
|  |       control_shuffle.innerHTML = "🔴 shuffle"; // 🔴 Red Circle
 | ||||||
|  |       control_shuffle.dataset.state = "on"; | ||||||
|  |     } else { | ||||||
|  |       control_shuffle.innerHTML = "🔘 shuffle"; // 🔘 Radio Button
 | ||||||
|  |       control_shuffle.dataset.state = "off"; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update crossfade state
 | ||||||
|  |   if ("xfade" in status) { | ||||||
|  |     control_xfade.value = status.xfade; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update volume
 | ||||||
|  |   if ("volume" in status) { | ||||||
|  |     control_volume.value = status.volume; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleCurrentSong(event) { | ||||||
|  |   const current_song = JSON.parse(event.data); | ||||||
|  | 
 | ||||||
|  |   let track; | ||||||
|  |   if ("Artist" in current_song && "Title" in current_song) { | ||||||
|  |     track = `${current_song.Artist} - ${current_song.Title}` | ||||||
|  |   } else { | ||||||
|  |     track = current_song.file; | ||||||
|  |   } | ||||||
|  |   // Only replace if necessary to not interrupt the animation
 | ||||||
|  |   if (control_track.innerHTML !== `<span>${track}</span>`) { | ||||||
|  |     control_track.innerHTML = `<span>${track}</span>`; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleQueue(event) { | ||||||
|  |   const queue = JSON.parse(event.data); | ||||||
|  | 
 | ||||||
|  |   console.log(queue); | ||||||
|  | 
 | ||||||
|  |   const tbody = document.createElement("tbody"); | ||||||
|  |   queue.forEach(song => { | ||||||
|  |     const tr = document.createElement("tr"); | ||||||
|  |     tr.dataset.song_id = song.Id; | ||||||
|  |     if (control_track.dataset.songid === song.Id) { | ||||||
|  |       tr.classList.add("playing"); | ||||||
|  |     } | ||||||
|  |     const pos = document.createElement("td"); | ||||||
|  |     pos.innerText = song.Pos; | ||||||
|  |     const artist = document.createElement("td"); | ||||||
|  |     if ("Artist" in song) { | ||||||
|  |       artist.innerText = song.Artist; | ||||||
|  |     } | ||||||
|  |     const track = document.createElement("td"); | ||||||
|  |     if ("Title" in song) { | ||||||
|  |       track.innerText = song.Title; | ||||||
|  |     } else { | ||||||
|  |       track.innerText = song.file; | ||||||
|  |     } | ||||||
|  |     const album = document.createElement("td"); | ||||||
|  |     // TODO: Do songs have album info attached to them?
 | ||||||
|  |     album.innerText = ""; | ||||||
|  |     const length = document.createElement("td"); | ||||||
|  |     length.innerText = secondsToTrackTime(song.duration); | ||||||
|  |     const actions = document.createElement("td"); | ||||||
|  |     const moveUp = document.createElement("button"); | ||||||
|  |     moveUp.classList.add("borderless"); | ||||||
|  |     if (parseInt(song.Pos) !== 0) { | ||||||
|  |       moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
 | ||||||
|  |       moveUp.addEventListener("click", event => { moveTrackInQueue(event, -1) }); | ||||||
|  |     } else { | ||||||
|  |       moveUp.innerHTML = " "; | ||||||
|  |     } | ||||||
|  |     const moveDown = document.createElement("button"); | ||||||
|  |     moveDown.classList.add("borderless"); | ||||||
|  |     if (parseInt(song.Pos) !== queue.length - 1) { | ||||||
|  |       moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
 | ||||||
|  |       moveDown.addEventListener("click", event => {moveTrackInQueue(event, 1)}); | ||||||
|  |     } else { | ||||||
|  |       moveDown.innerHTML = " "; | ||||||
|  |     } | ||||||
|  |     const remove = document.createElement("button"); | ||||||
|  |     remove.classList.add("borderless"); | ||||||
|  |     remove.innerHTML = "❌"; // ❌ Cross mark
 | ||||||
|  |     remove.addEventListener("click", removeTrackFromQueue); | ||||||
|  |     actions.appendChild(moveUp); | ||||||
|  |     actions.appendChild(moveDown); | ||||||
|  |     actions.appendChild(remove); | ||||||
|  |     tr.appendChild(pos); | ||||||
|  |     tr.appendChild(artist); | ||||||
|  |     tr.appendChild(track); | ||||||
|  |     tr.appendChild(album); | ||||||
|  |     tr.appendChild(length); | ||||||
|  |     tr.appendChild(actions); | ||||||
|  |     tbody.appendChild(tr); | ||||||
|  |   }); | ||||||
|  |   const currentQueue = document.querySelector("#queue tbody") | ||||||
|  |   // only update queue if necessary to not interrupt user interaction
 | ||||||
|  |   if (currentQueue.innerHTML !== tbody.innerHTML) { | ||||||
|  |     console.log("Updating queue") | ||||||
|  |     currentQueue.outerHTML = tbody.outerHTML; | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue