mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-30 13:23:00 +00:00 
			
		
		
		
	| @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.6) | ||||
| project (mympd C) | ||||
| set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") | ||||
| set(CPACK_PACKAGE_VERSION_MAJOR "3") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "4") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "5") | ||||
| set(CPACK_PACKAGE_VERSION_PATCH "0") | ||||
|  | ||||
| if(CMAKE_BUILD_TYPE MATCHES RELEASE) | ||||
| @@ -34,6 +34,7 @@ set(SOURCES | ||||
|     src/mpd_client.c | ||||
|     dist/src/mongoose/mongoose.c | ||||
|     dist/src/frozen/frozen.c | ||||
|     dist/src/inih/ini.c | ||||
| ) | ||||
|  | ||||
| add_executable(mympd ${SOURCES}) | ||||
| @@ -52,4 +53,4 @@ install(FILES dist/htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME | ||||
| install(FILES dist/htdocs/css/mpd.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/) | ||||
| install(DIRECTORY htdocs/assets DESTINATION share/${PROJECT_NAME}/htdocs) | ||||
| install(DIRECTORY DESTINATION /var/lib/${PROJECT_NAME}/) | ||||
| install(FILES contrib/options DESTINATION /etc/${PROJECT_NAME}/) | ||||
| install(FILES contrib/mympd.conf DESTINATION /etc/${PROJECT_NAME}/) | ||||
|   | ||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							| @@ -45,6 +45,7 @@ Backend | ||||
| ------- | ||||
|  - Mongoose: https://github.com/cesanta/mongoose | ||||
|  - Frozen: https://github.com/cesanta/frozen | ||||
|  - inih: https://github.com/benhoyt/inih | ||||
|  | ||||
| Dependencies | ||||
| ------------ | ||||
| @@ -68,29 +69,14 @@ Unix Build Instructions | ||||
| Run Flags | ||||
| --------- | ||||
| ``` | ||||
| Usage: ./mympd [OPTION]... | ||||
|  | ||||
|  -h, --mpdhost <host>          connect to mpd at host [localhost] | ||||
|  -p, --mpdport <port>          connect to mpd at port [6600] | ||||
|  -m, --mpdpass <password>      specifies the password to use when connecting to mpd | ||||
|  -w, --webport <port>          listen port for webserver [80] | ||||
|  -S, --ssl		       enable ssl | ||||
|  -W, --sslport <port>	       listen port for ssl webserver [443] | ||||
|  -C, --sslcert <filename>      filename for ssl certificate [/etc/mympd/ssl/server.pem] | ||||
|  -K, --sslkey <filename>       filename for ssl key [/etc/mympd/ssl/server.key] | ||||
|  -s, --streamport <port>       connect to mpd http stream at port [8000] | ||||
|  -u, --user <username>         drop priviliges to user after socket bind | ||||
|  -i, --coverimage <filename>   filename for coverimage [folder.jpg] | ||||
|  -t, --statefile <filename>    filename for mympd state [/var/lib/mympd/mympd.state] | ||||
|  -v, --version                 get version | ||||
|  --help                        this help | ||||
| Usage: ./mympd /etc/mypd/mympd.conf | ||||
| ``` | ||||
|  | ||||
| SSL | ||||
| --- | ||||
|  | ||||
| 1. Create ca and certificate ```/path/to/src/contrib/crcert.sh``` (mkrelease.sh do this for you). | ||||
| 2. Start myMPD with ```-S``` (use default certificate under ```/etc/mympd/ssl/```). | ||||
| 1. Create ca and certificate ```/path/to/src/contrib/crcert.sh``` (mkrelease.sh does this for you). | ||||
| 2. Set ```ssl=true``` in /etc/mympd/mympd.conf (use default certificate under ```/etc/mympd/ssl/```). | ||||
| 3. Import ```/etc/mympd/ssl/ca/ca.pem``` in your browser to trust the certificate. | ||||
| 4. myMPD redirects http requests to https, ensure that myMPD hostname is resolvable. | ||||
|  | ||||
|   | ||||
| @@ -1,61 +1,18 @@ | ||||
| .\" Manpage for myMPD. | ||||
| .\" Contact mail@jcgames.de to correct errors or typos. | ||||
| .TH man 1 "24 May 2018" "1.0.0" "myMPD man page" | ||||
| .TH man 1 "06 Aug 2018" "3.5.0" "myMPD man page" | ||||
| .SH NAME | ||||
| myMPD \- Standalone MPD Web GUI written in C, utilizing Websockets and Bootstrap/JS | ||||
| .SH SYNOPSIS | ||||
| mympd [OPTION]... | ||||
| mympd /path/to/mympd.conf | ||||
| .SH DESCRIPTION | ||||
| myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter.  | ||||
| It's tuned for minimal resource usage and requires only very litte dependencies.  | ||||
| myMPD is a fork of ympd. | ||||
|  | ||||
| .SH OPTIONS | ||||
| .TP | ||||
| \fB\-h\fR, \fB\-\-mpdhost HOST\fR | ||||
| connect to mpd at host, defaults to localhost | ||||
| .TP | ||||
| \fB\-p\fR, \fB\-\-mpdport PORT\fR | ||||
| connect to mpd at port, defaults to 6600 | ||||
| .TP | ||||
| \fB\-m\fR, \fB\-\-mpdpass PASSWORD\fR | ||||
| specifies the password to use when connecting to mpd | ||||
| .TP | ||||
| \fB\-w\fR, \fB\-\-webport PORT\fR | ||||
| listen interface/port for webserver [80] | ||||
| .TP | ||||
| \fB\-S\fR, \fB\-\-ssl\fR | ||||
| enable ssl | ||||
| .TP | ||||
| \fB\-W\fR, \fB\-\-sslport PORT\fR | ||||
| listen interface/port for ssl webserver [443] | ||||
| .TP | ||||
| \fB\-C\fR, \fB\-\-sslcert FILENAME\fR | ||||
| filename for ssl certificate [/etc/mympd/ssl/server.pem] | ||||
| .TP | ||||
| \fB\-K\fR, \fB\-\-sslkey FILENAME\fR | ||||
| filename for ssl key [/etc/mympd/ssl/server.key] | ||||
| .TP | ||||
| \fB-s\fR, \fB\-\-streamport PORT | ||||
| connect to mpd http stream at port [8000] | ||||
| .TP | ||||
| \fB\-u\fR, \fB\-\-user USERNAME\fR | ||||
| drop privileges to the provided username after socket binding | ||||
| .TP | ||||
| \fB-i\fR, \fB\-\-coverimage FILENAME\fR | ||||
| filename for coverimage [folder.jpg] | ||||
| .TP | ||||
| \fB-t\fR, \fB\-\-statefile FILENAME\fR | ||||
| filename for mympd state [/var/lib/mympd/mympd.state] | ||||
| .TP | ||||
| \fB\-v\fR, \fB\-\-version\fR | ||||
| print version and exit | ||||
| .TP | ||||
| \fB\-\-help\fR | ||||
| print all valid options and exits | ||||
| .SH BUGS | ||||
| No known bugs. | ||||
| .SH AUTHOR | ||||
| Juergen Mang (mail@jcgames.de) | ||||
|  | ||||
| https://github.com/jcorporation/ympd | ||||
| https://github.com/jcorporation/mympd | ||||
							
								
								
									
										27
									
								
								contrib/mympd.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								contrib/mympd.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #myMPD config file | ||||
|  | ||||
| #Connection to mpd | ||||
| mpdhost = 127.0.0.1 | ||||
| mpdport = 6600 | ||||
| #mpdpass =  | ||||
|  | ||||
| #Webserver options | ||||
| webport = 80 | ||||
|  | ||||
| #Enable ssl | ||||
| ssl = true | ||||
| sslport = 443 | ||||
| sslcert = /etc/mympd/ssl/server.pem | ||||
| sslkey = /etc/mympd/ssl/server.key | ||||
|  | ||||
| #myMPD user | ||||
| user = nobody | ||||
|  | ||||
| #Port for mpd http stream | ||||
| streamport = 8000 | ||||
|  | ||||
| #Name for coverimages | ||||
| coverimage = folder.jpg | ||||
|  | ||||
| #myMPD statefile | ||||
| statefile = /var/lib/mympd/mympd.state | ||||
| @@ -3,8 +3,7 @@ Description=myMPD server daemon | ||||
| Requires=network.target local-fs.target mpd.service | ||||
|  | ||||
| [Service] | ||||
| EnvironmentFile=/etc/mympd/options | ||||
| ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --mpdhost $MPD_HOST --mpdport $MPD_PORT $MPD_PASSWORD --coverimage $COVERIMAGE --statefile $STATEFILE $SSL | ||||
| ExecStart=/usr/bin/mympd /etc/mympd/mympd.conf | ||||
| Type=simple | ||||
|  | ||||
| [Install] | ||||
|   | ||||
| @@ -1,9 +0,0 @@ | ||||
| #myMPD startup options | ||||
| MPD_HOST=localhost | ||||
| MPD_PORT=6600 | ||||
| #MPD_PASSWORD=--mpdpass PASSWORD | ||||
| WEB_PORT=80 | ||||
| SSL=-S -W 443 -C /etc/mympd/ssl/server.pem -K /etc/mympd/ssl/server.key | ||||
| MYMPD_USER=nobody | ||||
| COVERIMAGE=folder.jpg | ||||
| STATEFILE=/var/lib/mympd/mympd.state | ||||
							
								
								
									
										2
									
								
								dist/htdocs/index.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/htdocs/index.html
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										37
									
								
								dist/htdocs/js/mpd.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								dist/htdocs/js/mpd.min.js
									
									
									
									
										vendored
									
									
								
							| @@ -8,8 +8,8 @@ $jscomp.polyfill("String.prototype.repeat",function(a){return a?a:function(a){va | ||||
| var socket,last_song="",last_state,current_song={},playstate="",settings={},alertTimeout,deferredPrompt,dragEl,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",scrollPos:0},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/",scrollPos:0},Playlists:{active:"All",views:{All:{state:"0/-/",scrollPos:0},Detail:{state:"0/-/",scrollPos:0}}},Database:{active:"Artist",views:{Artist:{state:"0/-/",scrollPos:0},Album:{state:"0/-/",scrollPos:0}}}}},Search:{state:"0/Any Tag/",scrollPos:0}}, | ||||
| current:{app:"Playback",tab:void 0,view:void 0,page:0,filter:"",search:"",scrollPos:0},last:{app:void 0,tab:void 0,view:void 0,filter:"",search:"",scrollPos:0}},domCache={};domCache.navbarBottomBtns=document.getElementById("navbar-bottom").getElementsByTagName("div");domCache.navbarBottomBtnsLen=domCache.navbarBottomBtns.length;domCache.panelHeadingBrowse=document.getElementById("panel-heading-browse").getElementsByTagName("a");domCache.panelHeadingBrowseLen=domCache.panelHeadingBrowse.length; | ||||
| domCache.counter=document.getElementById("counter");domCache.volumePrct=document.getElementById("volumePrct");domCache.volumeControl=document.getElementById("volumeControl");domCache.volumeIcon=document.getElementById("volumeIcon");domCache.btnPlay=document.getElementById("btnPlay");domCache.btnPrev=document.getElementById("btnPrev");domCache.btnNext=document.getElementById("btnNext");domCache.progressBar=document.getElementById("progressBar");domCache.volumeBar=document.getElementById("volumeBar"); | ||||
| domCache.outputs=document.getElementById("outputs");domCache.btnAdd=document.getElementById("nav-add2homescreen"); | ||||
| var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalSavequeue=new Modal(document.getElementById("modalSaveQueue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),modalAddToPlaylist=new Modal(document.getElementById("modalAddToPlaylist")),modalRenamePlaylist=new Modal(document.getElementById("modalRenamePlaylist")),mainMenu=new Dropdown(document.getElementById("mainMenu")); | ||||
| domCache.outputs=document.getElementById("outputs");domCache.btnAdd=document.getElementById("nav-add2homescreen");domCache.currentTrack=document.getElementById("currentTrack"); | ||||
| var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalSavequeue=new Modal(document.getElementById("modalSaveQueue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),modalAddToPlaylist=new Modal(document.getElementById("modalAddToPlaylist")),modalRenamePlaylist=new Modal(document.getElementById("modalRenamePlaylist")); | ||||
| function appPrepare(a){if(app.current.app!=app.last.app||app.current.tab!=app.last.tab||app.current.view!=app.last.view){for(var b=0;b<domCache.navbarBottomBtnsLen;b++)domCache.navbarBottomBtns[b].classList.remove("active");document.getElementById("cardPlayback").classList.add("hide");document.getElementById("cardQueue").classList.add("hide");document.getElementById("cardBrowse").classList.add("hide");document.getElementById("cardSearch").classList.add("hide");for(b=0;b<domCache.panelHeadingBrowseLen;b++)domCache.panelHeadingBrowse[b].classList.remove("active"); | ||||
| document.getElementById("cardBrowsePlaylists").classList.add("hide");document.getElementById("cardBrowseDatabase").classList.add("hide");document.getElementById("cardBrowseFilesystem").classList.add("hide");document.getElementById("card"+app.current.app).classList.remove("hide");document.getElementById("nav"+app.current.app).classList.add("active");void 0!=app.current.tab&&(document.getElementById("card"+app.current.app+app.current.tab).classList.remove("hide"),document.getElementById("card"+app.current.app+ | ||||
| "Nav"+app.current.tab).classList.add("active"));scrollTo(a)}(a=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+(void 0==app.current.view?"":app.current.view)+"List"))&&a.classList.add("opacity05")} | ||||
| @@ -25,10 +25,10 @@ var c=app.current.search.split("/"),e=c.length,d="";for(a=0;a<e;a++){if(e-1==a){ | ||||
| app.current.app)for(document.getElementById("searchstr").focus(),app.last.app!=app.current.app&&""!=app.current.search&&(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML='<tr><td><span class="material-icons">search</span></td><td colspan="5">Searching...</td></tr>'),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):(document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML= | ||||
| "",document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),document.getElementById("searchAddAllSongsBtn").setAttribute("disabled","disabled"),document.getElementById("panel-heading-search").innerText="",document.getElementById("SearchList").classList.remove("opacity05"),setPagination(0)),b=document.getElementById("searchtags").getElementsByTagName("button"),c=b.length,a=0;a<c;a++)b[a].classList.remove("active"),b[a].innerText==app.current.filter&&(b[a].classList.add("active"), | ||||
| document.getElementById("searchtagsdesc").innerText=b[a].innerText);else appGoto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else appGoto("Playback")} | ||||
| function appInit(){getSettings();sendAPI({cmd:"MPD_API_GET_OUTPUTNAMES"},parseOutputnames);webSocketConnect();domCache.volumeBar.value=0;domCache.volumeBar.addEventListener("change",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:domCache.volumeBar.value}})},!1);domCache.progressBar.value=0;domCache.progressBar.addEventListener("change",function(a){current_song&&0<=current_song.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:current_song.currentSongId,seek:Math.ceil(domCache.progressBar.value/ | ||||
| 100*current_song.totalTime)}})},!1);document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});document.getElementById("modalSaveQueue").addEventListener("shown.bs.modal",function(){var a=document.getElementById("saveQueueName");a.focus();a.value="";a.classList.remove("is-invalid");document.getElementById("saveQueueFrm").classList.remove("was-validated")});document.getElementById("modalSettings").addEventListener("shown.bs.modal", | ||||
| function appInit(){getSettings();webSocketConnect();domCache.volumeBar.value=0;domCache.volumeBar.addEventListener("change",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:domCache.volumeBar.value}})},!1);domCache.progressBar.value=0;domCache.progressBar.addEventListener("change",function(a){current_song&&0<=current_song.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:current_song.currentSongId,seek:Math.ceil(domCache.progressBar.value/100*current_song.totalTime)}})},!1);document.getElementById("volumeIcon").parentNode.addEventListener("show.bs.dropdown", | ||||
| function(){sendAPI({cmd:"MPD_API_GET_OUTPUTS"},parseOutputnames)});document.getElementById("modalAbout").addEventListener("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});document.getElementById("modalSaveQueue").addEventListener("shown.bs.modal",function(){var a=document.getElementById("saveQueueName");a.focus();a.value="";a.classList.remove("is-invalid");document.getElementById("saveQueueFrm").classList.remove("was-validated")});document.getElementById("modalSettings").addEventListener("shown.bs.modal", | ||||
| function(){getSettings();document.getElementById("settingsFrm").classList.remove("was-validated");document.getElementById("inputCrossfade").classList.remove("is-invalid");document.getElementById("inputMixrampdb").classList.remove("is-invalid");document.getElementById("inputMixrampdelay").classList.remove("is-invalid")});document.getElementById("addToPlaylistPlaylist").addEventListener("change",function(a){"New Playlist"==this.options[this.selectedIndex].text?(document.getElementById("addToPlaylistNewPlaylistDiv").classList.remove("hide"), | ||||
| document.getElementById("addToPlaylistNewPlaylist").focus()):document.getElementById("addToPlaylistNewPlaylistDiv").classList.add("hide")},!1);addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("button[data-href], a[data-href]"),b=a.length,c=0;c<b;c++)a[c].addEventListener("click",function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(this.getAttribute("data-href").replace(/'/g, | ||||
| document.getElementById("addToPlaylistNewPlaylist").focus()):document.getElementById("addToPlaylistNewPlaylistDiv").classList.add("hide")},!1);addFilterLetter("BrowseFilesystemFilterLetters");addFilterLetter("BrowseDatabaseFilterLetters");addFilterLetter("BrowsePlaylistsFilterLetters");for(var a=document.querySelectorAll("[data-href]"),b=a.length,c=0;c<b;c++)a[c].classList.add("clickable"),a[c].addEventListener("click",function(a){a.preventDefault();a.stopPropagation();a=JSON.parse(this.getAttribute("data-href").replace(/'/g, | ||||
| '"'));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}},!1);a=document.querySelectorAll(".pages");b=a.length;for(c=0;c<b;c++)a[c].addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&gotoPage(a.target.getAttribute("data-page"))},!1);document.getElementById("outputs").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&a.stopPropagation(); | ||||
| sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:a.target.getAttribute("data-output-id"),state:a.target.classList.contains("active")?0:1}});toggleBtn(a.target.id)},!1);document.getElementById("QueueList").addEventListener("click",function(a){"TD"==a.target.nodeName?sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:a.target.parentNode.getAttribute("data-trackid")}}):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseFilesystemList").addEventListener("click", | ||||
| function(a){if("TD"==a.target.nodeName)switch(a.target.parentNode.getAttribute("data-type")){case "dir":appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+decodeURI(a.target.parentNode.getAttribute("data-uri")));break;case "song":appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"));break;case "plist":appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name"))}else"A"== | ||||
| @@ -45,8 +45,8 @@ b.nodeName&&b.classList.remove("dragover")},!1);b.addEventListener("dragover",fu | ||||
| document.getElementById(a.dataTransfer.getData("Text")).classList.remove("opacity05")},!1);b.addEventListener("drop",function(c){c.stopPropagation();c.preventDefault();var e=c.target;"TD"==c.target.nodeName&&(e=c.target.parentNode);var d=document.getElementById(c.dataTransfer.getData("Text")).getAttribute("data-songpos"),f=e.getAttribute("data-songpos");document.getElementById(c.dataTransfer.getData("Text")).remove();dragEl.classList.remove("opacity05");b.insertBefore(dragEl,e);c=b.querySelectorAll(".dragover"); | ||||
| e=c.length;for(var g=0;g<e;g++)c[g].classList.remove("dragover");document.getElementById(a).classList.add("opacity05");"Queue"==app.current.app?sendAPI({cmd:"MPD_API_MOVE_TRACK",data:{from:d,to:f}}):"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view&&playlistMoveTrack(d,f)},!1)} | ||||
| function playlistMoveTrack(a,b){sendAPI({cmd:"MPD_API_PLAYLIST_MOVE_TRACK",data:{plist:app.current.search,from:a,to:b}});sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} | ||||
| function webSocketConnect(){socket=new WebSocket(getWsUrl());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");modalConnectionError.hide();appRoute();sendAPI({cmd:"MPD_API_GET_OUTPUTNAMES"},parseOutputnames)},socket.onmessage=function(a){if(a.data!==last_state&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "state":parseState(b);break;case "disconnected":showNotification("myMPD lost connection to MPD", | ||||
| "","","danger");break;case "update_queue":"Queue"===app.current.app&&getQueue();break;case "song_change":songChange(b);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("Error: "+a)}} | ||||
| function webSocketConnect(){socket=new WebSocket(getWsUrl());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");modalConnectionError.hide();appRoute()},socket.onmessage=function(a){if(a.data!==last_state&&0!=a.data.length){try{var b=JSON.parse(a.data)}catch(c){console.log("Invalid JSON data received: "+a.data)}switch(b.type){case "state":parseState(b);break;case "disconnected":showNotification("myMPD lost connection to MPD","","","danger"); | ||||
| break;case "update_queue":"Queue"===app.current.app&&getQueue();break;case "song_change":songChange(b);break;case "error":showNotification(b.data,"","","danger")}}},socket.onclose=function(){console.log("disconnected");modalConnectionError.show();setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("Error: "+a)}} | ||||
| function getWsUrl(){var a=document.URL;if("https"==a.substring(0,5)){var b="wss://";a=a.substr(8)}else b="ws://","http"==a.substring(0,4)&&(a=a.substr(7));a=a.split("#");var c=/\/$/.test(a[0])?"":"/";return b+a[0]+c+"ws"} | ||||
| function parseStats(a){document.getElementById("mpdstats_artists").innerText=a.data.artists;document.getElementById("mpdstats_albums").innerText=a.data.albums;document.getElementById("mpdstats_songs").innerText=a.data.songs;document.getElementById("mpdstats_dbplaytime").innerText=beautifyDuration(a.data.dbplaytime);document.getElementById("mpdstats_playtime").innerText=beautifyDuration(a.data.playtime);document.getElementById("mpdstats_uptime").innerText=beautifyDuration(a.data.uptime);var b=new Date(1E3* | ||||
| a.data.dbupdated);document.getElementById("mpdstats_dbupdated").innerText=b.toUTCString();document.getElementById("mympdVersion").innerText=a.data.mympd_version;document.getElementById("mpdVersion").innerText=a.data.mpd_version}function toggleBtn(a,b){if(a=document.getElementById(a))void 0==b&&(b=a.classList.contains("active")?0:1),1==b?(a.classList.add("active"),a.setAttribute("aria-pressed","true")):(a.classList.remove("active"),a.setAttribute("aria-pressed","false"))} | ||||
| @@ -54,12 +54,12 @@ function parseSettings(a){toggleBtn("btnRandom",a.data.random);toggleBtn("btnCon | ||||
| document.getElementById("inputMixrampdb").value=a.data.mixrampdb):document.getElementById("inputMixrampdb").setAttribute("disabled","disabled");void 0!=a.data.mixrampdelay?(document.getElementById("inputMixrampdelay").removeAttribute("disabled"),document.getElementById("inputMixrampdelay").value=a.data.mixrampdelay):document.getElementById("inputMixrampdelay").setAttribute("disabled","disabled");document.getElementById("selectReplaygain").value=a.data.replaygain;var b=document.getElementById("btnnotifyWeb"); | ||||
| notificationsSupported()?a.data.notificationWeb?(toggleBtn("btnnotifyWeb",a.data.notificationWeb),Notification.requestPermission(function(b){"permission"in Notification||(Notification.permission=b);"granted"===b?toggleBtn("btnnotifyWeb",1):(toggleBtn("btnnotifyWeb",0),a.data.notificationWeb=0)})):toggleBtn("btnnotifyWeb",0):(b.setAttribute("disabled","disabled"),toggleBtn("btnnotifyWeb",0));toggleBtn("btnnotifyPage",a.data.notificationPage);settings=a.data;settings.mpdstream="http://";settings.mpdstream= | ||||
| "127.0.0.1"==settings.mpdhost||"localhost"==settings.mpdhost?settings.mpdstream+window.location.hostname:settings.mpdstream+settings.mpdhost;settings.mpdstream+=":"+settings.streamport+"/"}function getSettings(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)} | ||||
| function parseOutputnames(a){for(var b="",c=a.data.outputs.length,e=0;e<c;e++)b+='<button id="btnoutput'+a.data.outputs[e].id+'" data-output-id="'+a.data.outputs[e].id+'" class="btn btn-secondary btn-block"><span class="material-icons float-left">volume_up</span> '+a.data.outputs[e].name+"</button>";domCache.outputs.innerHTML=b} | ||||
| function parseOutputnames(a){for(var b="",c=a.data.outputs.length,e=0;e<c;e++)b+='<button data-output-id="'+a.data.outputs[e].id+'" class="btn btn-secondary btn-block',1==a.data.outputs[e].state&&(b+=" active"),b+='"><span class="material-icons float-left">volume_up</span> '+a.data.outputs[e].name+"</button>";domCache.outputs.innerHTML=b} | ||||
| function parseState(a){if(JSON.stringify(a)!==JSON.stringify(last_state)){1==a.data.state?(domCache.btnPlay.innerText="play_arrow",playstate="stop"):2==a.data.state?(domCache.btnPlay.innerText="pause",playstate="play"):(domCache.btnPlay.innerText="play_arrow",playstate="pause");-1==a.data.nextsongpos?domCache.btnNext.setAttribute("disabled","disabled"):domCache.btnNext.removeAttribute("disabled");0>=a.data.songpos?domCache.btnPrev.setAttribute("disabled","disabled"):domCache.btnPrev.removeAttribute("disabled"); | ||||
| 0==a.data.queue_length?domCache.btnPlay.setAttribute("disabled","disabled"):domCache.btnPlay.removeAttribute("disabled");-1==a.data.volume?(domCache.volumePrct.innerText="Volumecontrol disabled",domCache.volumeControl.classList.add("hide")):(domCache.volumeControl.classList.remove("hide"),domCache.volumePrct.innerText=a.data.volume+" %",domCache.volumeIcon.innerText=0==a.data.volume?"volume_off":50>a.data.volume?"volume_down":"volume_up");domCache.volumeBar.value=a.data.volume;current_song.totalTime= | ||||
| a.data.totalTime;current_song.currentSongId=a.data.currentsongid;var b=Math.floor(a.data.totalTime/60),c=a.data.totalTime-60*b,e=Math.floor(a.data.elapsedTime/60),d=a.data.elapsedTime-60*e;domCache.progressBar.value=Math.floor(100*a.data.elapsedTime/a.data.totalTime);b=e+":"+(10>d?"0":"")+d+" / "+b+":"+(10>c?"0":"")+c;domCache.counter.innerText=b;last_state&&(c=document.getElementById("queueTrackId"+last_state.data.currentsongid))&&(e=c.getElementsByTagName("td"),e[4].innerText=c.getAttribute("data-duration"), | ||||
| e[0].classList.remove("material-icons"),e[0].innerText=c.getAttribute("data-songpos"),c.classList.remove("font-weight-bold"));if(c=document.getElementById("queueTrackId"+a.data.currentsongid))e=c.getElementsByTagName("td"),e[4].innerText=b,e[0].classList.add("material-icons"),e[0].innerText="play_arrow",c.classList.add("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);b=a.data.outputs.length;for(c=0;c< | ||||
| b;c++)toggleBtn("btnoutput"+a.data.outputs[c].id,a.data.outputs[c].state);last_state=a}}function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)} | ||||
| e[0].classList.remove("material-icons"),e[0].innerText=c.getAttribute("data-songpos"),c.classList.remove("font-weight-bold"));if(c=document.getElementById("queueTrackId"+a.data.currentsongid))e=c.getElementsByTagName("td"),e[4].innerText=b,e[0].classList.add("material-icons"),e[0].innerText="play_arrow",c.classList.add("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);"-1"==a.data.songpos&&(domCache.currentTrack.innerText= | ||||
| "Not playing",document.getElementById("currentAlbum").innerText="",document.getElementById("currentArtist").innerText="",document.getElementById("currentCover").style.backgroundImage="");last_state=a}}function getQueue(){2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH_QUEUE",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseQueue):sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue)} | ||||
| function parseQueue(a){if("Queue"===app.current.app){0<a.totalTime?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs \u2013 "+beautifyDuration(a.totalTime):0<a.totalEntities?document.getElementById("panel-heading-queue").innerText=a.totalEntities+" Songs":document.getElementById("panel-heading-queue").innerText="";var b=a.data.length,c=document.getElementById(app.current.app+"List");c.setAttribute("data-version",a.queue_version);c=c.getElementsByTagName("tbody")[0]; | ||||
| for(var e=c.getElementsByTagName("tr"),d=0;d<b;d++)if(!e[d]||e[d].getAttribute("data-trackid")!=a.data[d].id||e[d].getAttribute("data-songpos")!=a.data[d].pos+1){var f=Math.floor(a.data[d].duration/60),g=a.data[d].duration-60*f;f=f+":"+(10>g?"0":"")+g;g=document.createElement("tr");g.setAttribute("draggable","true");g.setAttribute("data-trackid",a.data[d].id);g.setAttribute("id","queueTrackId"+a.data[d].id);g.setAttribute("data-songpos",a.data[d].pos+1);g.setAttribute("data-duration",f);g.setAttribute("data-uri", | ||||
| a.data[d].uri);g.innerHTML="<td>"+(a.data[d].pos+1)+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(g):c.append(g)}for(d=e.length-1;d>=b;d--)e[d].remove();"queuesearch"==a.type&&0==b?c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results, please refine your search!</td></tr>':"queue"==a.type&& | ||||
| @@ -75,11 +75,11 @@ document.getElementById("BrowsePlaylistsAllList").classList.add("hide"),document | ||||
| h.setAttribute("data-uri",f);h.setAttribute("data-type","plist");h.setAttribute("data-name",a.data[d].name);h.innerHTML='<td><span class="material-icons">list</span></td><td>'+a.data[d].name+"</td><td>"+g.toUTCString()+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';d<e.length?e[d].replaceWith(h):c.append(h)}}else if("Detail"==app.current.view)for(d=0;d<b;d++)if(f=encodeURI(a.data[d].uri),g=a.offset+d+1,!e[d]||e[d].getAttribute("data-uri")!=f||e[d].getAttribute("id")!= | ||||
| "playlistTrackId"+g){h=document.createElement("tr");h.setAttribute("draggable","true");h.setAttribute("id","playlistTrackId"+g);h.setAttribute("data-type",a.data[d].type);h.setAttribute("data-uri",f);h.setAttribute("data-name",a.data[d].name);h.setAttribute("data-songpos",g);f=Math.floor(a.data[d].duration/60);var k=a.data[d].duration-60*f;h.innerHTML="<td>"+g+"</td><td>"+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>k?"0":"")+k+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>'; | ||||
| d<e.length?e[d].replaceWith(h):c.append(h)}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML="All"==app.current.view?'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No playlists found.</td></tr>':'<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty playlist.</td></tr>');document.getElementById(app.current.app+app.current.tab+app.current.view+"List").classList.remove("opacity05")}} | ||||
| function parseListDBtags(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view)if("AlbumArtist"==a.tagtype){document.getElementById("BrowseDatabaseAlbumCards").classList.add("hide");document.getElementById("BrowseDatabaseArtistList").classList.remove("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.add("hide");for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0], | ||||
| function parseListDBtags(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view)if("AlbumArtist"==a.tagtype){document.getElementById("BrowseDatabaseAlbumList").classList.add("hide");document.getElementById("BrowseDatabaseArtistList").classList.remove("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.add("hide");for(var b=a.data.length,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0], | ||||
| e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].value);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-uri",f);g.innerHTML='<td><span class="material-icons">album</span></td><td>'+a.data[d].value+"</td>";d<e.length?e[d].replaceWith(g):c.append(g)}}for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No entries found.</td></tr>'); | ||||
| document.getElementById("BrowseDatabaseArtistList").classList.remove("opacity05")}else if("Album"==a.tagtype){document.getElementById("BrowseDatabaseAlbumCards").classList.remove("hide");document.getElementById("BrowseDatabaseArtistList").classList.add("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.remove("hide");b=a.data.length;c=document.getElementById("BrowseDatabaseAlbumCards");e=c.querySelectorAll(".col-md");for(d=0;d<b;d++)f=genId(a.data[d].value),e[d]&&e[d].getAttribute("id")== | ||||
| document.getElementById("BrowseDatabaseArtistList").classList.remove("opacity05")}else if("Album"==a.tagtype){document.getElementById("BrowseDatabaseAlbumList").classList.remove("hide");document.getElementById("BrowseDatabaseArtistList").classList.add("hide");document.getElementById("btnBrowseDatabaseArtist").parentNode.classList.remove("hide");b=a.data.length;c=document.getElementById("BrowseDatabaseAlbumList");e=c.querySelectorAll(".col-md");for(d=0;d<b;d++)f=genId(a.data[d].value),e[d]&&e[d].getAttribute("id")== | ||||
| f||(g=document.createElement("div"),g.classList.add("col-md"),g.classList.add("mr-0"),g.setAttribute("id",f),g.innerHTML='<div class="card mb-4" id="card'+f+'"> <a href="#" class="card-img-top"><img class="card-img-top" src="" ></a> <div class="card-body">  <h5 class="card-title">'+a.searchstr+'</h5>  <h4 class="card-title">'+a.data[d].value+'</h4>  <table class="table table-sm table-hover" id="tbl'+f+'"><tbody></tbody></table </div></div>',d<e.length?e[d].replaceWith(g):c.append(g),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMTITLES", | ||||
| data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles));for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);document.getElementById("BrowseDatabaseAlbumCards").classList.remove("opacity05")}} | ||||
| data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles));for(d=e.length-1;d>=b;d--)e[d].remove();setPagination(a.totalEntities);document.getElementById("BrowseDatabaseAlbumList").classList.remove("opacity05")}} | ||||
| function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=document.getElementById("card"+b);b=c.getElementsByTagName("tbody")[0];var e=c.getElementsByTagName("img")[0];c=e.parentNode;e.setAttribute("src",a.cover);c.setAttribute("data-uri",encodeURI(a.data[0].uri.replace(/\/[^\/]+$/,"")));c.setAttribute("data-name",a.album);c.setAttribute("data-type","dir");e="";for(var d=a.data.length,f=0;f<d;f++)e+='<tr data-type="song" data-name="'+ | ||||
| a.data[f].title+'" data-uri="'+encodeURI(a.data[f].uri)+'"><td>'+a.data[f].track+"</td><td>"+a.data[f].title+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td></tr>';b.innerHTML=e;c.addEventListener("click",function(a){a.preventDefault();showMenu(this)},!1);b.parentNode.addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("song",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&& | ||||
| (a.preventDefault(),showMenu(a.target))},!1)}} | ||||
| @@ -88,8 +88,8 @@ function setPagination(a){var b=Math.ceil(a/settings.max_elements_per_page),c=ap | ||||
| document.getElementById(c+"ButtonsBottom").classList.add("hide"));0<app.current.page?(document.getElementById(c+e[d]+"Prev").removeAttribute("disabled"),document.getElementById(c+e[d]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):document.getElementById(c+e[d]+"Prev").setAttribute("disabled","disabled")}} | ||||
| function appendQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:b}});showNotification('"'+c+'" added',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_ADD_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" added',"","","success")}} | ||||
| function appendAfterQueue(a,b,c,e){switch(a){case "song":sendAPI({cmd:"MPD_API_ADD_TRACK_AFTER",data:{uri:b,to:c}}),showNotification('"'+e+'" added to pos '+c,"","","success")}} | ||||
| function replaceQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}} | ||||
| function songDetails(a){sendAPI({cmd:"MPD_API_GET_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()} | ||||
| function replaceQueue(a,b,c){switch(a){case "song":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "dir":sendAPI({cmd:"MPD_API_REPLACE_TRACK",data:{uri:b}});showNotification('"'+c+'" replaced',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_REPLACE_PLAYLIST",data:{plist:b}}),showNotification('"'+c+'" replaced',"","","success")}}function songClick(){songDetails(domCache.currentTrack.getAttribute("data-uri"))} | ||||
| function artistClick(){appGoto("Browse","Database","Album","0/-/"+document.getElementById("currentArtist").innerText)}function songDetails(a){sendAPI({cmd:"MPD_API_GET_SONGDETAILS",data:{uri:a}},parseSongDetails);modalSongDetails.show()} | ||||
| function parseSongDetails(a){var b=document.getElementById("modalSongDetails");b.querySelector(".album-cover").style.backgroundImage='url("'+a.data.cover+'")';b.getElementsByTagName("h1")[0].innerText=a.data.title;b=b.getElementsByTagName("tr");for(var c=b.length,e=0;e<c;e++){var d=b[e].getAttribute("data-name"),f=a.data[d];"duration"==d?(d=Math.floor(f/60),f-=60*d,f=d+":"+(10>f?"0":"")+f):"uri"==d&&(f='<a class="text-success" href="/library/'+f+'">'+f+"</a>");b[e].getElementsByTagName("td")[1].innerHTML= | ||||
| f}}function playlistDetails(a){appGoto("Browse","Playlists","Detail","0/-/"+a)}function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_RM_PLAYLIST_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} | ||||
| function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_PLAYLIST_LIST",data:{offset:app.current.page,filter:app.current.filter,uri:app.current.search}},parsePlaylists)} | ||||
| @@ -125,9 +125,10 @@ function saveQueue(){var a=document.getElementById("saveQueueName").value,b=a.re | ||||
| function showNotification(a,b,c,e){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&(document.getElementById("alertBox")?b=document.getElementById("alertBox"):(b=document.createElement("div"),b.setAttribute("id","alertBox"),b.addEventListener("click",function(){hideNotification()},!1)),b.classList.remove("alert-success","alert-danger"),b.classList.add("alert","alert-"+e),b.innerHTML="<div><strong>"+ | ||||
| a+"</strong><br/>"+c+"</div>",document.getElementsByTagName("main")[0].append(b),document.getElementById("alertBox").classList.add("alertBoxActive"),alertTimeout&&clearTimeout(alertTimeout),alertTimeout=setTimeout(function(){hideNotification()},3E3))}function hideNotification(){document.getElementById("alertBox")&&(document.getElementById("alertBox").classList.remove("alertBoxActive"),setTimeout(function(){document.getElementById("alertBox").remove()},600))} | ||||
| function notificationsSupported(){return"Notification"in window} | ||||
| function songChange(a){if("error"!=a.type&&"result"!=a.type){var b=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid;if(last_song!=b){var c="",e="",d="myMPD: ";document.getElementById("album-cover").style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(c+=a.data.artist,e+=a.data.artist,d+=a.data.artist+" - ",document.getElementById("artist").innerText=a.data.artist):document.getElementById("artist").innerText= | ||||
| "";"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,e+="<br/>"+a.data.album,document.getElementById("album").innerText=a.data.album):document.getElementById("album").innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=a.data.title,document.getElementById("currenttrack").innerText=a.data.title):document.getElementById("currenttrack").innerText="";document.title=d;if(d=document.getElementById("queueTrackId"+a.data.currentsongid))d.getElementsByTagName("td")[1].innerText= | ||||
| a.data.title;showNotification(a.data.title,c,e,"success");last_song=b}}}function doSetFilterLetter(a){var b=document.getElementById(a+"Letters").querySelector(".active");b&&b.classList.remove("active");b=app.current.filter;"0"==b&&(b="#");document.getElementById(a).innerText="Filter"+("-"!=b?": "+b:"");if("-"!=b){a=document.getElementById(a+"Letters").getElementsByTagName("button");for(var c=a.length,e=0;e<c;e++)if(a[e].innerText==b){a[e].classList.add("active");break}}} | ||||
| function songChange(a){if("error"!=a.type&&"result"!=a.type){var b=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid;if(last_song!=b){var c="",e="",d="myMPD: ";document.getElementById("currentCover").style.backgroundImage='url("'+a.data.cover+'")';"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(c+=a.data.artist,e+=a.data.artist,d+=a.data.artist+" - ",document.getElementById("currentArtist").innerText=a.data.artist):document.getElementById("currentArtist").innerText= | ||||
| "";"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(c+=" - "+a.data.album,e+="<br/>"+a.data.album,document.getElementById("currentAlbum").innerText=a.data.album):document.getElementById("currentAlbum").innerText="";"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=a.data.title,domCache.currentTrack.innerText=a.data.title,domCache.currentTrack.setAttribute("data-uri",a.data.uri)):(domCache.currentTrack.innerText="",domCache.currentTrack.setAttribute("data-uri", | ||||
| ""));document.title=d;if(d=document.getElementById("queueTrackId"+a.data.currentsongid))d.getElementsByTagName("td")[1].innerText=a.data.title;showNotification(a.data.title,c,e,"success");last_song=b}}} | ||||
| function doSetFilterLetter(a){var b=document.getElementById(a+"Letters").querySelector(".active");b&&b.classList.remove("active");b=app.current.filter;"0"==b&&(b="#");document.getElementById(a).innerText="Filter"+("-"!=b?": "+b:"");if("-"!=b){a=document.getElementById(a+"Letters").getElementsByTagName("button");for(var c=a.length,e=0;e<c;e++)if(a[e].innerText==b){a[e].classList.add("active");break}}} | ||||
| function addFilterLetter(a){for(var b='<button class="mr-1 mb-1 btn btn-sm btn-secondary material-icons material-icons-small">delete</button><button class="mr-1 mb-1 btn btn-sm btn-secondary">#</button>',c=65;90>=c;c++)b+='<button class="mr-1 mb-1 btn-sm btn btn-secondary">'+String.fromCharCode(c)+"</button>";a=document.getElementById(a);a.innerHTML=b;a.addEventListener("click",function(a){switch(a.target.innerText){case "delete":b="-";break;case "#":b="0";break;default:b=a.target.innerText}appGoto(app.current.app, | ||||
| app.current.tab,app.current.view,"0/"+b+"/"+app.current.search)},!1)}function chVolume(a){a=parseInt(domCache.volumeBar.value)+a;0>a?a=0:100<a&&(a=100);domCache.volumeBar.value=a;sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:a}})}function beautifyDuration(a){var b=Math.floor(a/86400),c=Math.floor(a/3600)-24*b,e=Math.floor(a/60)-60*c-1440*b;a=a-86400*b-3600*c-60*e;return(0<b?b+"\u2009d ":"")+(0<c?c+"\u2009h "+(10>e?"0":""):"")+e+"\u2009m "+(10>a?"0":"")+a+"\u2009s"} | ||||
| function genId(a){return"id"+a.replace(/[^\w]/g,"")}appInit(); | ||||
|   | ||||
							
								
								
									
										2
									
								
								dist/htdocs/sw.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/htdocs/sw.min.js
									
									
									
									
										vendored
									
									
								
							| @@ -10,5 +10,5 @@ function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c= | ||||
| void 0;try{d=a.then}catch(h){this.reject_(h);return}"function"==typeof d?this.settleSameAsThenable_(d,a):this.fulfill_(a)};c.prototype.reject_=function(a){this.settle_(2,a)};c.prototype.fulfill_=function(a){this.settle_(1,a)};c.prototype.settle_=function(a,b){if(0!=this.state_)throw Error("Cannot settle("+a+", "+b+"): Promise already settled in state"+this.state_);this.state_=a;this.result_=b;this.executeOnSettledCallbacks_()};c.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var a= | ||||
| 0;a<this.onSettledCallbacks_.length;++a)l.asyncExecute(this.onSettledCallbacks_[a]);this.onSettledCallbacks_=null}};var l=new b;c.prototype.settleSameAsPromise_=function(a){var b=this.createResolveAndReject_();a.callWhenSettled_(b.resolve,b.reject)};c.prototype.settleSameAsThenable_=function(a,b){var c=this.createResolveAndReject_();try{a.call(b,c.resolve,c.reject)}catch(k){c.reject(k)}};c.prototype.then=function(a,b){function d(a,b){return"function"==typeof a?function(b){try{f(a(b))}catch(m){e(m)}}: | ||||
| b}var f,e,g=new c(function(a,b){f=a;e=b});this.callWhenSettled_(d(a,f),d(b,e));return g};c.prototype.catch=function(a){return this.then(void 0,a)};c.prototype.callWhenSettled_=function(a,b){function c(){switch(d.state_){case 1:a(d.result_);break;case 2:b(d.result_);break;default:throw Error("Unexpected state: "+d.state_);}}var d=this;null==this.onSettledCallbacks_?l.asyncExecute(c):this.onSettledCallbacks_.push(c)};c.resolve=f;c.reject=function(a){return new c(function(b,c){c(a)})};c.race=function(a){return new c(function(b, | ||||
| c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v3.4.0",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mpd.min.css /js/bootstrap-native-v4.min.js /js/mpd.min.js /js/player.min.js /assets/appicon-167.png /assets/appicon-192.png /assets/appicon-512.png /assets/coverimage-httpstream.png /assets/coverimage-notavailable.png /assets/favicon.ico /assets/MaterialIcons-Regular.eot /assets/MaterialIcons-Regular.ttf /assets/MaterialIcons-Regular.woff /assets/MaterialIcons-Regular.woff2".split(" "); | ||||
| c){for(var d=$jscomp.makeIterator(a),e=d.next();!e.done;e=d.next())f(e.value).callWhenSettled_(b,c)})};c.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?f([]):new c(function(a,c){function e(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,f(d.value).callWhenSettled_(e(g.length-1),c),d=b.next();while(!d.done)})};return c},"es6","es3");var CACHE="myMPD-cache-v3.5.0",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mpd.min.css /js/bootstrap-native-v4.min.js /js/mpd.min.js /js/player.min.js /assets/appicon-167.png /assets/appicon-192.png /assets/appicon-512.png /assets/coverimage-httpstream.png /assets/coverimage-notavailable.png /assets/favicon.ico /assets/MaterialIcons-Regular.eot /assets/MaterialIcons-Regular.ttf /assets/MaterialIcons-Regular.woff /assets/MaterialIcons-Regular.woff2".split(" "); | ||||
| self.addEventListener("install",function(a){a.waitUntil(caches.open(CACHE).then(function(a){return a.addAll(urlsToCache)}))});self.addEventListener("fetch",function(a){a.respondWith(caches.match(a.request).then(function(b){return b?b:fetch(a.request)}))});self.addEventListener("activate",function(a){a.waitUntil(caches.keys().then(function(a){return Promise.all(a.map(function(a){if(a!=CACHE)return caches.delete(a)}))}))}); | ||||
|   | ||||
							
								
								
									
										27
									
								
								dist/src/inih/LICENSE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								dist/src/inih/LICENSE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
