add search db feature
This commit is contained in:
		
							parent
							
								
									cdf12411a2
								
							
						
					
					
						commit
						62598c805e
					
				
					 6 changed files with 94 additions and 31 deletions
				
			
		
							
								
								
									
										2
									
								
								NOTES.md
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								NOTES.md
									
										
									
									
									
								
							|  | @ -79,7 +79,7 @@ | |||
|   - [x] GET `/api/queue/:song_id/move/:position` | ||||
|   - [x] GET `/api/queue/replace/:playlist_name` | ||||
|   - [x] GET `/api/queue/attach/:playlist_name` | ||||
|   - [ ] `/api/list_database/:path` | ||||
|   - [ ] GET `/api/database/:path` | ||||
|   - [x] GET `/api/playlists` | ||||
|   - [x] POST `/api/playlists/:name` | ||||
|   - [x] GET `/api/playlists/:name` | ||||
|  |  | |||
							
								
								
									
										45
									
								
								mpd.go
									
										
									
									
									
								
							
							
						
						
									
										45
									
								
								mpd.go
									
										
									
									
									
								
							|  | @ -371,7 +371,7 @@ func deletePlaylist(c echo.Context) error { | |||
| 		return c.String(http.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.JSON(http.StatusNoContent, "") | ||||
| 	return c.String(http.StatusNoContent, "") | ||||
| } | ||||
| 
 | ||||
| func savePlaylist(c echo.Context) error { | ||||
|  | @ -390,5 +390,46 @@ func savePlaylist(c echo.Context) error { | |||
| 		return c.String(http.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.JSON(http.StatusCreated, "") | ||||
| 	return c.String(http.StatusCreated, "") | ||||
| } | ||||
| 
 | ||||
| func searchDatabase(c echo.Context) error { | ||||
| 	// Connect to MPD server | ||||
| 	conn, err := mpd.Dial("tcp", "localhost:6600") | ||||
| 	if err != nil { | ||||
| 		c.Logger().Error(err) | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
| 
 | ||||
| 	pattern := c.Param("pattern") | ||||
| 
 | ||||
| 	artistResult, err := conn.Search("artist", pattern) | ||||
| 	if err != nil { | ||||
| 		c.Logger().Error(err) | ||||
| 		return c.String(http.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 	albumResult, err := conn.Search("album", pattern) | ||||
| 	if err != nil { | ||||
| 		c.Logger().Error(err) | ||||
| 		return c.String(http.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 	titleResult, err := conn.Search("title", pattern) | ||||
| 	if err != nil { | ||||
| 		c.Logger().Error(err) | ||||
| 		return c.String(http.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	songs := append(append(artistResult, albumResult...), titleResult...) | ||||
| 
 | ||||
| 	// make list unique | ||||
| 	uniqueList := make([]mpd.Attrs, 0, len(songs)) | ||||
| 	keep := make(map[string]bool) | ||||
| 	for _, song := range songs { | ||||
| 		if _, ok := keep[song["file"]]; !ok { | ||||
| 			keep[song["file"]] = true | ||||
| 			uniqueList = append(uniqueList, song) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return c.JSON(http.StatusOK, uniqueList) | ||||
| } | ||||
|  |  | |||
|  | @ -99,6 +99,8 @@ func main() { | |||
| 	g.GET("/playlists/:name", listPlaylist) | ||||
| 	g.DELETE("/playlists/:name", deletePlaylist) | ||||
| 
 | ||||
| 	g.GET("/database/:pattern", searchDatabase) | ||||
| 
 | ||||
| 	g.GET("/download", downloadTrack) | ||||
| 
 | ||||
| 	e.GET("/ws", wsServe) | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| <!DOCTYPE html> | ||||
| <html lang="en" xmlns="http://www.w3.org/1999/html"> | ||||
| <head> | ||||
|   <meta charset="UTF-8" /> | ||||
|   <meta charset="UTF-8"> | ||||
|   <title>Sanic</title> | ||||
|   <link rel="stylesheet" href="style.css" /> | ||||
|   <link rel="stylesheet" href="style.css"> | ||||
|   <link rel="stylesheet" href="treeview.css"> | ||||
|   <link rel="icon" href="favicon.ico" sizes="16x16 32x32 48x48 64x64" type="image/png" /> | ||||
|   <link rel="icon" href="favicon.ico" sizes="16x16 32x32 48x48 64x64" type="image/png"> | ||||
| </head> | ||||
| <body> | ||||
| 
 | ||||
|  | @ -14,7 +14,7 @@ | |||
|   <button class="close">×</button> | ||||
|   <form method="dialog"> | ||||
|     <label for="control-playlist-name">Name</label> | ||||
|     <input type="text" id="control-playlist-name" name="playlist-name" autofocus /> | ||||
|     <input type="text" id="control-playlist-name" name="playlist-name" autofocus> | ||||
|     <button>Save</button> | ||||
|   </form> | ||||
| </dialog> | ||||
|  | @ -187,9 +187,8 @@ | |||
|     </div><!--/#file-browser--> | ||||
|     <div id="search" style="display: none"> | ||||
|       <div> | ||||
|         <label for="control-search"></label> | ||||
|         <input type="text" id="control-search" name="search" /> | ||||
|         <button>Search</button> | ||||
|         <input type="text" id="control-search-pattern" name="pattern"> | ||||
|         <button id="control-search-submit">Search</button> | ||||
|       </div> | ||||
|       <div> | ||||
|         actions | ||||
|  |  | |||
|  | @ -38,6 +38,8 @@ const control_attach_playlist = document.getElementById("control-attach-playlist | |||
| const control_save_playlist = document.getElementById("control-save-playlist"); | ||||
| const control_delete_playlist = document.getElementById("control-delete-playlist"); | ||||
| const result_table = document.querySelector("#result tbody"); | ||||
| const control_search_pattern = document.getElementById("control-search-pattern"); | ||||
| const control_search_submit = document.getElementById("control-search-submit"); | ||||
| 
 | ||||
| // Utility functions
 | ||||
| 
 | ||||
|  | @ -82,24 +84,7 @@ refreshPlaylists = () => { | |||
|         option.addEventListener("click", () => { | ||||
|           fetch(`${API_URL}/playlists/${p["playlist"]}`).then(async r => { | ||||
|             if (r.status === 200) { | ||||
|               const songs = await r.json(); | ||||
|               console.log(songs) | ||||
|               result_table.innerHTML = ""; | ||||
|               songs.forEach(song => { | ||||
|                 const tr = document.createElement("tr"); | ||||
|                 const artist = document.createElement("td"); | ||||
|                 artist.innerText = song["Artist"]; | ||||
|                 const title = document.createElement("td"); | ||||
|                 title.innerText = song["Title"]; | ||||
|                 const time = document.createElement("td"); | ||||
|                 time.innerText = secondsToTrackTime(parseInt(song["Time"])) | ||||
|                 tr.appendChild(artist); | ||||
|                 tr.appendChild(title); | ||||
|                 tr.appendChild(document.createElement("td")); // album
 | ||||
|                 tr.appendChild(document.createElement("td")); // genre
 | ||||
|                 tr.appendChild(time); | ||||
|                 result_table.appendChild(tr); | ||||
|               }); | ||||
|               fillResultTable(await r.json()); | ||||
|             } | ||||
|           }) | ||||
|         }); | ||||
|  | @ -109,6 +94,25 @@ refreshPlaylists = () => { | |||
|   }); | ||||
| } | ||||
| 
 | ||||
| fillResultTable = (songs) => { | ||||
|   result_table.innerHTML = ""; | ||||
|   songs.forEach(song => { | ||||
|     const tr = document.createElement("tr"); | ||||
|     const artist = document.createElement("td"); | ||||
|     artist.innerText = song["Artist"]; | ||||
|     const title = document.createElement("td"); | ||||
|     title.innerText = song["Title"]; | ||||
|     const time = document.createElement("td"); | ||||
|     time.innerText = secondsToTrackTime(parseInt(song["Time"])) | ||||
|     tr.appendChild(artist); | ||||
|     tr.appendChild(title); | ||||
|     tr.appendChild(document.createElement("td")); // album
 | ||||
|     tr.appendChild(document.createElement("td")); // genre
 | ||||
|     tr.appendChild(time); | ||||
|     result_table.appendChild(tr); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // UI controls
 | ||||
| 
 | ||||
| tab_browser.addEventListener("click", () => { | ||||
|  | @ -154,12 +158,20 @@ dialog_save_playlist_close.addEventListener("click", () => { | |||
|   dialog_save_playlist.close() | ||||
| }); | ||||
| 
 | ||||
| // control_progress.addEventListener("change", event => {
 | ||||
| //   control_time.value = `${secondsToTrackTime(event.target.value)}/${secondsToTrackTime(event.target.max)}`;
 | ||||
| // });
 | ||||
| 
 | ||||
| // Add API calls to controls
 | ||||
| 
 | ||||
| control_search_submit.addEventListener("click", event => { | ||||
|   event.preventDefault() | ||||
|   fetch(`${API_URL}/database/${control_search_pattern.value}`).then(async r => { | ||||
|     if (r.status === 200) { | ||||
|       fillResultTable([...new Set(await r.json())]); | ||||
|     } else { | ||||
|       console.error(`API returned ${r.status}: ${r.statusText}`); | ||||
|     } | ||||
|   }) | ||||
| }); | ||||
| 
 | ||||
| control_replace_playlist.addEventListener("click", () => { | ||||
|   fetch(`${API_URL}/queue/replace/${control_playlist_list.value}`).then(async r => { | ||||
|     if (r.status !== 200) { | ||||
|  |  | |||
|  | @ -426,3 +426,12 @@ dialog .close { | |||
|   top: 1pt; | ||||
|   right: 1pt; | ||||
| } | ||||
| 
 | ||||
| #control-search-pattern { | ||||
|   margin: 1em; | ||||
| } | ||||
| 
 | ||||
| #search:first-child { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue