mirror of
https://github.com/SuperBFG7/ympd
synced 2024-11-05 22:36:16 +00:00
commit
51e54e7bdc
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user