|  | ||||
| The "inih" library is distributed under the New BSD license: | ||||
|  | ||||
| Copyright (c) 2009, Ben Hoyt | ||||
| All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|     * Redistributions of source code must retain the above copyright | ||||
|       notice, this list of conditions and the following disclaimer. | ||||
|     * Redistributions in binary form must reproduce the above copyright | ||||
|       notice, this list of conditions and the following disclaimer in the | ||||
|       documentation and/or other materials provided with the distribution. | ||||
|     * Neither the name of Ben Hoyt nor the names of its contributors | ||||
|       may be used to endorse or promote products derived from this software | ||||
|       without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY | ||||
| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY | ||||
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										269
									
								
								dist/src/inih/ini.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								dist/src/inih/ini.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | ||||
| /* inih -- simple .INI file parser | ||||
|  | ||||
| inih is released under the New BSD license (see LICENSE.txt). Go to the project | ||||
| home page for more info: | ||||
|  | ||||
| https://github.com/benhoyt/inih | ||||
|  | ||||
| */ | ||||
|  | ||||
| #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) | ||||
| #define _CRT_SECURE_NO_WARNINGS | ||||
| #endif | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "ini.h" | ||||
|  | ||||
| #if !INI_USE_STACK | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
|  | ||||
| #define MAX_SECTION 50 | ||||
| #define MAX_NAME 50 | ||||
|  | ||||
| /* Used by ini_parse_string() to keep track of string parsing state. */ | ||||
| typedef struct { | ||||
|     const char* ptr; | ||||
|     size_t num_left; | ||||
| } ini_parse_string_ctx; | ||||
|  | ||||
| /* Strip whitespace chars off end of given string, in place. Return s. */ | ||||
| static char* rstrip(char* s) | ||||
| { | ||||
|     char* p = s + strlen(s); | ||||
|     while (p > s && isspace((unsigned char)(*--p))) | ||||
|         *p = '\0'; | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| /* Return pointer to first non-whitespace char in given string. */ | ||||
| static char* lskip(const char* s) | ||||
| { | ||||
|     while (*s && isspace((unsigned char)(*s))) | ||||
|         s++; | ||||
|     return (char*)s; | ||||
| } | ||||
|  | ||||
| /* Return pointer to first char (of chars) or inline comment in given string, | ||||
|    or pointer to null at end of string if neither found. Inline comment must | ||||
|    be prefixed by a whitespace character to register as a comment. */ | ||||
| static char* find_chars_or_comment(const char* s, const char* chars) | ||||
| { | ||||
| #if INI_ALLOW_INLINE_COMMENTS | ||||
|     int was_space = 0; | ||||
|     while (*s && (!chars || !strchr(chars, *s)) && | ||||
|            !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { | ||||
|         was_space = isspace((unsigned char)(*s)); | ||||
|         s++; | ||||
|     } | ||||
| #else | ||||
|     while (*s && (!chars || !strchr(chars, *s))) { | ||||
|         s++; | ||||
|     } | ||||
| #endif | ||||
|     return (char*)s; | ||||
| } | ||||
|  | ||||
| /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ | ||||
| static char* strncpy0(char* dest, const char* src, size_t size) | ||||
| { | ||||
|     strncpy(dest, src, size - 1); | ||||
|     dest[size - 1] = '\0'; | ||||
|     return dest; | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, | ||||
|                      void* user) | ||||
| { | ||||
|     /* Uses a fair bit of stack (use heap instead if you need to) */ | ||||
| #if INI_USE_STACK | ||||
|     char line[INI_MAX_LINE]; | ||||
|     int max_line = INI_MAX_LINE; | ||||
| #else | ||||
|     char* line; | ||||
|     int max_line = INI_INITIAL_ALLOC; | ||||
| #endif | ||||
| #if INI_ALLOW_REALLOC && !INI_USE_STACK | ||||
|     char* new_line; | ||||
|     int offset; | ||||
| #endif | ||||
|     char section[MAX_SECTION] = ""; | ||||
|     char prev_name[MAX_NAME] = ""; | ||||
|  | ||||
|     char* start; | ||||
|     char* end; | ||||
|     char* name; | ||||
|     char* value; | ||||
|     int lineno = 0; | ||||
|     int error = 0; | ||||
|  | ||||
| #if !INI_USE_STACK | ||||
|     line = (char*)malloc(INI_INITIAL_ALLOC); | ||||
|     if (!line) { | ||||
|         return -2; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| #if INI_HANDLER_LINENO | ||||
| #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) | ||||
| #else | ||||
| #define HANDLER(u, s, n, v) handler(u, s, n, v) | ||||
| #endif | ||||
|  | ||||
|     /* Scan through stream line by line */ | ||||
|     while (reader(line, max_line, stream) != NULL) { | ||||
| #if INI_ALLOW_REALLOC && !INI_USE_STACK | ||||
|         offset = strlen(line); | ||||
|         while (offset == max_line - 1 && line[offset - 1] != '\n') { | ||||
|             max_line *= 2; | ||||
|             if (max_line > INI_MAX_LINE) | ||||
|                 max_line = INI_MAX_LINE; | ||||
|             new_line = realloc(line, max_line); | ||||
|             if (!new_line) { | ||||
|                 free(line); | ||||
|                 return -2; | ||||
|             } | ||||
|             line = new_line; | ||||
|             if (reader(line + offset, max_line - offset, stream) == NULL) | ||||
|                 break; | ||||
|             if (max_line >= INI_MAX_LINE) | ||||
|                 break; | ||||
|             offset += strlen(line + offset); | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         lineno++; | ||||
|  | ||||
|         start = line; | ||||
| #if INI_ALLOW_BOM | ||||
|         if (lineno == 1 && (unsigned char)start[0] == 0xEF && | ||||
|                            (unsigned char)start[1] == 0xBB && | ||||
|                            (unsigned char)start[2] == 0xBF) { | ||||
|             start += 3; | ||||
|         } | ||||
| #endif | ||||
|         start = lskip(rstrip(start)); | ||||
|  | ||||
|         if (strchr(INI_START_COMMENT_PREFIXES, *start)) { | ||||
|             /* Start-of-line comment */ | ||||
|         } | ||||
| #if INI_ALLOW_MULTILINE | ||||
|         else if (*prev_name && *start && start > line) { | ||||
|             /* Non-blank line with leading whitespace, treat as continuation | ||||
|                of previous name's value (as per Python configparser). */ | ||||
|             if (!HANDLER(user, section, prev_name, start) && !error) | ||||
|                 error = lineno; | ||||
|         } | ||||
| #endif | ||||
|         else if (*start == '[') { | ||||
|             /* A "[section]" line */ | ||||
|             end = find_chars_or_comment(start + 1, "]"); | ||||
|             if (*end == ']') { | ||||
|                 *end = '\0'; | ||||
|                 strncpy0(section, start + 1, sizeof(section)); | ||||
|                 *prev_name = '\0'; | ||||
|             } | ||||
|             else if (!error) { | ||||
|                 /* No ']' found on section line */ | ||||
|                 error = lineno; | ||||
|             } | ||||
|         } | ||||
|         else if (*start) { | ||||
|             /* Not a comment, must be a name[=:]value pair */ | ||||
|             end = find_chars_or_comment(start, "=:"); | ||||
|             if (*end == '=' || *end == ':') { | ||||
|                 *end = '\0'; | ||||
|                 name = rstrip(start); | ||||
|                 value = end + 1; | ||||
| #if INI_ALLOW_INLINE_COMMENTS | ||||
|                 end = find_chars_or_comment(value, NULL); | ||||
|                 if (*end) | ||||
|                     *end = '\0'; | ||||
| #endif | ||||
|                 value = lskip(value); | ||||
|                 rstrip(value); | ||||
|  | ||||
|                 /* Valid name[=:]value pair found, call handler */ | ||||
|                 strncpy0(prev_name, name, sizeof(prev_name)); | ||||
|                 if (!HANDLER(user, section, name, value) && !error) | ||||
|                     error = lineno; | ||||
|             } | ||||
|             else if (!error) { | ||||
|                 /* No '=' or ':' found on name[=:]value line */ | ||||
|                 error = lineno; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| #if INI_STOP_ON_FIRST_ERROR | ||||
|         if (error) | ||||
|             break; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
| #if !INI_USE_STACK | ||||
|     free(line); | ||||
| #endif | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse_file(FILE* file, ini_handler handler, void* user) | ||||
| { | ||||
|     return ini_parse_stream((ini_reader)fgets, file, handler, user); | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse(const char* filename, ini_handler handler, void* user) | ||||
| { | ||||
|     FILE* file; | ||||
|     int error; | ||||
|  | ||||
|     file = fopen(filename, "r"); | ||||
|     if (!file) | ||||
|         return -1; | ||||
|     error = ini_parse_file(file, handler, user); | ||||
|     fclose(file); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| /* An ini_reader function to read the next line from a string buffer. This | ||||
|    is the fgets() equivalent used by ini_parse_string(). */ | ||||
| static char* ini_reader_string(char* str, int num, void* stream) { | ||||
|     ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; | ||||
|     const char* ctx_ptr = ctx->ptr; | ||||
|     size_t ctx_num_left = ctx->num_left; | ||||
|     char* strp = str; | ||||
|     char c; | ||||
|  | ||||
|     if (ctx_num_left == 0 || num < 2) | ||||
|         return NULL; | ||||
|  | ||||
|     while (num > 1 && ctx_num_left != 0) { | ||||
|         c = *ctx_ptr++; | ||||
|         ctx_num_left--; | ||||
|         *strp++ = c; | ||||
|         if (c == '\n') | ||||
|             break; | ||||
|         num--; | ||||
|     } | ||||
|  | ||||
|     *strp = '\0'; | ||||
|     ctx->ptr = ctx_ptr; | ||||
|     ctx->num_left = ctx_num_left; | ||||
|     return str; | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse_string(const char* string, ini_handler handler, void* user) { | ||||
|     ini_parse_string_ctx ctx; | ||||
|  | ||||
|     ctx.ptr = string; | ||||
|     ctx.num_left = strlen(string); | ||||
|     return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, | ||||
|                             user); | ||||
| } | ||||
							
								
								
									
										130
									
								
								dist/src/inih/ini.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								dist/src/inih/ini.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| /* inih -- simple .INI file parser | ||||
|  | ||||
| inih is released under the New BSD license (see LICENSE.txt). Go to the project | ||||
| home page for more info: | ||||
|  | ||||
| https://github.com/benhoyt/inih | ||||
|  | ||||
| */ | ||||
|  | ||||
| #ifndef __INI_H__ | ||||
| #define __INI_H__ | ||||
|  | ||||
| /* Make this header file easier to include in C++ code */ | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| /* Nonzero if ini_handler callback should accept lineno parameter. */ | ||||
| #ifndef INI_HANDLER_LINENO | ||||
| #define INI_HANDLER_LINENO 0 | ||||
| #endif | ||||
|  | ||||
| /* Typedef for prototype of handler function. */ | ||||
| #if INI_HANDLER_LINENO | ||||
| typedef int (*ini_handler)(void* user, const char* section, | ||||
|                            const char* name, const char* value, | ||||
|                            int lineno); | ||||
| #else | ||||
| typedef int (*ini_handler)(void* user, const char* section, | ||||
|                            const char* name, const char* value); | ||||
| #endif | ||||
|  | ||||
| /* Typedef for prototype of fgets-style reader function. */ | ||||
| typedef char* (*ini_reader)(char* str, int num, void* stream); | ||||
|  | ||||
| /* Parse given INI-style file. May have [section]s, name=value pairs | ||||
|    (whitespace stripped), and comments starting with ';' (semicolon). Section | ||||
|    is "" if name=value pair parsed before any section heading. name:value | ||||
|    pairs are also supported as a concession to Python's configparser. | ||||
|  | ||||
|    For each name=value pair parsed, call handler function with given user | ||||
|    pointer as well as section, name, and value (data only valid for duration | ||||
|    of handler call). Handler should return nonzero on success, zero on error. | ||||
|  | ||||
|    Returns 0 on success, line number of first error on parse error (doesn't | ||||
|    stop on first error), -1 on file open error, or -2 on memory allocation | ||||
|    error (only when INI_USE_STACK is zero). | ||||
| */ | ||||
| int ini_parse(const char* filename, ini_handler handler, void* user); | ||||
|  | ||||
| /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't | ||||
|    close the file when it's finished -- the caller must do that. */ | ||||
| int ini_parse_file(FILE* file, ini_handler handler, void* user); | ||||
|  | ||||
| /* Same as ini_parse(), but takes an ini_reader function pointer instead of | ||||
|    filename. Used for implementing custom or string-based I/O (see also | ||||
|    ini_parse_string). */ | ||||
| int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, | ||||
|                      void* user); | ||||
|  | ||||
| /* Same as ini_parse(), but takes a zero-terminated string with the INI data | ||||
| instead of a file. Useful for parsing INI data from a network socket or | ||||
| already in memory. */ | ||||
| int ini_parse_string(const char* string, ini_handler handler, void* user); | ||||
|  | ||||
| /* Nonzero to allow multi-line value parsing, in the style of Python's | ||||
|    configparser. If allowed, ini_parse() will call the handler with the same | ||||
|    name for each subsequent line parsed. */ | ||||
| #ifndef INI_ALLOW_MULTILINE | ||||
| #define INI_ALLOW_MULTILINE 1 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of | ||||
|    the file. See https://github.com/benhoyt/inih/issues/21 */ | ||||
| #ifndef INI_ALLOW_BOM | ||||
| #define INI_ALLOW_BOM 1 | ||||
| #endif | ||||
|  | ||||
| /* Chars that begin a start-of-line comment. Per Python configparser, allow | ||||
|    both ; and # comments at the start of a line by default. */ | ||||
| #ifndef INI_START_COMMENT_PREFIXES | ||||
| #define INI_START_COMMENT_PREFIXES ";#" | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow inline comments (with valid inline comment characters | ||||
|    specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match | ||||
|    Python 3.2+ configparser behaviour. */ | ||||
| #ifndef INI_ALLOW_INLINE_COMMENTS | ||||
| #define INI_ALLOW_INLINE_COMMENTS 1 | ||||
| #endif | ||||
| #ifndef INI_INLINE_COMMENT_PREFIXES | ||||
| #define INI_INLINE_COMMENT_PREFIXES ";" | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ | ||||
| #ifndef INI_USE_STACK | ||||
| #define INI_USE_STACK 1 | ||||
| #endif | ||||
|  | ||||
| /* Maximum line length for any line in INI file (stack or heap). Note that | ||||
|    this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ | ||||
| #ifndef INI_MAX_LINE | ||||
| #define INI_MAX_LINE 200 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow heap line buffer to grow via realloc(), zero for a | ||||
|    fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is | ||||
|    zero. */ | ||||
| #ifndef INI_ALLOW_REALLOC | ||||
| #define INI_ALLOW_REALLOC 0 | ||||
| #endif | ||||
|  | ||||
| /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK | ||||
|    is zero. */ | ||||
| #ifndef INI_INITIAL_ALLOC | ||||
| #define INI_INITIAL_ALLOC 200 | ||||
| #endif | ||||
|  | ||||
| /* Stop parsing on first error (default is to keep parsing). */ | ||||
| #ifndef INI_STOP_ON_FIRST_ERROR | ||||
| #define INI_STOP_ON_FIRST_ERROR 0 | ||||
| #endif | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif /* __INI_H__ */ | ||||
| @@ -75,7 +75,7 @@ | ||||
|     <div class="card" id="cardPlayback"> | ||||
|       <div class="card-header">Playback</div> | ||||
|       <div class="card-body"> | ||||
|         <div class="album-cover" id="album-cover"></div> | ||||
|         <div class="album-cover" id="currentCover"></div> | ||||
|         <h1 id="currentTrack" data-href="{'cmd': 'songClick', 'options': []}"></h1> | ||||
|         <h3 id="currentArtist" data-href="{'cmd': 'artistClick', 'options': []}"></h3> | ||||
|         <h4 id="currentAlbum"></h4> | ||||
|   | ||||
| @@ -83,7 +83,8 @@ var modalSavequeue = new Modal(document.getElementById('modalSaveQueue')); | ||||
| var modalSongDetails = new Modal(document.getElementById('modalSongDetails')); | ||||
| var modalAddToPlaylist = new Modal(document.getElementById('modalAddToPlaylist')); | ||||
| var modalRenamePlaylist = new Modal(document.getElementById('modalRenamePlaylist')); | ||||
| var mainMenu = new Dropdown(document.getElementById('mainMenu')); | ||||
| //var mainMenu = new Dropdown(document.getElementById('mainMenu')); | ||||
| //var volumeMenu = new Dropdown(document.getElementById('volumeIcon')); | ||||
|  | ||||
| function appPrepare(scrollPos) { | ||||
|     if (app.current.app != app.last.app || app.current.tab != app.last.tab || app.current.view != app.last.view) { | ||||
| @@ -287,7 +288,7 @@ function appRoute() { | ||||
|  | ||||
| function appInit() { | ||||
|     getSettings(); | ||||
|     sendAPI({"cmd":"MPD_API_GET_OUTPUTNAMES"}, parseOutputnames); | ||||
| //    sendAPI({"cmd":"MPD_API_GET_OUTPUTS"}, parseOutputnames); | ||||
|  | ||||
|     webSocketConnect(); | ||||
|  | ||||
| @@ -303,6 +304,10 @@ function appInit() { | ||||
|             sendAPI({"cmd": "MPD_API_SET_SEEK", "data": {"songid": current_song.currentSongId, "seek": seekVal}}); | ||||
|         } | ||||
|     }, false); | ||||
|    | ||||
|     document.getElementById('volumeIcon').parentNode.addEventListener('show.bs.dropdown', function () { | ||||
|         sendAPI({"cmd":"MPD_API_GET_OUTPUTS"}, parseOutputnames); | ||||
|     });     | ||||
|      | ||||
|     document.getElementById('modalAbout').addEventListener('shown.bs.modal', function () { | ||||
|         sendAPI({"cmd": "MPD_API_GET_STATS"}, parseStats); | ||||
| @@ -640,7 +645,6 @@ function webSocketConnect() { | ||||
|             showNotification('Connected to myMPD', '', '', 'success'); | ||||
|             modalConnectionError.hide(); | ||||
|             appRoute(); | ||||
|             sendAPI({"cmd":"MPD_API_GET_OUTPUTNAMES"}, parseOutputnames); | ||||
|         } | ||||
|  | ||||
|         socket.onmessage = function got_packet(msg) { | ||||
| @@ -807,8 +811,10 @@ function parseOutputnames(obj) { | ||||
|     var btns = ''; | ||||
|     var outputsLen = obj.data.outputs.length; | ||||
|     for (var i = 0; i < outputsLen; i++) { | ||||
|         btns += '<button id="btnoutput' + obj.data.outputs[i].id +'" data-output-id="' + obj.data.outputs[i].id + '" class="btn btn-secondary btn-block">'+ | ||||
|                 '<span class="material-icons float-left">volume_up</span> ' + obj.data.outputs[i].name + '</button>'; | ||||
|         btns += '<button data-output-id="' + obj.data.outputs[i].id + '" class="btn btn-secondary btn-block'; | ||||
|         if (obj.data.outputs[i].state == 1) | ||||
|             btns += ' active'; | ||||
|         btns += '"><span class="material-icons float-left">volume_up</span> ' + obj.data.outputs[i].name + '</button>'; | ||||
|     } | ||||
|     domCache.outputs.innerHTML = btns; | ||||
| } | ||||
| @@ -899,10 +905,14 @@ function parseState(obj) { | ||||
|     if (last_state == undefined || obj.data.queue_version != last_state.data.queue_version) | ||||
|         sendAPI({"cmd": "MPD_API_GET_CURRENT_SONG"}, songChange); | ||||
|          | ||||
|     //  Set outputs state                   | ||||
|     var outputsLen = obj.data.outputs.length; | ||||
|     for (var i = 0; i < outputsLen; i++) { | ||||
|         toggleBtn('btnoutput' + obj.data.outputs[i].id, obj.data.outputs[i].state); | ||||
|  | ||||
|      | ||||
|     //clear playback card if not playing | ||||
|     if (obj.data.songpos == '-1') { | ||||
|         domCache.currentTrack.innerText = 'Not playing'; | ||||
|         document.getElementById('currentAlbum').innerText = ''; | ||||
|         document.getElementById('currentArtist').innerText = ''; | ||||
|         document.getElementById('currentCover').style.backgroundImage = ''; | ||||
|     } | ||||
|  | ||||
|     last_state = obj;                     | ||||
| @@ -1841,7 +1851,7 @@ function songChange(obj) { | ||||
|     var htmlNotification = ''; | ||||
|     var pageTitle = 'myMPD: '; | ||||
|  | ||||
|     document.getElementById('album-cover').style.backgroundImage = 'url("' + obj.data.cover + '")'; | ||||
|     document.getElementById('currentCover').style.backgroundImage = 'url("' + obj.data.cover + '")'; | ||||
|  | ||||
|     if (typeof obj.data.artist != 'undefined' && obj.data.artist.length > 0 && obj.data.artist != '-') { | ||||
|         textNotification += obj.data.artist; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| var CACHE = 'myMPD-cache-v3.4.0'; | ||||
| var CACHE = 'myMPD-cache-v3.5.0'; | ||||
| var urlsToCache = [ | ||||
|     '/', | ||||
|     '/player.html', | ||||
|   | ||||
| @@ -81,4 +81,4 @@ else | ||||
| fi | ||||
|    | ||||
| echo "myMPD installed" | ||||
| echo "Edit /etc/mympd/options before starting mympd" | ||||
| echo "Edit /etc/mympd/mympd.conf before starting mympd" | ||||
|   | ||||
							
								
								
									
										288
									
								
								src/mpd_client.c
									
									
									
									
									
								
							
							
						
						
									
										288
									
								
								src/mpd_client.c
									
									
									
									
									
								
							| @@ -43,17 +43,15 @@ const char * mpd_cmd_strs[] = { | ||||
|     MPD_CMDS(GEN_STR) | ||||
| }; | ||||
|  | ||||
| static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) | ||||
| { | ||||
|     for(int i = 0; i < sizeof(mpd_cmd_strs)/sizeof(mpd_cmd_strs[0]); i++) | ||||
| static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { | ||||
|     for (int i = 0; i < sizeof(mpd_cmd_strs) / sizeof(mpd_cmd_strs[0]); i++) | ||||
|         if (!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i]))) | ||||
|             return i; | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
| { | ||||
| void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { | ||||
|     size_t n = 0; | ||||
|     char *cmd; | ||||
|     unsigned int uint_buf1, uint_buf2, uint_rc; | ||||
| @@ -81,41 +79,41 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); | ||||
|             break; | ||||
|         case MPD_API_SET_SETTINGS: | ||||
|             json_scanf(msg.p, msg.len, "{ data: { notificationWeb: %d, notificationPage: %d} }", &state.a, &state.b); | ||||
|             json_scanf(msg.p, msg.len, "{data: { notificationWeb: %d, notificationPage: %d}}", &state.a, &state.b); | ||||
|             char tmpfile[200]; | ||||
|             snprintf(tmpfile,200,"%s.tmp", mpd.statefile); | ||||
|             json_fprintf(tmpfile, "{ notificationWeb: %d, notificationPage: %d}", state.a, state.b); | ||||
|             rename(tmpfile,mpd.statefile); | ||||
|             snprintf(tmpfile,200,"%s.tmp", config.statefile); | ||||
|             json_fprintf(tmpfile, "{notificationWeb: %d, notificationPage: %d}", state.a, state.b); | ||||
|             rename(tmpfile,config.statefile); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { random:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {random:%u}}", &uint_buf1); | ||||
|             if (je == 1)         | ||||
|                 mpd_run_random(mpd.conn, uint_buf1); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { repeat:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {repeat:%u}}", &uint_buf1); | ||||
|             if (je == 1)         | ||||
|                 mpd_run_repeat(mpd.conn, uint_buf1); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { consume:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {consume:%u}}", &uint_buf1); | ||||
|             if (je == 1)         | ||||
|                 mpd_run_consume(mpd.conn, uint_buf1); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { single:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {single:%u}}", &uint_buf1); | ||||
|             if (je == 1) | ||||
|                 mpd_run_single(mpd.conn, uint_buf1); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { crossfade:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {crossfade:%u}}", &uint_buf1); | ||||
|             if (je == 1) | ||||
|                 mpd_run_crossfade(mpd.conn, uint_buf1); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { mixrampdb:%f } }", &float_buf); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {mixrampdb:%f}}", &float_buf); | ||||
|             if (je == 1)         | ||||
|                 mpd_run_mixrampdb(mpd.conn, float_buf); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { mixrampdelay:%f } }", &float_buf); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {mixrampdelay:%f}}", &float_buf); | ||||
|             if (je == 1)         | ||||
|                 mpd_run_mixrampdelay(mpd.conn, float_buf); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { replaygain:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {replaygain:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL); | ||||
|                 struct mpd_pair *pair; | ||||
| @@ -127,7 +125,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             break; | ||||
|         case MPD_API_GET_ARTISTALBUMTITLES: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { albumartist:%Q, album:%Q } }", &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {albumartist:%Q, album:%Q}}", &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 2) { | ||||
|                 n = mympd_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2); | ||||
|                 free(p_charbuf1); | ||||
| @@ -170,21 +168,21 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             n = mympd_queue_crop(mpd.buf); | ||||
|             break; | ||||
|         case MPD_API_RM_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {track:%u}}", &uint_buf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_delete_id(mpd.conn, uint_buf1); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_RM_RANGE: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { start:%u, end:%u } }", &uint_buf1, &uint_buf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {start:%u, end:%u}}", &uint_buf1, &uint_buf2); | ||||
|             if (je == 2) { | ||||
|                 mpd_run_delete_range(mpd.conn, uint_buf1, uint_buf2); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_MOVE_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { from:%u, to:%u } }", &uint_buf1, &uint_buf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {from:%u, to:%u}}", &uint_buf1, &uint_buf2); | ||||
|             if (je == 2) { | ||||
|                 uint_buf1 --; | ||||
|                 uint_buf2 --; | ||||
| @@ -195,7 +193,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_PLAYLIST_MOVE_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { plist: %Q, from:%u, to:%u } }", &p_charbuf1, &uint_buf1, &uint_buf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {plist: %Q, from:%u, to:%u }}", &p_charbuf1, &uint_buf1, &uint_buf2); | ||||
|             if (je == 3) { | ||||
|                 uint_buf1 --; | ||||
|                 uint_buf2 --; | ||||
| @@ -208,17 +206,17 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_PLAY_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: { track:%u}}", &uint_buf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_play_id(mpd.conn, uint_buf1); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_GET_OUTPUTNAMES: | ||||
|             n = mympd_put_outputnames(mpd.buf); | ||||
|         case MPD_API_GET_OUTPUTS: | ||||
|             n = mympd_put_outputs(mpd.buf); | ||||
|             break; | ||||
|         case MPD_API_TOGGLE_OUTPUT: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { output:%u, state:%u } }", &uint_buf1, &uint_buf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {output:%u, state:%u}}", &uint_buf1, &uint_buf2); | ||||
|             if (je == 2) { | ||||
|                 if (uint_buf2) | ||||
|                     mpd_run_enable_output(mpd.conn, uint_buf1); | ||||
| @@ -228,21 +226,21 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_SET_VOLUME: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { volume:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {volume:%u}}", &uint_buf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_set_volume(mpd.conn, uint_buf1); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_SET_SEEK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { songid:%u, seek:%u } }", &uint_buf1, &uint_buf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {songid:%u, seek:%u}}", &uint_buf1, &uint_buf2); | ||||
|             if (je == 2) { | ||||
|                 mpd_run_seek_id(mpd.conn, uint_buf1, uint_buf2); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_GET_QUEUE: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u } }", &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u }}", &uint_buf1); | ||||
|             if (je == 1) { | ||||
|                 n = mympd_put_queue(mpd.buf, uint_buf1); | ||||
|             } | ||||
| @@ -251,21 +249,21 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|                 n = mympd_put_current_song(mpd.buf); | ||||
|             break; | ||||
|         case MPD_API_GET_SONGDETAILS: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: { uri:%Q }}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 n = mympd_put_songdetails(mpd.buf, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
|             } | ||||
|             break;             | ||||
|         case MPD_API_GET_ARTISTS: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q } }", &uint_buf1, &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q}}", &uint_buf1, &p_charbuf1); | ||||
|             if (je == 2) { | ||||
|                 n = mympd_put_db_tag(mpd.buf, uint_buf1, "AlbumArtist", "", "", p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_GET_ARTISTALBUMS: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q, albumartist:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q, albumartist:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 3) { | ||||
|                 n = mympd_put_db_tag(mpd.buf, uint_buf1, "Album", "AlbumArtist", p_charbuf2, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -273,7 +271,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_PLAYLIST_RENAME: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { from:%Q, to:%Q } }", &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {from:%Q, to:%Q}}", &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 2) { | ||||
|                 mpd_run_rename(mpd.conn, p_charbuf1, p_charbuf2); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); | ||||
| @@ -282,14 +280,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break;             | ||||
|         case MPD_API_GET_PLAYLISTS: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q } }", &uint_buf1, &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q}}", &uint_buf1, &p_charbuf1); | ||||
|             if (je == 2) { | ||||
|                 n = mympd_put_playlists(mpd.buf, uint_buf1, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_GET_PLAYLIST_LIST: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri: %Q, offset:%u, filter:%Q } }", &p_charbuf1, &uint_buf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri: %Q, offset:%u, filter:%Q}}", &p_charbuf1, &uint_buf1, &p_charbuf2); | ||||
|             if (je == 3) { | ||||
|                 n = mympd_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); | ||||
|                 free(p_charbuf1); | ||||
| @@ -297,7 +295,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_ADD_TO_PLAYLIST: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q, uri:%Q } }", &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {plist:%Q, uri:%Q}}", &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 2) { | ||||
|                 mpd_run_playlist_add(mpd.conn, p_charbuf1, p_charbuf2); | ||||
|                 n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); | ||||
| @@ -306,7 +304,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_PLAYLIST_CLEAR: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_playlist_clear(mpd.conn, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -314,7 +312,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_RM_PLAYLIST_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q, track:%u } }", &p_charbuf1, &uint_buf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri:%Q, track:%u}}", &p_charbuf1, &uint_buf1); | ||||
|             if (je == 2) { | ||||
|                 mpd_run_playlist_delete(mpd.conn, p_charbuf1, uint_buf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -322,7 +320,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_GET_FILESYSTEM: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, filter:%Q, path:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q, path:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 3) { | ||||
|                 n = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -330,7 +328,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_ADD_TRACK_AFTER: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q, to:%d } }", &p_charbuf1, &int_buf); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri:%Q, to:%d}}", &p_charbuf1, &int_buf); | ||||
|             if (je == 2) { | ||||
|                 int_rc = mpd_run_add_id_to(mpd.conn, p_charbuf1, int_buf); | ||||
|                 if (int_rc > -1 )  | ||||
| @@ -339,7 +337,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_REPLACE_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri:%Q }}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_clear(mpd.conn); | ||||
|                 mpd_run_add(mpd.conn, p_charbuf1); | ||||
| @@ -349,7 +347,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_ADD_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_add(mpd.conn, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -357,7 +355,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_ADD_PLAY_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 int_buf = mpd_run_add_id(mpd.conn, p_charbuf1); | ||||
|                 if (int_buf != -1) | ||||
| @@ -367,7 +365,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_REPLACE_PLAYLIST: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {plist:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_clear(mpd.conn); | ||||
|                 mpd_run_load(mpd.conn, p_charbuf1); | ||||
| @@ -377,7 +375,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_ADD_PLAYLIST: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {plist:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_load(mpd.conn, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -385,7 +383,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_SAVE_QUEUE: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: {plist:%Q}}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_save(mpd.conn, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -393,7 +391,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_SEARCH_QUEUE: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, mpdtag:%Q, searchstr:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u, mpdtag:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 3) { | ||||
|                 n = mympd_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); | ||||
|                 free(p_charbuf1); | ||||
| @@ -401,7 +399,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break;             | ||||
|         case MPD_API_SEARCH_ADD: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { filter:%Q, searchstr:%Q } }", &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {filter:%Q, searchstr:%Q}}", &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 2) { | ||||
|                 n = mympd_search_add(mpd.buf, p_charbuf1, p_charbuf2); | ||||
|                 free(p_charbuf1); | ||||
| @@ -411,7 +409,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_SEARCH_ADD_PLIST: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { plist:%Q, filter:%Q, searchstr:%Q } }", &p_charbuf1, &p_charbuf2, &p_charbuf3); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {plist:%Q, filter:%Q, searchstr:%Q}}", &p_charbuf1, &p_charbuf2, &p_charbuf3); | ||||
|             if (je == 3) { | ||||
|                 n = mympd_search_add_plist(p_charbuf1, p_charbuf2, p_charbuf3); | ||||
|                 free(p_charbuf1); | ||||
| @@ -422,7 +420,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break;             | ||||
|         case MPD_API_SEARCH: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { offset:%u, mpdtag:%Q, searchstr:%Q } }", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {offset:%u, mpdtag:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 3) { | ||||
|                 n = mympd_search(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); | ||||
|                 free(p_charbuf1); | ||||
| @@ -434,7 +432,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
|             break; | ||||
|         case MPD_API_SEND_MESSAGE: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { channel:%Q, text:%Q } }", &p_charbuf1, &p_charbuf2); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: { channel:%Q, text:%Q}}", &p_charbuf1, &p_charbuf2); | ||||
|             if (je == 2) { | ||||
|                 mpd_run_send_message(mpd.conn, p_charbuf1, p_charbuf2); | ||||
|                 free(p_charbuf1); | ||||
| @@ -443,7 +441,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_RM_PLAYLIST: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { uri:%Q } }", &p_charbuf1); | ||||
|             je = json_scanf(msg.p, msg.len, "{data: { uri:%Q }}", &p_charbuf1); | ||||
|             if (je == 1) { | ||||
|                 mpd_run_rm(mpd.conn, p_charbuf1); | ||||
|                 free(p_charbuf1); | ||||
| @@ -454,13 +452,13 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             n = mympd_put_settings(mpd.buf); | ||||
|             break; | ||||
|         case MPD_API_GET_STATS: | ||||
|             n = mympd_get_stats(mpd.buf); | ||||
|             n = mympd_put_stats(mpd.buf); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stderr,"Error: %s\n", mpd_connection_get_error_message(mpd.conn)); | ||||
|         fprintf(stderr, "Error: %s\n", mpd_connection_get_error_message(mpd.conn)); | ||||
|         #endif | ||||
|         n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}",  | ||||
|             mpd_connection_get_error_message(mpd.conn)); | ||||
| @@ -475,13 +473,13 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|      | ||||
|     if (is_websocket(nc)) { | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stdout,"Send websocket response:\n %s\n",mpd.buf); | ||||
|         fprintf(stdout, "Send websocket response:\n %s\n",mpd.buf); | ||||
|         #endif | ||||
|         mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|     } | ||||
|     else { | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stdout,"Send http response:\n %s\n",mpd.buf); | ||||
|         fprintf(stdout, "Send http response:\n %s\n",mpd.buf); | ||||
|         #endif | ||||
|         mg_send_http_chunk(nc, mpd.buf, n); | ||||
|     } | ||||
| @@ -502,9 +500,9 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) { | ||||
|  | ||||
|     if (param) { | ||||
|         /* error message? */ | ||||
|         n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); | ||||
|         n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}",param); | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stdout,"Error in mpd_notify_callback: %s\n",param); | ||||
|         fprintf(stderr, "Error in mpd_notify_callback: %s\n",param); | ||||
|         #endif | ||||
|         mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|         return 0; | ||||
| @@ -516,31 +514,31 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) { | ||||
|     struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->user_data; | ||||
|  | ||||
|     if (mpd.conn_state != MPD_CONNECTED) { | ||||
|         n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}"); | ||||
|         n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stdout,"Notify: disconnected\n"); | ||||
|         fprintf(stdout, "Notify: disconnected\n"); | ||||
|         #endif | ||||
|         mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|     } | ||||
|     else { | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stdout,"Notify: %s\n",mpd.buf); | ||||
|         fprintf(stdout, "Notify: %s\n",mpd.buf); | ||||
|         #endif | ||||
|         mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); | ||||
|          | ||||
|         if (s->song_id != mpd.song_id) { | ||||
|             n=mympd_put_current_song(mpd.buf); | ||||
|             n = mympd_put_current_song(mpd.buf); | ||||
|             #ifdef DEBUG | ||||
|             fprintf(stdout,"Notify: %s\n",mpd.buf); | ||||
|             fprintf(stdout, "Notify: %s\n",mpd.buf); | ||||
|             #endif | ||||
|             mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|             s->song_id = mpd.song_id; | ||||
|         } | ||||
|          | ||||
|         if (s->queue_version != mpd.queue_version) { | ||||
|             n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); | ||||
|             #ifdef DEBUG | ||||
|             fprintf(stdout,"Notify: update_queue\n"); | ||||
|             fprintf(stdout, "Notify: update_queue\n"); | ||||
|             #endif | ||||
|             mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|             s->queue_version = mpd.queue_version; | ||||
| @@ -553,8 +551,8 @@ void mympd_poll(struct mg_mgr *s) { | ||||
|     switch (mpd.conn_state) { | ||||
|         case MPD_DISCONNECTED: | ||||
|             /* Try to connect */ | ||||
|             fprintf(stdout, "MPD Connecting to %s:%d\n", mpd.host, mpd.port); | ||||
|             mpd.conn = mpd_connection_new(mpd.host, mpd.port, 3000); | ||||
|             fprintf(stdout, "MPD Connecting to %s: %d\n", config.mpdhost, config.mpdport); | ||||
|             mpd.conn = mpd_connection_new(config.mpdhost, config.mpdport, 3000); | ||||
|             if (mpd.conn == NULL) { | ||||
|                 fprintf(stderr, "Out of memory."); | ||||
|                 mpd.conn_state = MPD_FAILURE; | ||||
| @@ -570,7 +568,7 @@ void mympd_poll(struct mg_mgr *s) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (mpd.password && !mpd_run_password(mpd.conn, mpd.password)) { | ||||
|             if (config.mpdpass && !mpd_run_password(mpd.conn, config.mpdpass)) { | ||||
|                 fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); | ||||
|                 for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { | ||||
|                     mympd_notify_callback(c, mpd_connection_get_error_message(mpd.conn)); | ||||
| @@ -619,9 +617,7 @@ char* mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { | ||||
| int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsigned *queue_version) { | ||||
|     struct mpd_status *status; | ||||
|     const struct mpd_audio_format *audioformat; | ||||
|     struct mpd_output *output; | ||||
|     int len; | ||||
|     int nr; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
|  | ||||
|     status = mpd_run_status(mpd.conn); | ||||
| @@ -656,31 +652,15 @@ int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsi | ||||
|         mpd_status_get_queue_version(status) | ||||
|     ); | ||||
|      | ||||
|     len += json_printf(&out, ",outputs: ["); | ||||
|  | ||||
|     mpd_send_outputs(mpd.conn); | ||||
|     nr=0; | ||||
|     while ((output = mpd_recv_output(mpd.conn)) != NULL) { | ||||
|         if (nr++) len += json_printf(&out, ","); | ||||
|         len += json_printf(&out, "{id: %d, state: %d}", | ||||
|             mpd_output_get_id(output),  | ||||
|             mpd_output_get_enabled(output) | ||||
|         ); | ||||
|         mpd_output_free(output); | ||||
|     } | ||||
|     if (!mpd_response_finish(mpd.conn)) { | ||||
|         fprintf(stderr, "MPD outputs: %s\n", mpd_connection_get_error_message(mpd.conn)); | ||||
|         mpd_connection_clear_error(mpd.conn); | ||||
|     } | ||||
|  | ||||
|     len += json_printf(&out, "]}}"); | ||||
|     len += json_printf(&out, "}}"); | ||||
|  | ||||
|     *current_song_id = mpd_status_get_song_id(status); | ||||
|     *next_song_id = mpd_status_get_next_song_id(status); | ||||
|     *queue_version = mpd_status_get_queue_version(status); | ||||
|     mpd_status_free(status); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -690,7 +670,8 @@ int mympd_put_welcome(char *buffer) { | ||||
|      | ||||
|     len = json_printf(&out, "{type: welcome, data: { version: %Q}}", MYMPD_VERSION); | ||||
|      | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -701,8 +682,8 @@ int mympd_put_settings(char *buffer) { | ||||
|     int je; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
|     struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 }; | ||||
|     if (access( mpd.statefile, F_OK ) != -1 ) { | ||||
|         char *content = json_fread(mpd.statefile); | ||||
|     if (access( config.statefile, F_OK ) != -1 ) { | ||||
|         char *content = json_fread(config.statefile); | ||||
|         je = json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b); | ||||
|         if (je != 2) { | ||||
|           state.a=0; | ||||
| @@ -741,9 +722,11 @@ int mympd_put_settings(char *buffer) { | ||||
|         mpd_status_get_random(status), | ||||
|         mpd_status_get_mixrampdb(status), | ||||
|         mpd_status_get_mixrampdelay(status), | ||||
|         mpd.host, mpd.port,  | ||||
|         mpd.password ? "true" : "false",  | ||||
|         streamport, coverimage, | ||||
|         config.mpdhost,  | ||||
|         config.mpdport,  | ||||
|         config.mpdpass ? "true" : "false",  | ||||
|         config.streamport,  | ||||
|         config.coverimage, | ||||
|         MAX_ELEMENTS_PER_PAGE, | ||||
|         replaygain, | ||||
|         state.a, | ||||
| @@ -751,26 +734,28 @@ int mympd_put_settings(char *buffer) { | ||||
|     ); | ||||
|     mpd_status_free(status); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
|  | ||||
| int mympd_put_outputnames(char *buffer) { | ||||
| int mympd_put_outputs(char *buffer) { | ||||
|     struct mpd_output *output; | ||||
|     int len; | ||||
|     int nr; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
|      | ||||
|     len = json_printf(&out,"{type: outputnames, data: { outputs: ["); | ||||
|     len = json_printf(&out,"{type: outputs, data: {outputs: ["); | ||||
|      | ||||
|     mpd_send_outputs(mpd.conn); | ||||
|     nr=0;     | ||||
|     while ((output = mpd_recv_output(mpd.conn)) != NULL) { | ||||
|         if (nr++) len += json_printf(&out, ","); | ||||
|         len += json_printf(&out,"{id: %d, name: %Q}", | ||||
|         len += json_printf(&out,"{id: %d, name: %Q, state: %d}", | ||||
|             mpd_output_get_id(output), | ||||
|             mpd_output_get_name(output) | ||||
|             mpd_output_get_name(output), | ||||
|             mpd_output_get_enabled(output) | ||||
|         ); | ||||
|         mpd_output_free(output); | ||||
|     } | ||||
| @@ -781,23 +766,24 @@ int mympd_put_outputnames(char *buffer) { | ||||
|  | ||||
|     len += json_printf(&out,"]}}"); | ||||
|      | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_get_cover(const char *uri, char *cover, int cover_len) { | ||||
|     char *path=strdup(uri); | ||||
|     char *path = strdup(uri); | ||||
|     int len; | ||||
|     if (strncasecmp("http:",path,5) == 0 ) { | ||||
|       len=snprintf(cover,cover_len,"/assets/coverimage-httpstream.png"); | ||||
|     if (strncasecmp("http:", path, 5) == 0 ) { | ||||
|       len=snprintf(cover,cover_len, "/assets/coverimage-httpstream.png"); | ||||
|     } | ||||
|     else { | ||||
|       dirname(path); | ||||
|       snprintf(cover,cover_len,"%s/library/%s/%s",SRC_PATH,path,coverimage); | ||||
|       snprintf(cover,cover_len,"%s/library/%s/%s", SRC_PATH, path, config.coverimage); | ||||
|       if ( access(cover, F_OK ) == -1 ) { | ||||
|         len = snprintf(cover,cover_len,"/assets/coverimage-notavailable.png"); | ||||
|         len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png"); | ||||
|       } else { | ||||
|         len = snprintf(cover,cover_len,"/library/%s/%s",path,coverimage); | ||||
|         len = snprintf(cover, cover_len, "/library/%s/%s", path, config.coverimage); | ||||
|       } | ||||
|     } | ||||
|     return len; | ||||
| @@ -819,7 +805,7 @@ int mympd_put_current_song(char *buffer) { | ||||
|  | ||||
|     len = json_printf(&out,"{type: song_change, data: { pos: %d, title: %Q, " | ||||
|         "artist: %Q, album: %Q, uri: %Q, currentsongid: %d, albumartist: %Q, " | ||||
|         "duration: %d, cover: %Q }}", | ||||
|         "duration: %d, cover: %Q}}", | ||||
|         mpd_song_get_pos(song), | ||||
|         mympd_get_tag(song, MPD_TAG_TITLE), | ||||
|         mympd_get_tag(song, MPD_TAG_ARTIST), | ||||
| @@ -834,7 +820,8 @@ int mympd_put_current_song(char *buffer) { | ||||
|     mpd_song_free(song); | ||||
|     mpd_response_finish(mpd.conn); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -866,7 +853,8 @@ int mympd_put_songdetails(char *buffer, char *uri) { | ||||
|     } | ||||
|     len += json_printf(&out, "}}"); | ||||
|      | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -893,7 +881,7 @@ int mympd_put_queue(char *buffer, unsigned int offset) { | ||||
|             entity_count ++; | ||||
|             if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|                 if (entities_returned ++) len += json_printf(&out,","); | ||||
|                 len += json_printf(&out, "{ id: %d, pos: %d, duration: %d, artist: %Q, album: %Q, title: %Q, uri: %Q }", | ||||
|                 len += json_printf(&out, "{id: %d, pos: %d, duration: %d, artist: %Q, album: %Q, title: %Q, uri: %Q }", | ||||
|                     mpd_song_get_id(song), | ||||
|                     mpd_song_get_pos(song), | ||||
|                     mpd_song_get_duration(song), | ||||
| @@ -915,7 +903,8 @@ int mympd_put_queue(char *buffer, unsigned int offset) { | ||||
|         mpd.queue_version | ||||
|     ); | ||||
|      | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE)  | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -931,7 +920,7 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter | ||||
|     if (!mpd_send_list_meta(mpd.conn, path)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); | ||||
|  | ||||
|     len = json_printf(&out, "{ type: browse, data: [ "); | ||||
|     len = json_printf(&out, "{type: browse, data: [ "); | ||||
|  | ||||
|     while((entity = mpd_recv_entity(mpd.conn)) != NULL) { | ||||
|         const struct mpd_song *song; | ||||
| @@ -997,7 +986,7 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter | ||||
|                         ( strncmp(filter,"0",1) == 0 && isalpha(*plName) == 0 ) | ||||
|                     ) { | ||||
|                         if (entities_returned ++) len += json_printf(&out,","); | ||||
|                         len += json_printf(&out, "{ type: plist, uri: %Q, name: %Q }", | ||||
|                         len += json_printf(&out, "{type: plist, uri: %Q, name: %Q }", | ||||
|                             entityName, | ||||
|                             plName | ||||
|                         ); | ||||
| @@ -1023,7 +1012,8 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter | ||||
|         filter | ||||
|     ); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1075,7 +1065,8 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char * | ||||
|         filter | ||||
|     ); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1087,9 +1078,8 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) { | ||||
|     char cover[500]; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
|  | ||||
|     if (mpd_search_db_songs(mpd.conn, true) == false) { | ||||
|     if (mpd_search_db_songs(mpd.conn, true) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); | ||||
|     } | ||||
|      | ||||
|     if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, albumartist) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); | ||||
| @@ -1107,7 +1097,7 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) { | ||||
|             if (entity_count <= MAX_ELEMENTS_PER_PAGE) { | ||||
|                 if (entities_returned ++) len += json_printf(&out, ", "); | ||||
|                 else mympd_get_cover(mpd_song_get_uri(song),cover,500); | ||||
|                 len += json_printf(&out, "{ type: song, uri: %Q, duration: %d, title: %Q, track: %Q }", | ||||
|                 len += json_printf(&out, "{ type: song, uri: %Q, duration: %d, title: %Q, track: %Q}", | ||||
|                     mpd_song_get_uri(song), | ||||
|                     mpd_song_get_duration(song), | ||||
|                     mympd_get_tag(song, MPD_TAG_TITLE), | ||||
| @@ -1117,7 +1107,7 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) { | ||||
|             mpd_song_free(song); | ||||
|         } | ||||
|          | ||||
|         len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, albumartist: %Q, album: %Q, cover: %Q }", | ||||
|         len += json_printf(&out, "], totalEntities: %d, returnedEntities: %d, albumartist: %Q, album: %Q, cover: %Q}", | ||||
|             entity_count, | ||||
|             entities_returned, | ||||
|             albumartist, | ||||
| @@ -1126,7 +1116,8 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) { | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1141,7 +1132,7 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { | ||||
|     if (!mpd_send_list_playlists(mpd.conn)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_lists_playlists"); | ||||
|  | ||||
|     len = json_printf(&out, "{ type: playlists, data: [ "); | ||||
|     len = json_printf(&out, "{type: playlists, data: [ "); | ||||
|  | ||||
|     while((pl = mpd_recv_playlist(mpd.conn)) != NULL) { | ||||
|         entity_count ++; | ||||
| @@ -1151,7 +1142,7 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { | ||||
|                     ( strncmp(filter,"0",1) == 0 && isalpha(*plpath) == 0 ) | ||||
|             ) { | ||||
|                 if (entities_returned ++) len += json_printf(&out, ", "); | ||||
|                 len += json_printf(&out, "{ type: plist, uri: %Q, name: %Q, last_modified: %d }", | ||||
|                 len += json_printf(&out, "{type: plist, uri: %Q, name: %Q, last_modified: %d}", | ||||
|                     plpath, | ||||
|                     plpath, | ||||
|                     mpd_playlist_get_last_modified(pl) | ||||
| @@ -1166,13 +1157,14 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { | ||||
|     if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_playlists"); | ||||
|          | ||||
|     len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d }", | ||||
|     len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d}", | ||||
|         entity_count, | ||||
|         offset, | ||||
|         entities_returned | ||||
|     ); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1187,7 +1179,7 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char * | ||||
|     if (!mpd_send_list_playlist_meta(mpd.conn, uri)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); | ||||
|  | ||||
|     len = json_printf(&out, "{ type: playlist_detail, data: [ "); | ||||
|     len = json_printf(&out, "{type: playlist_detail, data: [ "); | ||||
|  | ||||
|     while((entity = mpd_recv_entity(mpd.conn)) != NULL) { | ||||
|         const struct mpd_song *song; | ||||
| @@ -1199,7 +1191,7 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char * | ||||
|                ( strncmp(filter,"0",1) == 0 && isalpha(*entityName) == 0 ) | ||||
|             ) { | ||||
|                 if (entities_returned ++) len += json_printf(&out,","); | ||||
|                 len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q }", | ||||
|                 len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q}", | ||||
|                     mpd_song_get_uri(song), | ||||
|                     mympd_get_tag(song, MPD_TAG_ALBUM), | ||||
|                     mympd_get_tag(song, MPD_TAG_ARTIST), | ||||
| @@ -1214,7 +1206,7 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char * | ||||
|         mpd_entity_free(entity); | ||||
|     } | ||||
|  | ||||
|     len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, filter: %Q, uri: %Q }", | ||||
|     len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, filter: %Q, uri: %Q}", | ||||
|         entity_count, | ||||
|         offset, | ||||
|         entities_returned, | ||||
| @@ -1223,7 +1215,7 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char * | ||||
|     ); | ||||
|  | ||||
|     if (len > MAX_SIZE)  | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1249,13 +1241,13 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear | ||||
|     if (mpd_search_commit(mpd.conn) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_commit"); | ||||
|     else { | ||||
|         len = json_printf(&out, "{ type: search, data: [ "); | ||||
|         len = json_printf(&out, "{type: search, data: [ "); | ||||
|  | ||||
|         while((song = mpd_recv_song(mpd.conn)) != NULL) { | ||||
|             entity_count ++; | ||||
|             if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|                 if (entities_returned ++) len += json_printf(&out, ", "); | ||||
|                 len += json_printf(&out, "{ type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q }", | ||||
|                 len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q}", | ||||
|                     mpd_song_get_uri(song), | ||||
|                     mympd_get_tag(song, MPD_TAG_ALBUM), | ||||
|                     mympd_get_tag(song, MPD_TAG_ARTIST), | ||||
| @@ -1267,7 +1259,7 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear | ||||
|             mpd_song_free(song); | ||||
|         } | ||||
|  | ||||
|         len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, mpdtagtype: %Q }", | ||||
|         len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, mpdtagtype: %Q}", | ||||
|             entity_count, | ||||
|             offset, | ||||
|             entities_returned, | ||||
| @@ -1276,7 +1268,7 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear | ||||
|     } | ||||
|  | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1306,7 +1298,7 @@ int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr) { | ||||
|     } | ||||
|          | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1321,7 +1313,7 @@ int mympd_search_add_plist(char *plist, char *mpdtagtype, char *searchstr) { | ||||
|     }     | ||||
|  | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| @@ -1335,7 +1327,7 @@ int mympd_queue_crop(char *buffer) { | ||||
|     mpd_status_free(status); | ||||
|  | ||||
|     if (length < 0) { | ||||
|         len = json_printf(&out, "{ type: error, data: %Q }", "A playlist longer than 1 song in length is required to crop."); | ||||
|         len = json_printf(&out, "{type: error, data: %Q }", "A playlist longer than 1 song in length is required to crop."); | ||||
|     } | ||||
|     else if (mpd_status_get_state(status) == MPD_STATE_PLAY || mpd_status_get_state(status) == MPD_STATE_PAUSE) { | ||||
|         playing_song_pos++; | ||||
| @@ -1344,13 +1336,13 @@ int mympd_queue_crop(char *buffer) { | ||||
|         playing_song_pos--; | ||||
|         if (playing_song_pos > 0 ) | ||||
|             mpd_run_delete_range(mpd.conn, 0, playing_song_pos--);             | ||||
|         len = json_printf(&out, "{ type: result, data: ok }"); | ||||
|         len = json_printf(&out, "{type: result, data: ok}"); | ||||
|     } else { | ||||
|         len = json_printf(&out, "{ type: error, data: %Q }", "You need to be playing to crop the playlist"); | ||||
|         len = json_printf(&out, "{type: error, data: %Q}", "You need to be playing to crop the playlist"); | ||||
|     } | ||||
|      | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len;     | ||||
| } | ||||
|  | ||||
| @@ -1383,7 +1375,7 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char | ||||
|           entity_count ++; | ||||
|           if (entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|             if (entities_returned ++) len += json_printf(&out, ", "); | ||||
|             len += json_printf(&out, "{ type: song, id: %d, pos: %d, album: %Q, artist: %Q, duration: %d, title: %Q }", | ||||
|             len += json_printf(&out, "{type: song, id: %d, pos: %d, album: %Q, artist: %Q, duration: %d, title: %Q}", | ||||
|                 mpd_song_get_id(song), | ||||
|                 mpd_song_get_pos(song), | ||||
|                 mympd_get_tag(song, MPD_TAG_ALBUM), | ||||
| @@ -1395,7 +1387,7 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char | ||||
|           } | ||||
|         } | ||||
|          | ||||
|         len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, mpdtagtype: %Q }", | ||||
|         len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, mpdtagtype: %Q}", | ||||
|             entity_count, | ||||
|             offset, | ||||
|             entities_returned, | ||||
| @@ -1403,11 +1395,12 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE)  | ||||
|         fprintf(stderr, "Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_get_stats(char *buffer) { | ||||
| int mympd_put_stats(char *buffer) { | ||||
|     struct mpd_stats *stats = mpd_run_stats(mpd.conn); | ||||
|     const unsigned *version = mpd_connection_get_server_version(mpd.conn); | ||||
|     char mpd_version[20]; | ||||
| @@ -1417,9 +1410,9 @@ int mympd_get_stats(char *buffer) { | ||||
|     snprintf(mpd_version,20,"%i.%i.%i", version[0], version[1], version[2]); | ||||
|      | ||||
|     if (stats == NULL) | ||||
|         RETURN_ERROR_AND_RECOVER("mympd_get_stats"); | ||||
|     len = json_printf(&out, "{ type: mpdstats, data: { artists: %d, albums: %d, songs: %d, " | ||||
|         "playtime: %d, uptime: %d, dbupdated: %d, dbplaytime: %d, mympd_version: %Q, mpd_version: %Q }}", | ||||
|         RETURN_ERROR_AND_RECOVER("mympd_put_stats"); | ||||
|     len = json_printf(&out, "{type: mpdstats, data: { artists: %d, albums: %d, songs: %d, " | ||||
|         "playtime: %d, uptime: %d, dbupdated: %d, dbplaytime: %d, mympd_version: %Q, mpd_version: %Q}}", | ||||
|         mpd_stats_get_number_of_artists(stats), | ||||
|         mpd_stats_get_number_of_albums(stats), | ||||
|         mpd_stats_get_number_of_songs(stats), | ||||
| @@ -1432,7 +1425,8 @@ int mympd_get_stats(char *buffer) { | ||||
|     ); | ||||
|     mpd_stats_free(stats); | ||||
|  | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     if (len > MAX_SIZE) | ||||
|         fprintf(stderr,"Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -35,7 +35,7 @@ | ||||
|     if (!mpd_connection_clear_error(mpd.conn)) \ | ||||
|         mpd.conn_state = MPD_FAILURE; \ | ||||
|     return len; \ | ||||
| } while(0) | ||||
| } while (0) | ||||
|  | ||||
|  | ||||
| #define MAX_SIZE 1024 * 100 | ||||
| @@ -77,7 +77,7 @@ | ||||
|     X(MPD_API_SET_NEXT) \ | ||||
|     X(MPD_API_SET_PREV) \ | ||||
|     X(MPD_API_UPDATE_DB) \ | ||||
|     X(MPD_API_GET_OUTPUTNAMES) \ | ||||
|     X(MPD_API_GET_OUTPUTS) \ | ||||
|     X(MPD_API_TOGGLE_OUTPUT) \ | ||||
|     X(MPD_API_SEND_SHUFFLE) \ | ||||
|     X(MPD_API_GET_STATS) \ | ||||
| @@ -107,11 +107,6 @@ enum mpd_conn_states { | ||||
| }; | ||||
|  | ||||
| struct t_mpd { | ||||
|     int port; | ||||
|     char host[128]; | ||||
|     char *password; | ||||
|     char *statefile; | ||||
|  | ||||
|     struct mpd_connection *conn; | ||||
|     enum mpd_conn_states conn_state; | ||||
|  | ||||
| @@ -124,8 +119,22 @@ struct t_mpd { | ||||
|     unsigned queue_version; | ||||
| } mpd; | ||||
|  | ||||
| int streamport; | ||||
| char coverimage[40]; | ||||
| typedef struct { | ||||
|    int mpdport; | ||||
|    const char* mpdhost; | ||||
|    const char* mpdpass; | ||||
|    const char* webport; | ||||
|    bool ssl; | ||||
|    const char* sslport; | ||||
|    const char* sslcert; | ||||
|    const char* sslkey; | ||||
|    const char* user; | ||||
|    int streamport; | ||||
|    const char* coverimage; | ||||
|    const char* statefile; | ||||
| } configuration; | ||||
|  | ||||
| configuration config; | ||||
|  | ||||
| static int is_websocket(const struct mg_connection *nc) { | ||||
|   return nc->flags & MG_F_IS_WEBSOCKET; | ||||
| @@ -141,7 +150,7 @@ void mympd_poll(struct mg_mgr *s); | ||||
| void callback_mympd(struct mg_connection *nc, const struct mg_str msg); | ||||
| int mympd_close_handler(struct mg_connection *c); | ||||
| int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version); | ||||
| int mympd_put_outputnames(char *buffer); | ||||
| int mympd_put_outputs(char *buffer); | ||||
| int mympd_put_current_song(char *buffer); | ||||
| int mympd_put_queue(char *buffer, unsigned int offset); | ||||
| int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter); | ||||
| @@ -150,7 +159,7 @@ int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr); | ||||
| int mympd_search_add_plist(char *plist, char *mpdtagtype, char *searchstr); | ||||
| int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); | ||||
| int mympd_put_welcome(char *buffer); | ||||
| int mympd_get_stats(char *buffer); | ||||
| int mympd_put_stats(char *buffer); | ||||
| int mympd_put_settings(char *buffer); | ||||
| int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter); | ||||
| int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album); | ||||
|   | ||||
							
								
								
									
										219
									
								
								src/mympd.c
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								src/mympd.c
									
									
									
									
									
								
							| @@ -26,35 +26,34 @@ | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <getopt.h> | ||||
| #include <sys/time.h> | ||||
| #include <pwd.h> | ||||
|  | ||||
| #include "../dist/src/mongoose/mongoose.h" | ||||
| #include "../dist/src/inih/ini.h" | ||||
| #include "mpd_client.h" | ||||
| #include "config.h" | ||||
|  | ||||
| extern char *optarg; | ||||
| static sig_atomic_t s_signal_received = 0; | ||||
| static struct mg_serve_http_opts s_http_server_opts; | ||||
| char s_redirect[250]; | ||||
|  | ||||
| static void signal_handler(int sig_num) { | ||||
|   signal(sig_num, signal_handler);  // Reinstantiate signal handler | ||||
|   s_signal_received = sig_num; | ||||
|     signal(sig_num, signal_handler);  // Reinstantiate signal handler | ||||
|     s_signal_received = sig_num; | ||||
| } | ||||
|  | ||||
| static void handle_api(struct mg_connection *nc, struct http_message *hm) { | ||||
|     if (!is_websocket(nc)) { | ||||
|     if (!is_websocket(nc)) | ||||
|         mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n"); | ||||
|     } | ||||
|  | ||||
|     char buf[1000] = {0}; | ||||
|     memcpy(buf, hm->body.p,sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len); | ||||
|     struct mg_str d = {buf, strlen(buf)}; | ||||
|     callback_mympd(nc, d); | ||||
|     if (!is_websocket(nc)) { | ||||
|  | ||||
|     if (!is_websocket(nc)) | ||||
|         mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | ||||
| @@ -63,14 +62,14 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | ||||
|              #ifdef DEBUG | ||||
|              fprintf(stdout,"New Websocket connection\n"); | ||||
|              #endif | ||||
|              struct mg_str d = {(char *) "{\"cmd\":\"MPD_API_WELCOME\"}", 25 }; | ||||
|              struct mg_str d = {(char *) "{\"cmd\": \"MPD_API_WELCOME\"}", 25 }; | ||||
|              callback_mympd(nc, d); | ||||
|              break; | ||||
|         } | ||||
|         case MG_EV_HTTP_REQUEST: { | ||||
|             struct http_message *hm = (struct http_message *) ev_data; | ||||
|             #ifdef DEBUG | ||||
|             printf("HTTP request: %.*s\n",hm->uri.len,hm->uri.p); | ||||
|             printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p); | ||||
|             #endif | ||||
|             if (mg_vcmp(&hm->uri, "/api") == 0) { | ||||
|               handle_api(nc, hm); | ||||
| @@ -107,114 +106,85 @@ static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int inihandler(void* user, const char* section, const char* name, const char* value) { | ||||
|     configuration* p_config = (configuration*)user; | ||||
|  | ||||
|     #define MATCH(n) strcmp(name, n) == 0 | ||||
|     if (MATCH("mpdhost")) | ||||
|         p_config->mpdhost = strdup(value); | ||||
|     else if (MATCH("mpdhost")) | ||||
|         p_config->mpdhost = strdup(value); | ||||
|     else if (MATCH("mpdport")) | ||||
|         p_config->mpdport = atoi(value); | ||||
|     else if (MATCH("mpdhost")) | ||||
|         p_config->mpdhost = strdup(value); | ||||
|     else if (MATCH("mpdpass")) | ||||
|         p_config->mpdpass = strdup(value); | ||||
|     else if (MATCH("webport")) | ||||
|         p_config->webport = strdup(value); | ||||
|     else if (MATCH("ssl")) | ||||
|         if (strcmp(value, "true") == 0) | ||||
|             p_config->ssl = true; | ||||
|         else | ||||
|             p_config->ssl = false; | ||||
|     else if (MATCH("sslcert")) | ||||
|         p_config->sslcert = strdup(value); | ||||
|     else if (MATCH("sslkey")) | ||||
|         p_config->sslkey = strdup(value); | ||||
|     else if (MATCH("user")) | ||||
|         p_config->user = strdup(value); | ||||
|     else if (MATCH("streamport")) | ||||
|         p_config->streamport = atoi(value); | ||||
|     else if (MATCH("coverimage")) | ||||
|         p_config->coverimage = strdup(value); | ||||
|     else if (MATCH("statefile")) | ||||
|         p_config->statefile = strdup(value); | ||||
|     else | ||||
|         return 0;  /* unknown section/name, error */ | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
|     int n, option_index = 0; | ||||
|     struct mg_mgr mgr; | ||||
|     struct mg_connection *nc; | ||||
|     struct mg_connection *nc_http; | ||||
|     unsigned int current_timer = 0, last_timer = 0; | ||||
|     char *run_as_user = NULL; | ||||
|     char *webport = "80"; | ||||
|     char *sslport = "443"; | ||||
|     mpd.port = 6600; | ||||
|     strcpy(mpd.host, "127.0.0.1"); | ||||
|     streamport = 8000; | ||||
|     strcpy(coverimage, "folder.jpg"); | ||||
|     mpd.statefile = "/var/lib/mympd/mympd.state"; | ||||
|     struct mg_bind_opts bind_opts; | ||||
|     const char *err; | ||||
|     bool ssl = false; | ||||
|     char *s_ssl_cert = "/etc/mympd/ssl/server.pem"; | ||||
|     char *s_ssl_key = "/etc/mympd/ssl/server.key"; | ||||
|      | ||||
|     char hostname[1024]; | ||||
|     hostname[1023] = '\0'; | ||||
|     gethostname(hostname, 1023); | ||||
|     gethostname(hostname, 1023);     | ||||
|  | ||||
|     static struct option long_options[] = { | ||||
|         {"mpdhost",      required_argument, 0, 'h'}, | ||||
|         {"mpdport",      required_argument, 0, 'p'}, | ||||
|         {"mpdpass",      required_argument, 0, 'm'},         | ||||
|         {"webport",      required_argument, 0, 'w'}, | ||||
|         {"ssl",		 no_argument,	    0, 'S'}, | ||||
|         {"sslport",	 required_argument, 0, 'W'}, | ||||
|         {"sslcert",	 required_argument, 0, 'C'}, | ||||
|         {"sslkey",	 required_argument, 0, 'K'}, | ||||
|         {"user",         required_argument, 0, 'u'}, | ||||
|         {"streamport",	 required_argument, 0, 's'}, | ||||
|         {"coverimage",	 required_argument, 0, 'i'}, | ||||
|         {"statefile",	 required_argument, 0, 't'}, | ||||
|         {"version",      no_argument,       0, 'v'}, | ||||
|         {"help",         no_argument,       0,  0 }, | ||||
|         {0,              0,                 0,  0 } | ||||
|     }; | ||||
|  | ||||
|     while((n = getopt_long(argc, argv, "h:p:w:SW:C:K:u:vm:s:i:t:", | ||||
|                 long_options, &option_index)) != -1) { | ||||
|         switch (n) { | ||||
|             case 't': | ||||
|                 mpd.statefile = strdup(optarg); | ||||
|                 break; | ||||
|             case 'h': | ||||
|                 strncpy(mpd.host, optarg, sizeof(mpd.host)); | ||||
|                 break; | ||||
|             case 'p': | ||||
|                 mpd.port = atoi(optarg); | ||||
|                 break; | ||||
|             case 'w': | ||||
|                 webport = strdup(optarg); | ||||
|                 break; | ||||
|             case 'S': | ||||
|                 ssl = true; | ||||
|                 break; | ||||
|             case 'W': | ||||
|                 sslport = strdup(optarg); | ||||
|                 break; | ||||
|             case 'C': | ||||
|                 s_ssl_cert = strdup(optarg); | ||||
|                 break;                 | ||||
|             case 'K': | ||||
|                 s_ssl_key = strdup(optarg); | ||||
|                 break; | ||||
|             case 'u': | ||||
|                 run_as_user = strdup(optarg); | ||||
|                 break; | ||||
|             case 'm': | ||||
|                 if (strlen(optarg) > 0) | ||||
|                     mpd.password = strdup(optarg); | ||||
|                 break; | ||||
|             case 's': | ||||
|                 streamport = atoi(optarg); | ||||
|                 break; | ||||
|             case 'i': | ||||
|                 strncpy(coverimage, optarg, sizeof(coverimage)); | ||||
|                 break; | ||||
|             case 'v': | ||||
|                 fprintf(stdout, "myMPD  %d.%d.%d\n" | ||||
|                         "Copyright (C) 2018 Juergen Mang <mail@jcgames.de>\n" | ||||
|                         "Built " __DATE__ " "__TIME__"\n", | ||||
|                         MYMPD_VERSION_MAJOR, MYMPD_VERSION_MINOR, MYMPD_VERSION_PATCH); | ||||
|                 return EXIT_SUCCESS; | ||||
|                 break; | ||||
|             default: | ||||
|                 fprintf(stderr, "Usage: %s [OPTION]...\n\n" | ||||
|                         " -h, --host <host>\t\tconnect to mpd at host [localhost]\n" | ||||
|                         " -p, --port <port>\t\tconnect to mpd at port [6600]\n" | ||||
|                         " -w, --webport [ip:]<port>\tlisten interface/port for webserver [80]\n" | ||||
|                         " -S, --ssl\tenable ssl\n" | ||||
|                         " -W, --sslport [ip:]<port>\tlisten interface/port for ssl webserver [443]\n" | ||||
|                         " -C, --sslcert <filename>\tfilename for ssl certificate [/etc/mympd/ssl/server.pem]\n" | ||||
|                         " -K, --sslkey <filename>\tfilename for ssl key [/etc/mympd/ssl/server.key]\n" | ||||
|                         " -u, --user <username>\t\tdrop priviliges to user after socket bind\n" | ||||
|                         " -v, --version\t\t\tget version\n" | ||||
|                         " -m, --mpdpass <password>\tspecifies the password to use when connecting to mpd\n" | ||||
|                         " -s, --streamport <port>\tconnect to mpd http stream at port [8000]\n" | ||||
|                         " -i, --coverimage <filename>\tfilename for coverimage [folder.jpg]\n" | ||||
|                         " -t, --statefile <filename>\tfilename for mympd state [/var/lib/mympd/mympd.state]\n" | ||||
|                         " --help\t\t\t\tthis help\n" | ||||
|                         , argv[0]); | ||||
|                 return EXIT_FAILURE; | ||||
|     //defaults | ||||
|     config.mpdhost = "127.0.0.1"; | ||||
|     config.mpdport = 6600; | ||||
|     config.mpdpass = NULL; | ||||
|     config.webport = "80"; | ||||
|     config.ssl = true; | ||||
|     config.sslport = "443"; | ||||
|     config.sslcert = "/etc/mympd/ssl/server.pem"; | ||||
|     config.sslkey = "/etc/mympd/ssl/server.key"; | ||||
|     config.user = "nobody"; | ||||
|     config.streamport = 8000; | ||||
|     config.coverimage = "folder.jpg"; | ||||
|     config.statefile = "/var/lib/mympd/mympd.state"; | ||||
|      | ||||
|     if (argc == 2) { | ||||
|         if (ini_parse(argv[1], inihandler, &config) < 0) { | ||||
|             printf("Can't load '%s'\n", argv[1]); | ||||
|             return EXIT_FAILURE; | ||||
|         } | ||||
|  | ||||
|     }  | ||||
|     else { | ||||
|         fprintf(stdout, "myMPD  %s\n" | ||||
|                         "Copyright (C) 2018 Juergen Mang <mail@jcgames.de>\n" | ||||
|                         "https://github.com/jcorporation/myMPD\n" | ||||
|                         "Built " __DATE__ " "__TIME__"\n\n", | ||||
|                         MYMPD_VERSION); | ||||
|         printf("Usage: %s /path/to/mympd.conf\n", argv[0]); | ||||
|         return EXIT_FAILURE;     | ||||
|     } | ||||
|  | ||||
|     signal(SIGTERM, signal_handler); | ||||
| @@ -224,42 +194,45 @@ int main(int argc, char **argv) { | ||||
|      | ||||
|     mg_mgr_init(&mgr, NULL); | ||||
|  | ||||
|     if (ssl == true) { | ||||
|         snprintf(s_redirect, 200, "https://%s:%s/", hostname, sslport); | ||||
|         nc_http = mg_bind(&mgr, webport, ev_handler_http); | ||||
|     if (config.ssl == true) { | ||||
|         snprintf(s_redirect, 200, "https://%s:%s/", hostname, config.sslport); | ||||
|         nc_http = mg_bind(&mgr, config.webport, ev_handler_http); | ||||
|         if (nc_http == NULL) { | ||||
|            fprintf(stderr, "Error starting server on port %s\n", webport ); | ||||
|            fprintf(stderr, "Error starting server on port %s\n", config.webport ); | ||||
|            return EXIT_FAILURE; | ||||
|         } | ||||
|         memset(&bind_opts, 0, sizeof(bind_opts)); | ||||
|         bind_opts.ssl_cert = s_ssl_cert; | ||||
|         bind_opts.ssl_key = s_ssl_key; | ||||
|         bind_opts.ssl_cert = config.sslcert; | ||||
|         bind_opts.ssl_key = config.sslkey; | ||||
|         bind_opts.error_string = &err; | ||||
|         nc = mg_bind_opt(&mgr, sslport, ev_handler, bind_opts); | ||||
|         nc = mg_bind_opt(&mgr, config.sslport, ev_handler, bind_opts); | ||||
|         if (nc == NULL) { | ||||
|             fprintf(stderr, "Error starting server on port %s: %s\n", sslport, err); | ||||
|             fprintf(stderr, "Error starting server on port %s: %s\n", config.sslport, err); | ||||
|             return EXIT_FAILURE; | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         nc = mg_bind(&mgr, webport, ev_handler); | ||||
|         nc = mg_bind(&mgr, config.webport, ev_handler); | ||||
|         if (nc == NULL) { | ||||
|            fprintf(stderr, "Error starting server on port %s\n", webport ); | ||||
|            fprintf(stderr, "Error starting server on port %s\n", config.webport ); | ||||
|            return EXIT_FAILURE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (run_as_user != NULL) { | ||||
|     if (config.user != NULL) { | ||||
|         printf("Droping privileges\n"); | ||||
|         struct passwd *pw; | ||||
|         if ((pw = getpwnam(run_as_user)) == NULL) { | ||||
|         if ((pw = getpwnam(config.user)) == NULL) { | ||||
|             printf("Unknown user\n"); | ||||
|             mg_mgr_free(&mgr); | ||||
|             return EXIT_FAILURE; | ||||
|         } else if (setgid(pw->pw_gid) != 0) { | ||||
|             printf("setgid() failed\n"); | ||||
|             mg_mgr_free(&mgr); | ||||
|             return EXIT_FAILURE; | ||||
|         } else if (setuid(pw->pw_uid) != 0) { | ||||
|             printf("setuid() failed\n"); | ||||
|             mg_mgr_free(&mgr); | ||||
|             return EXIT_FAILURE; | ||||
|         } | ||||
|     } | ||||
| @@ -270,16 +243,16 @@ int main(int argc, char **argv) { | ||||
|       return EXIT_FAILURE; | ||||
|     } | ||||
|      | ||||
|     if (ssl == true) | ||||
|     if (config.ssl == true) | ||||
|         mg_set_protocol_http_websocket(nc_http); | ||||
|          | ||||
|     mg_set_protocol_http_websocket(nc); | ||||
|     s_http_server_opts.document_root = SRC_PATH; | ||||
|     s_http_server_opts.enable_directory_listing = "no"; | ||||
|  | ||||
|     printf("myMPD started on http port %s\n", webport); | ||||
|     if (ssl == true) | ||||
|         printf("myMPD started on ssl port %s\n", sslport); | ||||
|     printf("myMPD started on http port %s\n", config.webport); | ||||
|     if (config.ssl == true) | ||||
|         printf("myMPD started on ssl port %s\n", config.sslport); | ||||
|          | ||||
|     while (s_signal_received == 0) { | ||||
|         mg_mgr_poll(&mgr, 200); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jürgen Mang
					Jürgen Mang