mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-31 05:43:01 +00:00 
			
		
		
		
	| @@ -3,8 +3,8 @@ 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 "1") | ||||
| set(CPACK_PACKAGE_VERSION_PATCH "1") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "2") | ||||
| set(CPACK_PACKAGE_VERSION_PATCH "0") | ||||
|  | ||||
| if(CMAKE_BUILD_TYPE MATCHES RELEASE) | ||||
|     set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs") | ||||
| @@ -12,7 +12,6 @@ if(CMAKE_BUILD_TYPE MATCHES RELEASE) | ||||
| else() | ||||
|     set(ASSETS_PATH "${PROJECT_SOURCE_DIR}/htdocs") | ||||
|     set(DEBUG "ON") | ||||
|     set(CS_NDEBUG "ON") | ||||
| endif() | ||||
|  | ||||
| find_package(LibMPDClient REQUIRED) | ||||
| @@ -23,9 +22,13 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}	${LIBMPDCLIENT_I | ||||
|  | ||||
| include(CheckCSourceCompiles) | ||||
|  | ||||
| set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D MG_DISABLE_SSL -D MG_ENABLE_IPV6 -D MG_DISABLE_MQTT -D MG_DISABLE_MQTT_BROKER -D MG_DISABLE_DNS_SERVER -D MG_DISABLE_COAP -D MG_DISABLE_HTTP_CGI -D MG_DISABLE_HTTP_SSI -D MG_DISABLE_HTTP_WEBDAV") | ||||
| set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D MG_ENABLE_SSL -D MG_ENABLE_IPV6 -D MG_DISABLE_MQTT -D MG_DISABLE_MQTT_BROKER -D MG_DISABLE_DNS_SERVER -D MG_DISABLE_COAP -D MG_DISABLE_HTTP_CGI -D MG_DISABLE_HTTP_SSI -D MG_DISABLE_HTTP_WEBDAV") | ||||
| set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") | ||||
|  | ||||
| find_package(OpenSSL REQUIRED) | ||||
| include_directories(${OPENSSL_INCLUDE_DIR}) | ||||
| set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL) | ||||
|  | ||||
| set(SOURCES | ||||
|     src/mympd.c | ||||
|     src/mpd_client.c | ||||
| @@ -34,12 +37,14 @@ set(SOURCES | ||||
| ) | ||||
|  | ||||
| add_executable(mympd ${SOURCES}) | ||||
| target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) | ||||
| target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}) | ||||
|  | ||||
| install(TARGETS mympd DESTINATION bin) | ||||
| install(FILES mympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) | ||||
| install(FILES contrib/mympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) | ||||
| install(FILES htdocs/mympd.webmanifest DESTINATION share/${PROJECT_NAME}/htdocs/) | ||||
| install(FILES htdocs/index.html DESTINATION share/${PROJECT_NAME}/htdocs/) | ||||
| install(FILES htdocs/player.html DESTINATION share/${PROJECT_NAME}/htdocs/) | ||||
| install(FILES htdocs/sw.min.js DESTINATION share/${PROJECT_NAME}/htdocs/) | ||||
| install(FILES htdocs/js/player.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) | ||||
| install(FILES htdocs/js/bootstrap-native-v4.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) | ||||
| install(FILES htdocs/js/mpd.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/) | ||||
| @@ -47,3 +52,4 @@ install(FILES htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME}/htd | ||||
| install(FILES 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}/) | ||||
|   | ||||
							
								
								
									
										50
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,18 +1,35 @@ | ||||
| myMPD | ||||
| ==== | ||||
| ===== | ||||
|  | ||||
| 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. | ||||
| myMPD is a fork of ympd (https://github.com/notandy/ympd). | ||||
|  | ||||
| This fork provides a reworked ui based on Bootstrap 4 and a modernized backend. | ||||
|  | ||||
| Design principles: | ||||
|  - Keep it small and simple | ||||
|  - Uses only mpd as source of truth | ||||
|  - Mobile first UI | ||||
|  - Keep security in mind | ||||
|  | ||||
| Featurelist: | ||||
|  - Browse mpd database by artist | ||||
|  - Browse filesystem and playlists | ||||
|  - Queue management | ||||
|  - Advanced search | ||||
|  - Progressiv Web App enabled | ||||
|  - Local coverart support | ||||
|  | ||||
| myMPD is work in progress. Bugreportes and feature requests are very welcome. Please use the issues function from github (https://github.com/jcorporation/myMPD/issues). | ||||
| Planed functions are managed in a github project (https://github.com/jcorporation/myMPD/projects/1). | ||||
|  | ||||
|  | ||||
|  | ||||
| UI Components | ||||
| ------------- | ||||
|  - Bootstrap 4: https://getbootstrap.com/ | ||||
|  - Material Design Icons: https://material.io/tools/icons/?style=baseline | ||||
|  - Material Design Icons: https://material.io/tools/icons/ | ||||
|  - Bootstrap Native: http://thednp.github.io/bootstrap.native/ | ||||
|  | ||||
| Backend | ||||
| @@ -24,32 +41,45 @@ Dependencies | ||||
| ------------ | ||||
|  - libmpdclient 2: http://www.musicpd.org/libs/libmpdclient/ | ||||
|  - cmake 2.6: http://cmake.org/ | ||||
|  - OpenSSL: https://www.openssl.org/ | ||||
|  | ||||
| Unix Build Instructions | ||||
| ----------------------- | ||||
|  | ||||
| 1. install dependencies. cmake and libmpdclient (dev) are available from all major distributions. | ||||
| 2. build and install it ```cd /path/to/src; ./mkrelease.sh``` | ||||
| 3. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories | ||||
| 4. Configure your mpd with http stream output to use the local player | ||||
| 1. Install dependencies: cmake, libmpdclient (dev) and OpenSSL (dev) are available from all major distributions. | ||||
| 2. Build and install: ```cd /path/to/src; ./mkrelease.sh```. | ||||
| 3. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories (mkrelease.sh tries to do this for you). | ||||
| 4. Configure your mpd with http stream output to use the local player. | ||||
|  | ||||
| Run flags | ||||
| --------- | ||||
| ``` | ||||
| Usage: ./mympd [OPTION]... | ||||
|  | ||||
|  -h, --host <host>             connect to mpd at host [localhost] | ||||
|  -p, --port <port>             connect to mpd at port [6600] | ||||
|  -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 | ||||
|  -m, --mpdpass <password>      specifies the password to use when connecting to mpd | ||||
|  -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 | ||||
| ``` | ||||
|  | ||||
| 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/```). | ||||
| 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. | ||||
|  | ||||
| Copyright | ||||
| --------- | ||||
| ympd: 2013-2014 <andy@ndyk.de> | ||||
|   | ||||
							
								
								
									
										100
									
								
								contrib/crcert.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										100
									
								
								contrib/crcert.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| if [ -d /etc/mympd/ssl ] | ||||
| then | ||||
|   echo "SSL directory exists, to recreate certificates: \"rm -r /etc/mympd/ssl\"" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| mkdir -p /etc/mympd/ssl/ca/certs | ||||
| cd /etc/mympd/ssl/ca | ||||
|  | ||||
| echo '01' > serial | ||||
| touch index.txt | ||||
| touch index.txt.attr | ||||
|  | ||||
| echo "Creating ca" | ||||
|  | ||||
| cat > ca.cnf << EOL | ||||
| [req] | ||||
| distinguished_name = root_ca_distinguished_name | ||||
| x509_extensions = root_ca_extensions | ||||
| prompt = no | ||||
|  | ||||
| [root_ca_distinguished_name] | ||||
| O = myMPD | ||||
| CN = myMPD_CA | ||||
|  | ||||
| [root_ca_extensions] | ||||
| basicConstraints = CA:true | ||||
|  | ||||
| [ ca ] | ||||
| default_ca = mympd_ca | ||||
|  | ||||
| [mympd_ca] | ||||
| dir = /etc/mympd/ssl/ca | ||||
| database = /etc/mympd/ssl/ca/index.txt | ||||
| new_certs_dir = /etc/mympd/ssl/ca/certs/ | ||||
| serial = /etc/mympd/ssl/ca/serial | ||||
| copy_extensions = copy | ||||
| policy = local_ca_policy | ||||
| x509_extensions = local_ca_extensions | ||||
| default_md = sha256 | ||||
|  | ||||
| [ local_ca_policy ] | ||||
| commonName = supplied | ||||
| organizationName = supplied | ||||
|  | ||||
| [ local_ca_extensions ] | ||||
| basicConstraints = CA:false | ||||
|  | ||||
| EOL | ||||
|  | ||||
| openssl req -new -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes -config ca.cnf \ | ||||
| 	-keyout ca.key -out ca.pem | ||||
|  | ||||
| HOSTNAME=$(hostname) | ||||
| FQDN=$(hostname -f) | ||||
| IP=$(getent hosts $HOSTNAME | awk {'print $1'}) | ||||
|  | ||||
| cd /etc/mympd/ssl | ||||
| echo "Creating cert:" | ||||
| echo "\t$HOSTNAME" | ||||
| echo "\t$FQDN" | ||||
| echo "\t$IP" | ||||
|  | ||||
| cat > req.cnf << EOL | ||||
| [req] | ||||
| distinguished_name = req_distinguished_name | ||||
| req_extensions = v3_req | ||||
| prompt = no | ||||
|  | ||||
| [req_distinguished_name] | ||||
| O = myMPD | ||||
| CN = $FQDN | ||||
|  | ||||
| [v3_req] | ||||
| basicConstraints = CA:FALSE | ||||
| keyUsage = digitalSignature, keyEncipherment, dataEncipherment | ||||
| extendedKeyUsage = serverAuth | ||||
| subjectAltName = @alt_names | ||||
|  | ||||
| [alt_names] | ||||
| DNS.1 = $HOSTNAME | ||||
| DNS.2 = $FQDN | ||||
| DNS.3 = localhost | ||||
| IP.1 = $IP | ||||
| IP.2 = 127.0.0.1 | ||||
| EOL | ||||
|  | ||||
| openssl req -new -sha256 -newkey rsa:2048 -days 3650 -nodes -config req.cnf \ | ||||
| 	-keyout server.key -out server.csr \ | ||||
| 	-extensions v3_req | ||||
|  | ||||
| echo "Sign cert with ca" | ||||
| openssl ca -in server.csr -cert ca/ca.pem -keyfile ca/ca.key -config ca/ca.cnf \ | ||||
| 	-out server.pem -days 3650 -batch | ||||
|  | ||||
| rm server.csr | ||||
| rm ca/ca.cnf | ||||
| rm req.cnf | ||||
| @@ -12,14 +12,29 @@ myMPD is a fork of ympd. | ||||
| 
 | ||||
| .SH OPTIONS | ||||
| .TP | ||||
| \fB\-h\fR, \fB\-\-host HOST\fR | ||||
| \fB\-h\fR, \fB\-\-mpdhost HOST\fR | ||||
| connect to mpd at host, defaults to localhost | ||||
| .TP | ||||
| \fB\-p\fR, \fB\-\-port PORT\fR | ||||
| \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 | ||||
| specifies the port for the webserver to listen to, defaults to 8080 | ||||
| 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] | ||||
| @@ -27,9 +42,6 @@ connect to mpd http stream at port [8000] | ||||
| \fB\-u\fR, \fB\-\-user USERNAME\fR | ||||
| drop privileges to the provided username after socket binding | ||||
| .TP | ||||
| \fB\-m\fR, \fB\-\-mpdpass PASSWORD\fR | ||||
| specifies the password to use when connecting to mpd | ||||
| .TP | ||||
| \fB-i\fR, \fB\-\-coverimage FILENAME\fR | ||||
| filename for coverimage [folder.jpg] | ||||
| .TP | ||||
| @@ -1,8 +0,0 @@ | ||||
| #Copy this file to /etc/default/mympd | ||||
| MPD_HOST=localhost | ||||
| MPD_PORT=6600 | ||||
| MPD_PASSWORD= | ||||
| WEB_PORT=80 | ||||
| MYMPD_USER=nobody | ||||
| COVERIMAGE=--coverimage folder.jpg | ||||
| STATEFILE=--statefile /var/lib/mympd/mympd.state | ||||
| @@ -3,15 +3,8 @@ Description=myMPD server daemon | ||||
| Requires=network.target local-fs.target | ||||
|  | ||||
| [Service] | ||||
| Environment=MPD_HOST=localhost | ||||
| Environment=MPD_PORT=6600 | ||||
| Environment=MPD_PASSWORD= | ||||
| Environment=WEB_PORT=80 | ||||
| Environment=MYMPD_USER=nobody | ||||
| Environment=COVERIMAGE=--coverimage folder.jpg | ||||
| Environment=STATEFILE=--statefile /var/lib/mympd/mympd.state | ||||
| EnvironmentFile=/etc/default/mympd | ||||
| ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $COVERIMAGE $STATEFILE | ||||
| EnvironmentFile=/etc/mympd/options | ||||
| ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --mpdhost $MPD_HOST --mpdport $MPD_PORT $MPD_PASSWORD $COVERIMAGE $STATEFILE $SSL | ||||
| Type=simple | ||||
|  | ||||
| [Install] | ||||
|   | ||||
							
								
								
									
										9
									
								
								contrib/options
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contrib/options
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| #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=--coverimage folder.jpg | ||||
| STATEFILE=--statefile /var/lib/mympd/mympd.state | ||||
							
								
								
									
										
											BIN
										
									
								
								htdocs/assets/appicon-167.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								htdocs/assets/appicon-167.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								htdocs/assets/appicon-192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								htdocs/assets/appicon-192.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								htdocs/assets/appicon-512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								htdocs/assets/appicon-512.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.0 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 6.3 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.2 KiB | 
| @@ -36,10 +36,6 @@ button { | ||||
|   min-width: 50px; | ||||
| } | ||||
|  | ||||
| #search { | ||||
|   width: 200px; | ||||
| } | ||||
|  | ||||
| .card { | ||||
|   min-height:350px; | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								htdocs/css/mpd.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								htdocs/css/mpd.min.css
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | ||||
| html{position:relative;min-height:100%}body{margin-bottom:60px}footer{position:absolute;bottom:0}body{padding-top:50px;padding-bottom:50px;background-color:#888}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}#counter{font-size:22px;margin-left:10px;min-width:50px}#search{width:200px}.card{min-height:350px}@media only screen and (max-width:576px){.header-logo{display:none!important}}.clickable{cursor:pointer}.tblnum,.tblaction{width:30px}.album-cover{background-size:cover;border:1px solid black;border-radius:5px;overflow:hidden;margin-bottom:20px;width:240px;height:240px;background-color:#eee}.hide{display:none!important}.pull-right{float:right!important}.card-toolbar{margin-bottom:10px}.card-toolbar>div,.card-toolbar>form{margin-bottom:5px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(/assets/MaterialIcons-Regular.eot);src:local('Material Icons'),local('MaterialIcons-Regular');src:url(/assets/MaterialIcons-Regular.woff2) format('woff2'),url(/assets/MaterialIcons-Regular.woff) format('woff'),url(/assets/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:18px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;vertical-align:top;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}.material-icons-small{font-size:16px}main{padding-top:20px}.color-darkgrey{color:#6c757d}.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}#progressBar{width:100%;margin-top:8px}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-top{height:250px;overflow:hidden;display:block}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5} | ||||
| html{position:relative;min-height:100%}body{margin-bottom:60px}footer{position:absolute;bottom:0}body{padding-top:50px;padding-bottom:50px;background-color:#888}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}#counter{font-size:22px;margin-left:10px;min-width:50px}.card{min-height:350px}@media only screen and (max-width:576px){.header-logo{display:none!important}}.clickable{cursor:pointer}.tblnum,.tblaction{width:30px}.album-cover{background-size:cover;border:1px solid black;border-radius:5px;overflow:hidden;margin-bottom:20px;width:240px;height:240px;background-color:#eee}.hide{display:none!important}.pull-right{float:right!important}.card-toolbar{margin-bottom:10px}.card-toolbar>div,.card-toolbar>form{margin-bottom:5px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(/assets/MaterialIcons-Regular.eot);src:local('Material Icons'),local('MaterialIcons-Regular');src:url(/assets/MaterialIcons-Regular.woff2) format('woff2'),url(/assets/MaterialIcons-Regular.woff) format('woff'),url(/assets/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:18px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;vertical-align:top;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}.material-icons-small{font-size:16px}main{padding-top:20px}.color-darkgrey{color:#6c757d}.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}#progressBar{width:100%;margin-top:8px}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-top{height:250px;overflow:hidden;display:block}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}div#alertBox{position:fixed;top:50px;right:10px;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5} | ||||
| @@ -1,17 +1,19 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <title>myMPD</title> | ||||
|   <meta charset="utf-8"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|   <meta name="description" content="myMPD - fast and lightweight MPD webclient"> | ||||
|   <meta name="author" content="mail@jcgames.de"> | ||||
|   <title>myMPD</title> | ||||
|   <meta name="apple-mobile-web-app-capable" content="yes"> | ||||
|   <meta name="apple-mobile-web-app-status-bar-style" content="black"> | ||||
|   <meta name="theme-color" content="#343a40"> | ||||
|   <link href="css/bootstrap.min.css" rel="stylesheet"> | ||||
|   <link href="css/mpd.css" rel="stylesheet"> | ||||
|   <link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">   | ||||
|   <meta name="apple-mobile-web-app-capable" content="yes" /> | ||||
|   <meta name="apple-mobile-web-app-status-bar-style" content="black"/> | ||||
|   <link rel="apple-touch-icon" href="assets/appicon.png"/> | ||||
|   <link rel="manifest" href="mympd.webmanifest"> | ||||
|   <link rel="apple-touch-icon" href="assets/appicon-167.png"> | ||||
| </head> | ||||
| <body> | ||||
|   <header> | ||||
| @@ -21,15 +23,12 @@ | ||||
|             <span class="material-icons header-logo">play_circle_outline</span>myMPD | ||||
|           </a> | ||||
|           <div class="dropdown-menu bg-dark"> | ||||
|             <form id="search" class="px-4 py-3" role="search"> | ||||
|               <input id="inputSearch" type="text" class="form-control" placeholder="Search"> | ||||
|             </form> | ||||
|             <div class="dropdown-divider"></div> | ||||
|             <a id="nav-addstream" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAddstream">Add Stream</a> | ||||
|             <a id="nav-updatedb" class="dropdown-item text-light bg-dark" href="#" data-href="{'cmd':'updateDB','options':[]}">Update Database</a> | ||||
|             <a id="nav-localplayer" class="dropdown-item text-light bg-dark" href="#" data-href="{'cmd':'openLocalPlayer','options':[]}">Local Player</a> | ||||
|             <a id="nav-settings" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalSettings">Settings</a> | ||||
|             <a id="nav-about" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#modalAbout">About</a> | ||||
|             <a id="btnAdd" class="dropdown-item text-light bg-dark hide" href="#">Add2HomeScreen</a> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="btn-toolbar col-auto pl-0 pr-0" role="toolbar"> | ||||
| @@ -139,7 +138,7 @@ | ||||
|         </div> | ||||
|  | ||||
|         <div class="table-responsive-md"> | ||||
|           <table id="QueueList" class="table table-hover table-sm"> | ||||
|           <table id="QueueList" class="table table-hover table-sm" data-version=""> | ||||
|             <col class="tblnum"/> | ||||
|             <col class="tbltitle"/> | ||||
|             <col class="tblartist"/> | ||||
| @@ -379,15 +378,15 @@ | ||||
|       </div> | ||||
|       <div class="card-body"> | ||||
|         <div class="btn-toolbar card-toolbar" id="SearchButtons" role="toolbar"> | ||||
|           <form id="search2" role="search"> | ||||
|           <form id="search" role="search"> | ||||
|             <div class="input-group mr-2"> | ||||
|               <input type="text" class="form-control" placeholder="Search" id="searchstr2"/> | ||||
|               <input type="text" class="form-control" placeholder="Search" id="searchstr"/> | ||||
|               <div class="input-group-append"> | ||||
|                 <button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"> | ||||
|                   <span class="material-icons">search</span> | ||||
|                   <span id="searchtags2desc">Any Tag</span> | ||||
|                   <span id="searchtagsdesc">Any Tag</span> | ||||
|                 </button> | ||||
|                 <div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchtags2"> | ||||
|                 <div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchtags"> | ||||
|                   <h6 class="dropdown-header text-light">Search in Tag</h6> | ||||
|                   <button type="button" class="btn btn-secondary active btn-block">Any Tag</button> | ||||
|                   <button type="button" class="btn btn-secondary btn-block">Title</button> | ||||
| @@ -582,31 +581,26 @@ | ||||
|           </button> | ||||
|         </div> | ||||
|         <div class="modal-body"> | ||||
|           <h4><a class="text-success" href="https://github.com/jcorporation/ympd">myMPD</a> – <small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4> | ||||
|           <h4><a class="text-success" rel="noreferrer" href="https://github.com/jcorporation/ympd">myMPD</a> – <small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4> | ||||
|           <p>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 <a class="text-success" href="http://www.ympd.org">ympd</a>.</p> | ||||
|           <ul> | ||||
|             <li>Version: <span id="mympdVersion"></span></li> | ||||
|             <li>Homepage: <a class="text-success" target="_blank" href="https://github.com/jcorporation/mympd">https://github.com/jcorporation/mympd</a></li> | ||||
|             <li>Homepage: <a class="text-success" target="_blank" rel="noreferrer" href="https://github.com/jcorporation/mympd">https://github.com/jcorporation/mympd</a></li> | ||||
|             <li>Autor: Jürgen Mang <<a class="text-success" href="mailto:mail@jcgames.de">mail@jcgames.de</a>></li> | ||||
|           </ul> | ||||
|           <hr/> | ||||
|           <h5>Database Statistics</h5> | ||||
|           <table class="table table-sm"> | ||||
|             <tbody> | ||||
|               <tr><td colspan="2" class="pt-3"><h5>Database Statistics</h5></td></tr> | ||||
|               <tr><th>Artists</th><td id="mpdstats_artists"></td></tr> | ||||
|               <tr><th>Albums</th><td id="mpdstats_albums"></td></tr> | ||||
|               <tr><th>Songs</th><td id="mpdstats_songs"></td></tr> | ||||
|               <tr><th>DB Play Time</th><td id="mpdstats_dbplaytime"></td></tr> | ||||
|               <tr><th>DB Updated</th><td id="mpdstats_dbupdated"></td></tr> | ||||
|             </tbody> | ||||
|           </table> | ||||
|           <hr/> | ||||
|           <h5>Play Statistics</h5> | ||||
|           <table class="table table-sm"> | ||||
|             <tbody> | ||||
|               <tr><th>MPD Version</th><td id="mpdVersion"></td></tr> | ||||
|               <tr><td colspan="2" class="pt-3"><h5>Play Statistics</h5></td></tr> | ||||
|               <tr><th>Uptime</th><td id="mpdstats_uptime"></td></tr> | ||||
|               <tr><th>Play Time</th><td id="mpdstats_playtime"></td></tr> | ||||
|               <tr><td colspan="2" class="pt-3"><h5>MPD</h5></td></tr> | ||||
|               <tr><th>Protocol Version</th><td id="mpdVersion"></td></tr> | ||||
|             </tbody> | ||||
|           </table>           | ||||
|         </div> | ||||
| @@ -673,7 +667,7 @@ | ||||
|     <div class="modal-dialog"> | ||||
|       <div class="modal-content"> | ||||
|         <div class="modal-header"> | ||||
|           <h5 class="modal-title" id="savequeueLabel"><span class="material-icons title-icon">music_note</span> Song Details</h5> | ||||
|           <h5 class="modal-title"><span class="material-icons title-icon">music_note</span> Song Details</h5> | ||||
|           <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
|             <span aria-hidden="true">×</span> | ||||
|           </button>           | ||||
|   | ||||
							
								
								
									
										160
									
								
								htdocs/js/mpd.js
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								htdocs/js/mpd.js
									
									
									
									
									
								
							| @@ -29,6 +29,7 @@ var current_song = new Object(); | ||||
| var playstate = ''; | ||||
| var settings = {}; | ||||
| var alertTimeout; | ||||
| let deferredPrompt; | ||||
|  | ||||
| var app = {}; | ||||
| app.apps = { "Playback": { "state": "0/-/", "scrollPos": 0 }, | ||||
| @@ -66,6 +67,7 @@ domCache.btnNext = document.getElementById('btnNext'); | ||||
| domCache.progressBar = document.getElementById('progressBar'); | ||||
| domCache.volumeBar = document.getElementById('volumeBar'); | ||||
| domCache.outputs = document.getElementById('outputs'); | ||||
| domCache.btnAdd = document.getElementById('btnAdd'); | ||||
|  | ||||
| var modalConnectionError = new Modal(document.getElementById('modalConnectionError')); | ||||
| var modalSettings = new Modal(document.getElementById('modalSettings')); | ||||
| @@ -77,14 +79,14 @@ var mainMenu = new Dropdown(document.getElementById('mainMenu')); | ||||
| function appPrepare(scrollPos) { | ||||
|     if (app.current.app != app.last.app || app.current.tab != app.last.tab || app.current.view != app.last.view) { | ||||
|         //Hide all cards + nav | ||||
|         for (var i = 0; i < domCache.navbarBottomBtnsLen; i ++) { | ||||
|         for (var i = 0; i < domCache.navbarBottomBtnsLen; i++) { | ||||
|             domCache.navbarBottomBtns[i].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 (var i = 0; i < domCache.panelHeadingBrowseLen; i ++) { | ||||
|         for (var i = 0; i < domCache.panelHeadingBrowseLen; i++) { | ||||
|             domCache.panelHeadingBrowse[i].classList.remove('active'); | ||||
|         } | ||||
|         document.getElementById('cardBrowsePlaylists').classList.add('hide'); | ||||
| @@ -168,13 +170,15 @@ function appRoute() { | ||||
|     }     | ||||
|     else if (app.current.app == 'Queue' ) { | ||||
|         document.getElementById('QueueList').classList.add('opacity05'); | ||||
| /* | ||||
|         if (app.last.app != app.current.app) { | ||||
|             if (app.current.search.length < 2) { | ||||
|                 setPagination(app.current.page);         | ||||
|             } | ||||
|         } | ||||
| */ | ||||
|         var btns = document.getElementById('searchqueuetag').getElementsByTagName('button'); | ||||
|         for (var i = 0; i < btns.length; i ++) { | ||||
|         for (var i = 0; i < btns.length; i++) { | ||||
|             btns[i].classList.remove('active'); | ||||
|             if (btns[i].innerText == app.current.filter) {  | ||||
|                 btns[i].classList.add('active');  | ||||
| @@ -211,7 +215,7 @@ function appRoute() { | ||||
|         var pathArray = app.current.search.split('/'); | ||||
|         var pathArrayLen = pathArray.length; | ||||
|         var fullPath = ''; | ||||
|         for (var i = 0; i < pathArrayLen; i ++) { | ||||
|         for (var i = 0; i < pathArrayLen; i++) { | ||||
|             if (pathArrayLen -1 == i) { | ||||
|                 breadcrumbs += '<li class="breadcrumb-item active">' + pathArray[i] + '</li>'; | ||||
|                 break; | ||||
| @@ -224,7 +228,7 @@ function appRoute() { | ||||
|         elBrowseBreadcrumb.innerHTML = breadcrumbs; | ||||
|         var breadcrumbItems = elBrowseBreadcrumb.getElementsByTagName('a'); | ||||
|         var breadcrumbItemsLen = breadcrumbItems.length; | ||||
|         for (var i = 0; i < breadcrumbItemsLen; i ++) { | ||||
|         for (var i = 0; i < breadcrumbItemsLen; i++) { | ||||
|             breadcrumbItems[i].addEventListener('click', function() { | ||||
| 	        appGoto('Browse', 'Filesystem', undefined, '0/' + app.current.filter + '/' + this.getAttribute('data-uri')); | ||||
|             }, false); | ||||
| @@ -232,17 +236,17 @@ function appRoute() { | ||||
|         doSetFilterLetter('BrowseFilesystemFilter'); | ||||
|     } | ||||
|     else if (app.current.app == 'Search') { | ||||
|         document.getElementById('searchstr2').focus(); | ||||
|         document.getElementById('searchstr').focus(); | ||||
|         document.getElementById('SearchList').classList.add('opacity05'); | ||||
|         if (app.last.app != app.current.app) { | ||||
|             if (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>'; | ||||
|             else | ||||
|                 setPagination(app.current.page);         | ||||
| //            else | ||||
| //                setPagination(app.current.page);         | ||||
|                  | ||||
|             document.getElementById('searchstr2').value = app.current.search; | ||||
| //            document.getElementById('searchstr').value = app.current.search; | ||||
|         } | ||||
|  | ||||
|         if (app.current.search.length >= 2) { | ||||
| @@ -251,17 +255,17 @@ function appRoute() { | ||||
|             document.getElementById('SearchList').getElementsByTagName('tbody')[0].innerHTML = ''; | ||||
|             document.getElementById('searchAddAllSongs').setAttribute('disabled', 'disabled'); | ||||
|             document.getElementById('panel-heading-search').innerText = ''; | ||||
|             setPagination(app.current.page); | ||||
| //            setPagination(app.current.page); | ||||
|             document.getElementById('SearchList').classList.remove('opacity05'); | ||||
|         } | ||||
|          | ||||
|         var btns = document.getElementById('searchtags2').getElementsByTagName('button'); | ||||
|         var btns = document.getElementById('searchtags').getElementsByTagName('button'); | ||||
|         var btnsLen = btns.length; | ||||
|         for (var i = 0; i < btnsLen; i ++) { | ||||
|         for (var i = 0; i < btnsLen; i++) { | ||||
|             btns[i].classList.remove('active'); | ||||
|             if (btns[i].innerText == app.current.filter) {  | ||||
|                 btns[i].classList.add('active');  | ||||
|                 document.getElementById('searchtags2desc').innerText = btns[i].innerText; | ||||
|                 document.getElementById('searchtagsdesc').innerText = btns[i].innerText; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -313,23 +317,13 @@ function appInit() { | ||||
|         addStream(); | ||||
|     }); | ||||
|      | ||||
|     document.getElementById('mainMenu').addEventListener('shown.bs.dropdown', function () { | ||||
|         var si = document.getElementById('inputSearch'); | ||||
|         si.value = ''; | ||||
|         si.focus(); | ||||
|     }); | ||||
|      | ||||
|     document.getElementById('inputSearch').addEventListener('click', function(event) { | ||||
|         event.stopPropagation(); | ||||
|     }); | ||||
|      | ||||
|     addFilterLetter('BrowseFilesystemFilterLetters'); | ||||
|     addFilterLetter('BrowseDatabaseFilterLetters'); | ||||
|     addFilterLetter('BrowsePlaylistsFilterLetters'); | ||||
|  | ||||
|     var hrefs = document.querySelectorAll('button[data-href], a[data-href]'); | ||||
|     var hrefsLen = hrefs.length; | ||||
|     for (var i = 0; i < hrefsLen; i ++) { | ||||
|     for (var i = 0; i < hrefsLen; i++) { | ||||
|         hrefs[i].addEventListener('click', function(event) { | ||||
|             event.preventDefault(); | ||||
|             event.stopPropagation(); | ||||
| @@ -348,7 +342,7 @@ function appInit() { | ||||
|  | ||||
|     var pd = document.querySelectorAll('.pages'); | ||||
|     var pdLen = pd.length; | ||||
|     for (var i = 0; i < pdLen; i ++) { | ||||
|     for (var i = 0; i < pdLen; i++) { | ||||
|         pd[i].addEventListener('click', function(event) { | ||||
|             if (event.target.nodeName == 'BUTTON') { | ||||
|                 gotoPage(event.target.getAttribute('data-page')); | ||||
| @@ -418,7 +412,7 @@ function appInit() { | ||||
|         } | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('searchtags2').addEventListener('click', function(event) { | ||||
|     document.getElementById('searchtags').addEventListener('click', function(event) { | ||||
|         if (event.target.nodeName == 'BUTTON') | ||||
|             appGoto(app.current.app, app.current.tab, app.current.view, '0/' + event.target.innerText + '/' + app.current.search);             | ||||
|     }, false); | ||||
| @@ -432,19 +426,7 @@ function appInit() { | ||||
|             appGoto(app.current.app, app.current.tab, app.current.view, app.current.page + '/' + event.target.innerText + '/' + app.current.search); | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('inputSearch').addEventListener('keypress', function (event) { | ||||
|         if ( event.which == 13 ) | ||||
|             mainMenu.toggle(); | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('search').addEventListener('submit', function () { | ||||
|         var searchStr = document.getElementById('inputSearch').value; | ||||
|         appGoto('Search', undefined, undefined, app.current.page + '/Any Tag/' + searchStr); | ||||
|         document.getElementById('searchstr2').value = searchStr; | ||||
|         return false; | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('search2').addEventListener('submit', function () { | ||||
|         return false; | ||||
|     }, false); | ||||
|  | ||||
| @@ -452,7 +434,7 @@ function appInit() { | ||||
|         return false; | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('searchstr2').addEventListener('keyup', function (event) { | ||||
|     document.getElementById('searchstr').addEventListener('keyup', function (event) { | ||||
|         appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + this.value); | ||||
|     }, false); | ||||
|  | ||||
| @@ -476,6 +458,51 @@ function appInit() { | ||||
|         } | ||||
|         event.preventDefault(); | ||||
|     }, false); | ||||
|      | ||||
|     if ('serviceWorker' in navigator) { | ||||
|         window.addEventListener('load', function() { | ||||
|             navigator.serviceWorker.register('/sw.js', {scope: '/'}).then(function(registration) { | ||||
|                 // Registration was successful | ||||
|                 console.log('ServiceWorker registration successful with scope: ', registration.scope); | ||||
|             }, function(err) { | ||||
|                 // registration failed :( | ||||
|                 console.log('ServiceWorker registration failed: ', err); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|      | ||||
|     window.addEventListener('beforeinstallprompt', (e) => { | ||||
|         // Prevent Chrome 67 and earlier from automatically showing the prompt | ||||
|         e.preventDefault(); | ||||
|         // Stash the event so it can be triggered later. | ||||
|         deferredPrompt = e; | ||||
|     }); | ||||
|      | ||||
|     window.addEventListener('beforeinstallprompt', (e) => { | ||||
|         e.preventDefault(); | ||||
|         deferredPrompt = e; | ||||
|         // Update UI notify the user they can add to home screen | ||||
|         domCache.btnAdd.classList.remove('hide'); | ||||
|     }); | ||||
|      | ||||
|     domCache.btnAdd.addEventListener('click', (e) => { | ||||
|         // hide our user interface that shows our A2HS button | ||||
|         domCache.btnAdd.classList.add('hide'); | ||||
|         // Show the prompt | ||||
|         deferredPrompt.prompt(); | ||||
|         // Wait for the user to respond to the prompt | ||||
|         deferredPrompt.userChoice.then((choiceResult) => { | ||||
|             if (choiceResult.outcome === 'accepted') | ||||
|                 console.log('User accepted the A2HS prompt'); | ||||
|             else | ||||
|                 console.log('User dismissed the A2HS prompt'); | ||||
|             deferredPrompt = null; | ||||
|         }); | ||||
|     }); | ||||
|      | ||||
|     window.addEventListener('appinstalled', (evt) => { | ||||
|         console.log('appinstalled'); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function webSocketConnect() { | ||||
| @@ -651,7 +678,7 @@ function getSettings() { | ||||
| function parseOutputnames(obj) { | ||||
|     var btns = ''; | ||||
|     var outputsLen = obj.data.outputs.length; | ||||
|     for (var i = 0; i < outputsLen; i ++) { | ||||
|     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>'; | ||||
|     } | ||||
| @@ -746,7 +773,7 @@ function parseState(obj) { | ||||
|          | ||||
|     //  Set outputs state                   | ||||
|     var outputsLen = obj.data.outputs.length; | ||||
|     for (var i = 0; i < outputsLen; i ++) { | ||||
|     for (var i = 0; i < outputsLen; i++) { | ||||
|         toggleBtn('btnoutput' + obj.data.outputs[i].id, obj.data.outputs[i].state); | ||||
|     } | ||||
|  | ||||
| @@ -756,8 +783,13 @@ function parseState(obj) { | ||||
| function getQueue() { | ||||
|     if (app.current.search.length >= 2)  | ||||
|         sendAPI({"cmd": "MPD_API_SEARCH_QUEUE", "data": {"mpdtag":app.current.filter, "offset":app.current.page, "searchstr": app.current.search}}, parseQueue); | ||||
|     else | ||||
|         sendAPI({"cmd": "MPD_API_GET_QUEUE", "data": {"offset": app.current.page}}, parseQueue); | ||||
|     else { | ||||
|         var queue_version = document.getElementById('QueueList').getAttribute('data-version'); | ||||
|         if (last_state && queue_version != last_state.data.queue_version) | ||||
|             sendAPI({"cmd": "MPD_API_GET_QUEUE", "data": {"offset": app.current.page}}, parseQueue); | ||||
|         else | ||||
|             document.getElementById('QueueList').classList.remove('opacity05'); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function parseQueue(obj) { | ||||
| @@ -772,9 +804,11 @@ function parseQueue(obj) { | ||||
|         document.getElementById('panel-heading-queue').innerText = ''; | ||||
|  | ||||
|     var nrItems = obj.data.length; | ||||
|     var tbody = document.getElementById(app.current.app + 'List').getElementsByTagName('tbody')[0]; | ||||
|     var table = document.getElementById(app.current.app + 'List'); | ||||
|     table.setAttribute('data-version', obj.queue_version); | ||||
|     var tbody = table.getElementsByTagName('tbody')[0]; | ||||
|     var tr = tbody.getElementsByTagName('tr'); | ||||
|     for (var i = 0; i < nrItems; i ++) { | ||||
|     for (var i = 0; i < nrItems; i++) { | ||||
|         if (tr[i]) | ||||
|             if (tr[i].getAttribute('data-trackid') == obj.data[i].id) | ||||
|                 continue; | ||||
| @@ -833,7 +867,7 @@ function parseFilesystem(obj) { | ||||
|     var nrItems = obj.data.length; | ||||
|     var tbody = document.getElementById(app.current.app + (app.current.tab==undefined ? '' : app.current.tab) + 'List').getElementsByTagName('tbody')[0]; | ||||
|     var tr = tbody.getElementsByTagName('tr'); | ||||
|     for (var i = 0; i < nrItems; i ++) { | ||||
|     for (var i = 0; i < nrItems; i++) { | ||||
|         var uri = encodeURI(obj.data[i].uri); | ||||
|         if (tr[i]) | ||||
|             if (tr[i].getAttribute('data-uri') == uri) | ||||
| @@ -890,7 +924,7 @@ function parsePlaylists(obj) { | ||||
|     var nrItems = obj.data.length; | ||||
|     var tbody = document.getElementById(app.current.app + app.current.tab + 'List').getElementsByTagName('tbody')[0]; | ||||
|     var tr = tbody.getElementsByTagName('tr'); | ||||
|     for (var i = 0; i < nrItems; i ++) { | ||||
|     for (var i = 0; i < nrItems; i++) { | ||||
|         var uri = encodeURI(obj.data[i].uri); | ||||
|         if (tr[i]) | ||||
|             if (tr[i].getAttribute('data-uri') == uri) | ||||
| @@ -932,7 +966,7 @@ function parseListDBtags(obj) { | ||||
|         var nrItems = obj.data.length; | ||||
|         var tbody = document.getElementById(app.current.app + app.current.tab + app.current.view + 'List').getElementsByTagName('tbody')[0]; | ||||
|         var tr = tbody.getElementsByTagName('tr'); | ||||
|         for (var i = 0; i < nrItems; i ++) { | ||||
|         for (var i = 0; i < nrItems; i++) { | ||||
|             var uri = encodeURI(obj.data[i].value); | ||||
|             if (tr[i]) | ||||
|                 if (tr[i].getAttribute('data-uri') == uri) | ||||
| @@ -1017,7 +1051,7 @@ function parseListTitles(obj) { | ||||
|    | ||||
|     var titleList = ''; | ||||
|     var nrItems = obj.data.length; | ||||
|     for (var i = 0; i < nrItems; i ++) { | ||||
|     for (var i = 0; i < nrItems; i++) { | ||||
|         titleList += '<tr data-type="song" data-name="' + obj.data[i].title + '" data-uri="' + encodeURI(obj.data[i].uri) + '">' + | ||||
|                      '<td>' + obj.data[i].track + '</td><td>' + obj.data[i].title + '</td>' + | ||||
|                      '<td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>' +  | ||||
| @@ -1047,12 +1081,12 @@ function setPagination(number) { | ||||
|     if (totalPages == 0)  | ||||
|         totalPages = 1; | ||||
|     var p = ['PaginationTop', 'PaginationBottom']; | ||||
|     for (var i = 0; i < 2; i ++) { | ||||
|     for (var i = 0; i < 2; i++) { | ||||
|         document.getElementById(cat + p[i] + 'Page').innerText = (app.current.page / settings.max_elements_per_page + 1) + ' / ' + totalPages; | ||||
|         if (totalPages > 1) { | ||||
|             document.getElementById(cat + p[i] + 'Page').removeAttribute('disabled'); | ||||
|             var pl = ''; | ||||
|             for (var j = 0; j < totalPages; j ++) { | ||||
|             for (var j = 0; j < totalPages; j++) { | ||||
|                 pl += '<button data-page="' + (j * settings.max_elements_per_page) + '" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">' + | ||||
|                     ( j + 1) + '</button>'; | ||||
|             } | ||||
| @@ -1136,7 +1170,7 @@ function parseSongDetails(obj) { | ||||
|     modal.getElementsByTagName('h1')[0].innerText = obj.data.title; | ||||
|     var tr = modal.getElementsByTagName('tr'); | ||||
|     var trLen = tr.length; | ||||
|     for (var i = 0; i < trLen; i ++) { | ||||
|     for (var i = 0; i < trLen; i++) { | ||||
|         var key = tr[i].getAttribute('data-name'); | ||||
|         var value = obj.data[key]; | ||||
|         if (key == 'duration') { | ||||
| @@ -1389,6 +1423,9 @@ function showNotification(notificationTitle,notificationText,notificationHtml,no | ||||
|         if (!document.getElementById('alertBox')) { | ||||
|             alertBox = document.createElement('div'); | ||||
|             alertBox.setAttribute('id', 'alertBox'); | ||||
|             alertBox.addEventListener('click', function() { | ||||
|                 hideNotification(); | ||||
|             }, false); | ||||
|         } | ||||
|         else { | ||||
|             alertBox = document.getElementById('alertBox'); | ||||
| @@ -1400,15 +1437,20 @@ function showNotification(notificationTitle,notificationText,notificationHtml,no | ||||
|         if (alertTimeout) | ||||
|             clearTimeout(alertTimeout); | ||||
|         alertTimeout = setTimeout(function() { | ||||
|             if (document.getElementById('alertBox')) | ||||
|                 document.getElementById('alertBox').classList.remove('alertBoxActive'); | ||||
|             setTimeout(function() { | ||||
|                 document.getElementById('alertBox').remove(); | ||||
|             }, 600); | ||||
|             hideNotification();     | ||||
|         }, 3000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function hideNotification() { | ||||
|     if (document.getElementById('alertBox')) { | ||||
|         document.getElementById('alertBox').classList.remove('alertBoxActive'); | ||||
|         setTimeout(function() { | ||||
|             document.getElementById('alertBox').remove(); | ||||
|         }, 600); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function notificationsSupported() { | ||||
|     return "Notification" in window; | ||||
| } | ||||
| @@ -1468,7 +1510,7 @@ function doSetFilterLetter(x) { | ||||
|     if (filter != '-') { | ||||
|         var btns = document.getElementById(x + 'Letters').getElementsByTagName('button'); | ||||
|         var btnsLen = btns.length; | ||||
|         for (var i = 0; i < btnsLen; i ++) { | ||||
|         for (var i = 0; i < btnsLen; i++) { | ||||
|             if (btns[i].innerText == filter) { | ||||
|                 btns[i].classList.add('active'); | ||||
|                 break; | ||||
| @@ -1480,7 +1522,7 @@ function doSetFilterLetter(x) { | ||||
| function addFilterLetter(x) { | ||||
|     var filter = '<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>'; | ||||
|     for (i = 65; i <= 90; i ++) { | ||||
|     for (i = 65; i <= 90; i++) { | ||||
|         filter += '<button class="mr-1 mb-1 btn-sm btn btn-secondary">' + String.fromCharCode(i) + '</button>'; | ||||
|     } | ||||
|     var letters = document.getElementById(x); | ||||
|   | ||||
							
								
								
									
										57
									
								
								htdocs/js/mpd.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										57
									
								
								htdocs/js/mpd.min.js
									
									
									
									
										vendored
									
									
								
							| @@ -5,10 +5,10 @@ $jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};a[ | ||||
| $jscomp.checkStringArgs=function(a,b,c){if(null==a)throw new TypeError("The 'this' value for String.prototype."+c+" must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype."+c+" must not be a regular expression");return a+""}; | ||||
| $jscomp.polyfill=function(a,b,c,e){if(b){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var d=a[e];d in c||(c[d]={});c=c[d]}a=a[a.length-1];e=c[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}}; | ||||
| $jscomp.polyfill("String.prototype.repeat",function(a){return a?a:function(a){var b=$jscomp.checkStringArgs(this,null,"repeat");if(0>a||1342177279<a)throw new RangeError("Invalid count value");a|=0;for(var e="";a;)if(a&1&&(e+=b),a>>>=1)b+=b;return e}},"es6","es3"); | ||||
| var socket,last_song="",last_state,current_song={},playstate="",settings={},alertTimeout,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",scrollPos:0},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/",scrollPos:0},Playlists:{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"); | ||||
| var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalAddstream=new Modal(document.getElementById("modalAddstream")),modalSavequeue=new Modal(document.getElementById("modalSavequeue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),mainMenu=new Dropdown(document.getElementById("mainMenu")); | ||||
| var socket,last_song="",last_state,current_song={},playstate="",settings={},alertTimeout,deferredPrompt,app={apps:{Playback:{state:"0/-/",scrollPos:0},Queue:{state:"0/Any Tag/",scrollPos:0},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/",scrollPos:0},Playlists:{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("btnAdd");var modalConnectionError=new Modal(document.getElementById("modalConnectionError")),modalSettings=new Modal(document.getElementById("modalSettings")),modalAddstream=new Modal(document.getElementById("modalAddstream")),modalSavequeue=new Modal(document.getElementById("modalSavequeue")),modalSongDetails=new Modal(document.getElementById("modalSongDetails")),mainMenu=new Dropdown(document.getElementById("mainMenu")); | ||||
| 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)}} | ||||
| @@ -16,26 +16,26 @@ function appGoto(a,b,c,e){var d=document.body.scrollTop?document.body.scrollTop: | ||||
| (void 0==b&&(b=app.apps[a].active),app.apps[a].tabs[b].views?(void 0==c&&(c=app.apps[a].tabs[b].active),a="/"+a+"/"+b+"/"+c+"!"+(void 0==e?app.apps[a].tabs[b].views[c].state:e)):a="/"+a+"/"+b+"!"+(void 0==e?app.apps[a].tabs[b].state:e)):a="/"+a+"!"+(void 0==e?app.apps[a].state:e);location.hash=a} | ||||
| function appRoute(){if(params=decodeURI(location.hash).match(/^#\/(\w+)\/?(\w+)?\/?(\w+)?!((\d+)\/([^\/]+)\/(.*))$/)){app.current.app=params[1];app.current.tab=params[2];app.current.view=params[3];app.apps[app.current.app].state?(app.apps[app.current.app].state=params[4],app.current.scrollPos=app.apps[app.current.app].scrollPos):app.apps[app.current.app].tabs[app.current.tab].state?(app.apps[app.current.app].tabs[app.current.tab].state=params[4],app.apps[app.current.app].active=app.current.tab,app.current.scrollPos= | ||||
| app.apps[app.current.app].tabs[app.current.tab].scrollPos):app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].state&&(app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].state=params[4],app.apps[app.current.app].active=app.current.tab,app.apps[app.current.app].tabs[app.current.tab].active=app.current.view,app.current.scrollPos=app.apps[app.current.app].tabs[app.current.tab].views[app.current.view].scrollPos);app.current.page=parseInt(params[5]);app.current.filter= | ||||
| params[6];app.current.search=params[7];appPrepare(app.current.scrollPos);if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app){document.getElementById("QueueList").classList.add("opacity05");app.last.app!=app.current.app&&2>app.current.search.length&&setPagination(app.current.page);for(var a=document.getElementById("searchqueuetag").getElementsByTagName("button"),b=0;b<a.length;b++)a[b].classList.remove("active"),a[b].innerText==app.current.filter&& | ||||
| (a[b].classList.add("active"),document.getElementById("searchqueuetagdesc").innerText=a[b].innerText);getQueue()}else if("Browse"==app.current.app&&"Playlists"==app.current.tab)document.getElementById("BrowsePlaylistsList").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Artist"==app.current.view)document.getElementById("BrowseDatabaseArtistList").classList.add("opacity05"), | ||||
| sendAPI({cmd:"MPD_API_GET_ARTISTS",data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)document.getElementById("BrowseDatabaseAlbumCards").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter"); | ||||
| else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){document.getElementById("BrowseFilesystemList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page,path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);app.current.search?document.getElementById("BrowseFilesystemAddAllSongs").removeAttribute("disabled"):document.getElementById("BrowseFilesystemAddAllSongs").setAttribute("disabled","disabled");a='<li class="breadcrumb-item"><a data-uri="">root</a></li>'; | ||||
| params[6];app.current.search=params[7];appPrepare(app.current.scrollPos);if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app){document.getElementById("QueueList").classList.add("opacity05");for(var a=document.getElementById("searchqueuetag").getElementsByTagName("button"),b=0;b<a.length;b++)a[b].classList.remove("active"),a[b].innerText==app.current.filter&&(a[b].classList.add("active"),document.getElementById("searchqueuetagdesc").innerText= | ||||
| a[b].innerText);getQueue()}else if("Browse"==app.current.app&&"Playlists"==app.current.tab)document.getElementById("BrowsePlaylistsList").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists),doSetFilterLetter("BrowsePlaylistsFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Artist"==app.current.view)document.getElementById("BrowseDatabaseArtistList").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_ARTISTS", | ||||
| data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)document.getElementById("BrowseDatabaseAlbumCards").classList.add("opacity05"),sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags),doSetFilterLetter("BrowseDatabaseFilter");else if("Browse"==app.current.app&& | ||||
| "Filesystem"==app.current.tab){document.getElementById("BrowseFilesystemList").classList.add("opacity05");sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page,path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);app.current.search?document.getElementById("BrowseFilesystemAddAllSongs").removeAttribute("disabled"):document.getElementById("BrowseFilesystemAddAllSongs").setAttribute("disabled","disabled");a='<li class="breadcrumb-item"><a data-uri="">root</a></li>'; | ||||
| var c=app.current.search.split("/"),e=c.length,d="";for(b=0;b<e;b++){if(e-1==b){a+='<li class="breadcrumb-item active">'+c[b]+"</li>";break}d+=c[b];a+='<li class="breadcrumb-item"><a data-uri="'+d+'">'+c[b]+"</a></li>";d+="/"}b=document.getElementById("BrowseBreadcrumb");b.innerHTML=a;a=b.getElementsByTagName("a");c=a.length;for(b=0;b<c;b++)a[b].addEventListener("click",function(){appGoto("Browse","Filesystem",void 0,"0/"+app.current.filter+"/"+this.getAttribute("data-uri"))},!1);doSetFilterLetter("BrowseFilesystemFilter")}else if("Search"== | ||||
| app.current.app)for(document.getElementById("searchstr2").focus(),document.getElementById("SearchList").classList.add("opacity05"),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>':setPagination(app.current.page),document.getElementById("searchstr2").value=app.current.search),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("panel-heading-search").innerText="",setPagination(app.current.page),document.getElementById("SearchList").classList.remove("opacity05")),a=document.getElementById("searchtags2").getElementsByTagName("button"),c= | ||||
| a.length,b=0;b<c;b++)a[b].classList.remove("active"),a[b].innerText==app.current.filter&&(a[b].classList.add("active"),document.getElementById("searchtags2desc").innerText=a[b].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")} | ||||
| app.current.app)for(document.getElementById("searchstr").focus(),document.getElementById("SearchList").classList.add("opacity05"),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("panel-heading-search").innerText="",document.getElementById("SearchList").classList.remove("opacity05")),a=document.getElementById("searchtags").getElementsByTagName("button"),c=a.length,b=0;b<c;b++)a[b].classList.remove("active"),a[b].innerText==app.current.filter&&(a[b].classList.add("active"), | ||||
| document.getElementById("searchtagsdesc").innerText=a[b].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("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("modalAddstream").addEventListener("shown.bs.modal",function(){document.getElementById("streamurl").focus()});document.getElementById("addstreamFrm").addEventListener("submit",function(){addStream()});document.getElementById("mainMenu").addEventListener("shown.bs.dropdown",function(){var a=document.getElementById("inputSearch");a.value="";a.focus()});document.getElementById("inputSearch").addEventListener("click",function(a){a.stopPropagation()});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,'"'));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"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")), | ||||
| a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseDatabaseArtistList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database","Album","0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").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);document.getElementById("searchtags2").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,"0/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click", | ||||
| function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("inputSearch").addEventListener("keypress",function(a){13==a.which&&mainMenu.toggle()},!1);document.getElementById("search").addEventListener("submit",function(){var a=document.getElementById("inputSearch").value;appGoto("Search",void 0,void 0,app.current.page+"/Any Tag/"+a);document.getElementById("searchstr2").value= | ||||
| a;return!1},!1);document.getElementById("search2").addEventListener("submit",function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr2").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);window.addEventListener("hashchange",appRoute,!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:clickPrev(); | ||||
| break;case 39:clickNext();break;case 32:clickPlay();break;default:return}a.preventDefault()}},!1)} | ||||
| document.getElementById("modalAddstream").addEventListener("shown.bs.modal",function(){document.getElementById("streamurl").focus()});document.getElementById("addstreamFrm").addEventListener("submit",function(){addStream()});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,'"'));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"== | ||||
| a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowsePlaylistsList").addEventListener("click",function(a){"TD"==a.target.nodeName?appendQueue("plist",decodeURI(a.target.parentNode.getAttribute("data-uri")),a.target.parentNode.getAttribute("data-name")):"A"==a.target.nodeName&&(a.preventDefault(),showMenu(a.target))},!1);document.getElementById("BrowseDatabaseArtistList").addEventListener("click",function(a){"TD"==a.target.nodeName&&appGoto("Browse","Database", | ||||
| "Album","0/-/"+a.target.parentNode.getAttribute("data-uri"))},!1);document.getElementById("SearchList").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);document.getElementById("searchtags").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view, | ||||
| "0/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("searchqueuestr").addEventListener("keyup",function(a){appGoto(app.current.app,app.current.tab,app.current.view,"0/"+app.current.filter+"/"+this.value)},!1);document.getElementById("searchqueuetag").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+a.target.innerText+"/"+app.current.search)},!1);document.getElementById("search").addEventListener("submit", | ||||
| function(){return!1},!1);document.getElementById("searchqueue").addEventListener("submit",function(){return!1},!1);document.getElementById("searchstr").addEventListener("keyup",function(a){appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+this.value)},!1);window.addEventListener("hashchange",appRoute,!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:clickPrev();break;case 39:clickNext();break;case 32:clickPlay();break;default:return}a.preventDefault()}}, | ||||
| !1);"serviceWorker"in navigator&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ",a.scope)},function(a){console.log("ServiceWorker registration failed: ",a)})});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a;domCache.btnAdd.classList.remove("hide")}); | ||||
| domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome?console.log("User accepted the A2HS prompt"):console.log("User dismissed the A2HS prompt");deferredPrompt=null})});window.addEventListener("appinstalled",function(a){console.log("appinstalled")})} | ||||
| 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"} | ||||
| @@ -50,11 +50,12 @@ function parseState(a){if(JSON.stringify(a)!==JSON.stringify(last_state)){1==a.d | ||||
| 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)} | ||||
| 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="";for(var b=a.data.length,c=document.getElementById(app.current.app+"List").getElementsByTagName("tbody")[0],e=c.getElementsByTagName("tr"),d=0;d<b;d++)if(!e[d]|| | ||||
| e[d].getAttribute("data-trackid")!=a.data[d].id){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("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&&0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty queue</td></tr>');setPagination(a.totalEntities); | ||||
| document.getElementById("QueueList").classList.remove("opacity05")}}function parseSearch(a){"Search"===app.current.app&&(document.getElementById("panel-heading-search").innerHTML=a.totalEntities+" Songs found",0<a.totalEntities?document.getElementById("searchAddAllSongs").removeAttribute("disabled"):document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),parseFilesystem(a))} | ||||
| b;c++)toggleBtn("btnoutput"+a.data.outputs[c].id,a.data.outputs[c].state);last_state=a}} | ||||
| function getQueue(){if(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);else{var a=document.getElementById("QueueList").getAttribute("data-version");last_state&&a!=last_state.data.queue_version?sendAPI({cmd:"MPD_API_GET_QUEUE",data:{offset:app.current.page}},parseQueue):document.getElementById("QueueList").classList.remove("opacity05")}} | ||||
| 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){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("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&&0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">Empty queue</td></tr>'); | ||||
| setPagination(a.totalEntities);document.getElementById("QueueList").classList.remove("opacity05")}}function parseSearch(a){"Search"===app.current.app&&(document.getElementById("panel-heading-search").innerHTML=a.totalEntities+" Songs found",0<a.totalEntities?document.getElementById("searchAddAllSongs").removeAttribute("disabled"):document.getElementById("searchAddAllSongs").setAttribute("disabled","disabled"),parseFilesystem(a))} | ||||
| function parseFilesystem(a){if("Browse"===app.current.app||"Filesystem"===app.current.tab||"Search"===app.current.app){for(var b=a.data.length,c=document.getElementById(app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List").getElementsByTagName("tbody")[0],e=c.getElementsByTagName("tr"),d=0;d<b;d++){var f=encodeURI(a.data[d].uri);if(!e[d]||e[d].getAttribute("data-uri")!=f){var g=document.createElement("tr");g.setAttribute("data-type",a.data[d].type);g.setAttribute("data-uri",f);g.setAttribute("data-name", | ||||
| a.data[d].name);switch(a.data[d].type){case "dir":g.innerHTML='<td><span class="material-icons">folder_open</span></td><td colspan="4">'+a.data[d].name+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';break;case "song":f=Math.floor(a.data[d].duration/60);var h=a.data[d].duration-60*f;g.innerHTML='<td><span class="material-icons">music_note</span></td><td>'+a.data[d].title+"</td><td>"+a.data[d].artist+"</td><td>"+a.data[d].album+"</td><td>"+f+":"+(10>h?"0":"")+h+'</td><td><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>'; | ||||
| break;case "plist":g.innerHTML='<td><span class="material-icons">list</span></td><td colspan="4">'+a.data[d].name+'</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();setPagination(a.totalEntities);0==b&&(c.innerHTML='<tr><td><span class="material-icons">error_outline</span></td><td colspan="5">No results</td></tr>');document.getElementById(app.current.app+(void 0==app.current.tab?"": | ||||
| @@ -94,8 +95,8 @@ mixrampdb:document.getElementById("inputMixrampdb").value,mixrampdelay:document. | ||||
| function addAllFromBrowse(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:app.current.search}});showNotification("Added all songs","","","success")}function addAllFromSearch(){2<=app.current.search.length&&(sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:app.current.search}}),showNotification("Added "+parseInt(document.getElementById("panel-heading-search").innerText)+" songs from search","","","success"))} | ||||
| function scrollTo(a){document.body.scrollTop=a;document.documentElement.scrollTop=a}function gotoPage(a){switch(a){case "next":app.current.page+=settings.max_elements_per_page;break;case "prev":app.current.page-=settings.max_elements_per_page;0>=app.current.page&&(app.current.page=0);break;default:app.current.page=a}appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)} | ||||
| function addStream(){var a=document.getElementById("streamurl");""!=a.value&&sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:a.value}});a.value="";modalAddstream.hide()}function saveQueue(){var a=document.getElementById("playlistname");""!=a.value&&sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:a.value}});a.value="";modalSavequeue.hide()} | ||||
| 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.classList.add("alert","alert-"+e),b.innerHTML="<div><strong>"+a+"</strong>"+c+"</div>",document.getElementsByTagName("main")[0].append(b),document.getElementById("alertBox").classList.add("alertBoxActive"), | ||||
| alertTimeout&&clearTimeout(alertTimeout),alertTimeout=setTimeout(function(){document.getElementById("alertBox")&&document.getElementById("alertBox").classList.remove("alertBoxActive");setTimeout(function(){document.getElementById("alertBox").remove()},600)},3E3))}function notificationsSupported(){return"Notification"in window} | ||||
| 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.add("alert","alert-"+e),b.innerHTML="<div><strong>"+a+"</strong>"+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){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+="<br/>"+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}}} | ||||
|   | ||||
							
								
								
									
										21
									
								
								htdocs/mympd.webmanifest
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								htdocs/mympd.webmanifest
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| { | ||||
|   "background_color": "#343a40", | ||||
|   "theme_color": "#343a40", | ||||
|   "description": "myMPD - fast and lightweight MPD webclient", | ||||
|   "display": "standalone", | ||||
|   "icons": [ | ||||
|     { | ||||
|       "src": "assets/appicon-192.png", | ||||
|       "sizes": "192x192", | ||||
|       "type": "image/png" | ||||
|     }, | ||||
|     { | ||||
|       "src": "assets/appicon-512.png", | ||||
|       "sizes": "512x512", | ||||
|       "type": "image/png" | ||||
|     } | ||||
|   ], | ||||
|   "name": "myMPD", | ||||
|   "short_name": "myMPD", | ||||
|   "start_url": "/index.html" | ||||
| } | ||||
							
								
								
									
										56
									
								
								htdocs/sw.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								htdocs/sw.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| var CACHE_NAME = 'myMPD-cache-v1'; | ||||
| var urlsToCache = [ | ||||
|     '/', | ||||
|     '/player.html', | ||||
|     '/css/bootstrap.min.css', | ||||
|     '/css/mpd.css', | ||||
|     '/js/bootstrap-native-v4.min.js', | ||||
|     '/js/mpd.js', | ||||
|     '/js/player.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' | ||||
| ]; | ||||
|  | ||||
| self.addEventListener('install', function(event) { | ||||
|     // Perform install steps | ||||
|     event.waitUntil( | ||||
|         caches.open(CACHE_NAME).then(function(cache) { | ||||
|             console.log('Opened cache'); | ||||
|             return cache.addAll(urlsToCache); | ||||
|         }) | ||||
|     ); | ||||
| }); | ||||
|  | ||||
| self.addEventListener('fetch', function(event) { | ||||
|     event.respondWith( | ||||
|         caches.match(event.request).then(function(response) { | ||||
|             // Cache hit - return response | ||||
|             if (response) | ||||
|                 return response | ||||
|             else | ||||
|                 return fetch(event.request); | ||||
|         }) | ||||
|     ); | ||||
| }); | ||||
|  | ||||
| self.addEventListener('activate', function(event) { | ||||
|     var cacheWhitelist = ['myMPD-cache-v1']; | ||||
|     event.waitUntil( | ||||
|         caches.keys().then(function(cacheNames) { | ||||
|             return Promise.all( | ||||
|                 cacheNames.map(function(cacheName) { | ||||
|                     if (cacheWhitelist.indexOf(cacheName) === -1) | ||||
|                         return caches.delete(cacheName); | ||||
|                 }) | ||||
|             ); | ||||
|         }) | ||||
|     ); | ||||
| }); | ||||
							
								
								
									
										14
									
								
								htdocs/sw.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								htdocs/sw.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,f){a!=Array.prototype&&a!=Object.prototype&&(a[b]=f.value)};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_"; | ||||
| $jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var a=0;return function(b){return $jscomp.SYMBOL_PREFIX+(b||"")+a++}}(); | ||||
| $jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.iterator;a||(a=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&$jscomp.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(a){var b=0;return $jscomp.iteratorPrototype(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})}; | ||||
| $jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};a[$jscomp.global.Symbol.iterator]=function(){return this};return a};$jscomp.iteratorFromArray=function(a,b){$jscomp.initSymbolIterator();a instanceof String&&(a+="");var f=0,e={next:function(){if(f<a.length){var c=f++;return{value:b(c,a[c]),done:!1}}e.next=function(){return{done:!0,value:void 0}};return e.next()}};e[Symbol.iterator]=function(){return e};return e}; | ||||
| $jscomp.polyfill=function(a,b,f,e){if(b){f=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var c=a[e];c in f||(f[c]={});f=f[c]}a=a[a.length-1];e=f[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(f,a,{configurable:!0,writable:!0,value:b})}};$jscomp.polyfill("Array.prototype.keys",function(a){return a?a:function(){return $jscomp.iteratorFromArray(this,function(a){return a})}},"es6","es3"); | ||||
| $jscomp.makeIterator=function(a){$jscomp.initSymbolIterator();var b=a[Symbol.iterator];return b?b.call(a):$jscomp.arrayIterator(a)};$jscomp.FORCE_POLYFILL_PROMISE=!1; | ||||
| $jscomp.polyfill("Promise",function(a){function b(){this.batch_=null}function f(d){return d instanceof c?d:new c(function(a,b){a(d)})}if(a&&!$jscomp.FORCE_POLYFILL_PROMISE)return a;b.prototype.asyncExecute=function(a){null==this.batch_&&(this.batch_=[],this.asyncExecuteBatch_());this.batch_.push(a);return this};b.prototype.asyncExecuteBatch_=function(){var a=this;this.asyncExecuteFunction(function(){a.executeBatch_()})};var e=$jscomp.global.setTimeout;b.prototype.asyncExecuteFunction=function(a){e(a, | ||||
| 0)};b.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var a=this.batch_;this.batch_=[];for(var g=0;g<a.length;++g){var b=a[g];a[g]=null;try{b()}catch(k){this.asyncThrow_(k)}}}this.batch_=null};b.prototype.asyncThrow_=function(a){this.asyncExecuteFunction(function(){throw a;})};var c=function(a){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];var d=this.createResolveAndReject_();try{a(d.resolve,d.reject)}catch(h){d.reject(h)}};c.prototype.createResolveAndReject_= | ||||
| function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c=!1;return{resolve:a(this.resolveTo_),reject:a(this.reject_)}};c.prototype.resolveTo_=function(a){if(a===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(a instanceof c)this.settleSameAsPromise_(a);else{a:switch(typeof a){case "object":var d=null!=a;break a;case "function":d=!0;break a;default:d=!1}d?this.resolveToNonPromiseObj_(a):this.fulfill_(a)}};c.prototype.resolveToNonPromiseObj_=function(a){var d= | ||||
| 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_NAME="myMPD-cache-v1",urlsToCache="/ /player.html /css/bootstrap.min.css /css/mpd.css /js/bootstrap-native-v4.min.js /js/mpd.js /js/player.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_NAME).then(function(a){console.log("Opened cache");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){var b=["myMPD-cache-v1"];a.waitUntil(caches.keys().then(function(a){return Promise.all(a.map(function(a){if(-1===b.indexOf(a))return caches.delete(a)}))}))}); | ||||
							
								
								
									
										47
									
								
								mkrelease.sh
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								mkrelease.sh
									
									
									
									
									
								
							| @@ -2,26 +2,35 @@ | ||||
|  | ||||
| if [ -f buildtools/closure-compiler.jar ] | ||||
| then | ||||
|   echo "Minifying javascript" | ||||
|   [ htdocs/js/player.js -nt htdocs/js/player.min.js ] && \ | ||||
|     java -jar buildtools/closure-compiler.jar htdocs/js/player.js > htdocs/js/player.min.js | ||||
|   [ htdocs/js/mpd.js -nt  htdocs/js/mpd.min.js ] && \ | ||||
|     java -jar buildtools/closure-compiler.jar htdocs/js/mpd.js > htdocs/js/mpd.min.js | ||||
|   [ htdocs/sw.js -nt htdocs/sw.min.js ] && \ | ||||
|     java -jar buildtools/closure-compiler.jar htdocs/sw.js > htdocs/sw.min.js | ||||
| else | ||||
|   echo "buildtools/closure-compiler.jar not found, using non-minified files" | ||||
|   [ htdocs/js/player.js -nt htdocs/js/player.min.js ] && \ | ||||
|     cp htdocs/js/player.js htdocs/js/player.min.js | ||||
|   [ htdocs/js/mpd.js -nt  htdocs/js/mpd.min.js ] && \ | ||||
|     cp htdocs/js/mpd.js  htdocs/js/mpd.min.js | ||||
|   [ htdocs/sw.js -nt htdocs/sw.min.js ] && \ | ||||
|     cp htdocs/sw.js htdocs/sw.min.js     | ||||
| fi | ||||
|  | ||||
| if [ -f buildtools/closure-stylesheets.jar ] | ||||
| then | ||||
|   echo "Minifying stylesheets" | ||||
|   [ htdocs/css/mpd.css -nt htdocs/css/mpd.min.css ] && \ | ||||
|     java -jar buildtools/closure-stylesheets.jar htdocs/css/mpd.css > htdocs/css/mpd.min.css | ||||
| else | ||||
|   echo "buildtools/closure-stylesheets.jar not found, using non-minified files" | ||||
|   [ htdocs/css/mpd.css -nt htdocs/css/mpd.min.css ] && \ | ||||
|     cp htdocs/css/mpd.css htdocs/css/mpd.min.css     | ||||
| fi | ||||
|  | ||||
| echo "Compiling and installing mympd" | ||||
| [ -d release ] || mkdir release | ||||
| cd release | ||||
| cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE .. | ||||
| @@ -29,15 +38,45 @@ make | ||||
| sudo make install | ||||
| cd .. | ||||
|  | ||||
| echo "Replacing javascript and stylesheets with minified files" | ||||
| sudo sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' /usr/share/mympd/htdocs/index.html | ||||
| sudo sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/player\.js/player\.min\.js/' /usr/share/mympd/htdocs/player.html | ||||
| sudo sed -i -e 's/mpd\.css/mpd\.min\.css/' -e 's/mpd\.js/mpd\.min\.js/' -e 's/player\.js/player\.min\.js/' /usr/share/mympd/htdocs/sw.min.js | ||||
| sudo sed -i -e 's/\/sw\.js/\/sw\.min\.js/' /usr/share/mympd/htdocs/js/mpd.min.js | ||||
| echo "Minifying html" | ||||
| perl -i -pe 's/^\s*//gm; s/\s*$//gm' /usr/share/mympd/htdocs/index.html | ||||
| perl -i -pe 's/^\s*//gm; s/\s*$//gm' /usr/share/mympd/htdocs/player.html | ||||
|  | ||||
| echo "Fixing ownership of /var/lib/mympd" | ||||
| sudo chown nobody /var/lib/mympd | ||||
|  | ||||
| [ -d /etc/systemd/system ] && \ | ||||
|   sudo cp -v contrib/mympd.service /etc/systemd/system/ | ||||
| echo "Trying to link musicdir to library" | ||||
| if [ -f /etc/mpd.conf ] | ||||
| then | ||||
|   LIBRARY=$(grep music /etc/mpd.conf | awk {'print $2'}) | ||||
|   [ "$LIBRARY" != "" ] && [ ! -e /usr/share/mympd/htdocs/library ] && ln -s $LIBRARY /usr/share/mympd/htdocs/library | ||||
| else | ||||
|   echo "/etc/mpd.conf not found, you must link your musicdir manually to /usr/share/mympd/htdocs/library" | ||||
| fi | ||||
|  | ||||
| [ -d /etc/default ] && \ | ||||
|   sudo cp -v contrib/mympd.default /etc/default/mympd | ||||
| echo "Installing systemd service" | ||||
| if [ -d /etc/systemd/system ] | ||||
| then | ||||
|   if [ contrib/mympd.service -nt /etc/systemd/system/mympd.service ] | ||||
|   then | ||||
|     sudo cp -v contrib/mympd.service /etc/systemd/system/ | ||||
|     systemctl daemon-reload | ||||
|   fi | ||||
|   systemctl enable mympd   | ||||
| fi | ||||
|  | ||||
| if [ -d /etc/mympd/ssl ] | ||||
| then | ||||
|   echo "Certificates already created" | ||||
| else | ||||
|   echo "Creating certificates" | ||||
|   contrib/crcert.sh | ||||
| fi | ||||
|    | ||||
| echo "myMPD installed" | ||||
| echo "Edit /etc/mympd/options before starting mympd" | ||||
|   | ||||
| @@ -9479,7 +9479,6 @@ static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) { | ||||
|  */ | ||||
| static void mg_ws_close(struct mg_connection *nc, const void *data, | ||||
|                         size_t len) { | ||||
|   printf("SEND: %.*s\n",len,data); | ||||
|   if ((int) len == ~0) { | ||||
|     len = strlen((const char *) data); | ||||
|   } | ||||
|   | ||||
| @@ -4682,7 +4682,7 @@ int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf, | ||||
| #ifdef __GNUC__ | ||||
|     __attribute__((deprecated)); | ||||
| #endif | ||||
|  | ||||
| ; | ||||
|  | ||||
| /* | ||||
|  * Gets and parses the Authorization: Basic header | ||||
|   | ||||
							
								
								
									
										113
									
								
								src/mpd_client.c
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								src/mpd_client.c
									
									
									
									
									
								
							| @@ -80,7 +80,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|         case MPD_API_SET_SETTINGS: | ||||
|             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); | ||||
|             snprintf(tmpfile,200,"%s.tmp", mpd.statefile); | ||||
|             json_fprintf(tmpfile, "{ notificationWeb: %d, notificationPage: %d}", state.a, state.b); | ||||
|             rename(tmpfile,mpd.statefile); | ||||
|              | ||||
| @@ -113,8 +113,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|                 mpd_run_mixrampdelay(mpd.conn, float_buf); | ||||
|              | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { replaygain:%Q } }", &p_charbuf1); | ||||
|             if (je == 1) | ||||
|             { | ||||
|             if (je == 1) { | ||||
|                 mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL); | ||||
|                 struct mpd_pair *pair; | ||||
|                 while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { | ||||
| @@ -168,8 +167,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             break; | ||||
|         case MPD_API_MOVE_TRACK: | ||||
|             je = json_scanf(msg.p, msg.len, "{ data: { track:%u, pos:%u } }", &uint_buf1, &uint_buf2); | ||||
|             if (je == 2)         | ||||
|             { | ||||
|             if (je == 2) { | ||||
|                 uint_buf1 -= 1; | ||||
|                 uint_buf2 -= 1; | ||||
|                 mpd_run_move(mpd.conn, uint_buf1, uint_buf2); | ||||
| @@ -354,7 +352,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|     } | ||||
|     free(cmd); | ||||
|      | ||||
|     if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) | ||||
|     if (mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) | ||||
|     { | ||||
|         #ifdef DEBUG | ||||
|         fprintf(stdout,"Error: %s\n",mpd_connection_get_error_message(mpd.conn)); | ||||
| @@ -367,8 +365,8 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|             mpd.conn_state = MPD_FAILURE; | ||||
|     } | ||||
|  | ||||
|     if(n > 0) { | ||||
|         if(is_websocket(nc)) { | ||||
|     if (n > 0) { | ||||
|         if (is_websocket(nc)) { | ||||
|             #ifdef DEBUG | ||||
|             fprintf(stdout,"Send websocket response:\n %s\n",mpd.buf); | ||||
|             #endif | ||||
| @@ -383,21 +381,19 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) | ||||
|     }             | ||||
| } | ||||
|  | ||||
| int mympd_close_handler(struct mg_connection *c) | ||||
| { | ||||
| int mympd_close_handler(struct mg_connection *c) { | ||||
|     /* Cleanup session data */ | ||||
|     if(c->user_data) | ||||
|     if (c->user_data) | ||||
|         free(c->user_data); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int mympd_notify_callback(struct mg_connection *c, const char *param) { | ||||
|     size_t n; | ||||
|     if(!is_websocket(c)) | ||||
|     if (!is_websocket(c)) | ||||
|         return 0; | ||||
|  | ||||
|     if(param) | ||||
|     { | ||||
|     if (param) { | ||||
|         /* error message? */ | ||||
|         n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",param); | ||||
|         #ifdef DEBUG | ||||
| @@ -419,15 +415,13 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) { | ||||
|         #endif | ||||
|         mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     else { | ||||
|         #ifdef DEBUG | ||||
|         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) | ||||
|         { | ||||
|         if(s->song_id != mpd.song_id) { | ||||
|             n=mympd_put_current_song(mpd.buf); | ||||
|             #ifdef DEBUG | ||||
|             fprintf(stdout,"Notify: %s\n",mpd.buf); | ||||
| @@ -436,8 +430,7 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) { | ||||
|             s->song_id = mpd.song_id; | ||||
|         } | ||||
|          | ||||
|         if(s->queue_version != mpd.queue_version) | ||||
|         { | ||||
|         if(s->queue_version != mpd.queue_version) { | ||||
|             n=snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_queue\"}"); | ||||
|             #ifdef DEBUG | ||||
|             fprintf(stdout,"Notify: update_queue\n"); | ||||
| @@ -445,14 +438,11 @@ static int mympd_notify_callback(struct mg_connection *c, const char *param) { | ||||
|             mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, n); | ||||
|             s->queue_version = mpd.queue_version; | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void mympd_poll(struct mg_mgr *s) | ||||
| { | ||||
| void mympd_poll(struct mg_mgr *s) { | ||||
|     switch (mpd.conn_state) { | ||||
|         case MPD_DISCONNECTED: | ||||
|             /* Try to connect */ | ||||
| @@ -466,19 +456,16 @@ void mympd_poll(struct mg_mgr *s) | ||||
|  | ||||
|             if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { | ||||
|                 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)) | ||||
|                 { | ||||
|                 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)); | ||||
|                 } | ||||
|                 mpd.conn_state = MPD_FAILURE; | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if(mpd.password && !mpd_run_password(mpd.conn, mpd.password)) | ||||
|             { | ||||
|             if(mpd.password && !mpd_run_password(mpd.conn, mpd.password)) { | ||||
|                 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)) | ||||
|                 { | ||||
|                 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)); | ||||
|                 } | ||||
|                 mpd.conn_state = MPD_FAILURE; | ||||
| @@ -503,19 +490,17 @@ void mympd_poll(struct mg_mgr *s) | ||||
|  | ||||
|         case MPD_CONNECTED: | ||||
|             mpd.buf_size = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.queue_version); | ||||
|             for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) | ||||
|             { | ||||
|             for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { | ||||
|                 mympd_notify_callback(c, NULL); | ||||
|             } | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| char* mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) | ||||
| { | ||||
| char* mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { | ||||
|     char *str; | ||||
|     str = (char *)mpd_song_get_tag(song, tag, 0); | ||||
|     if (str == NULL){ | ||||
|     if (str == NULL) { | ||||
|         if (tag == MPD_TAG_TITLE) | ||||
|             str = basename((char *)mpd_song_get_uri(song)); | ||||
|         else | ||||
| @@ -524,8 +509,7 @@ char* mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) | ||||
|     return str; | ||||
| } | ||||
|  | ||||
| int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsigned *queue_version) | ||||
| { | ||||
| 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; | ||||
| @@ -593,8 +577,7 @@ int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsi | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_welcome(char *buffer) | ||||
| { | ||||
| int mympd_put_welcome(char *buffer) { | ||||
|     int len; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
|      | ||||
| @@ -604,8 +587,7 @@ int mympd_put_welcome(char *buffer) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_settings(char *buffer) | ||||
| { | ||||
| int mympd_put_settings(char *buffer) { | ||||
|     struct mpd_status *status; | ||||
|     char *replaygain; | ||||
|     int len; | ||||
| @@ -667,8 +649,7 @@ int mympd_put_settings(char *buffer) | ||||
| } | ||||
|  | ||||
|  | ||||
| int mympd_put_outputnames(char *buffer) | ||||
| { | ||||
| int mympd_put_outputnames(char *buffer) { | ||||
|     struct mpd_output *output; | ||||
|     int len; | ||||
|     int nr; | ||||
| @@ -697,8 +678,7 @@ int mympd_put_outputnames(char *buffer) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_get_cover(const char *uri, char *cover, int cover_len) | ||||
| { | ||||
| int mympd_get_cover(const char *uri, char *cover, int cover_len) { | ||||
|     char *path=strdup(uri); | ||||
|     int len; | ||||
|     if (strncasecmp("http:",path,5) == 0 ) { | ||||
| @@ -716,8 +696,7 @@ int mympd_get_cover(const char *uri, char *cover, int cover_len) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_current_song(char *buffer) | ||||
| { | ||||
| int mympd_put_current_song(char *buffer) { | ||||
|     struct mpd_song *song; | ||||
|     int len; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
| @@ -750,8 +729,7 @@ int mympd_put_current_song(char *buffer) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_songdetails(char *buffer, char *uri) | ||||
| { | ||||
| int mympd_put_songdetails(char *buffer, char *uri) { | ||||
|     struct mpd_entity *entity; | ||||
|     const struct mpd_song *song; | ||||
|     int len; | ||||
| @@ -783,8 +761,7 @@ int mympd_put_songdetails(char *buffer, char *uri) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_queue(char *buffer, unsigned int offset) | ||||
| { | ||||
| int mympd_put_queue(char *buffer, unsigned int offset) { | ||||
|     struct mpd_entity *entity; | ||||
|     unsigned long totalTime = 0; | ||||
|     unsigned long entity_count = 0; | ||||
| @@ -821,19 +798,19 @@ int mympd_put_queue(char *buffer, unsigned int offset) | ||||
|         mpd_entity_free(entity); | ||||
|     } | ||||
|  | ||||
|     len += json_printf(&out, "],totalTime: %d, totalEntities: %d, offset: %d, returnedEntities: %d }", | ||||
|     len += json_printf(&out, "],totalTime: %d, totalEntities: %d, offset: %d, returnedEntities: %d, queue_version: %d }", | ||||
|         totalTime, | ||||
|         entity_count, | ||||
|         offset, | ||||
|         entities_returned | ||||
|         entities_returned, | ||||
|         mpd.queue_version | ||||
|     ); | ||||
|      | ||||
|     if (len > MAX_SIZE) fprintf(stderr,"Buffer truncated\n"); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) | ||||
| { | ||||
| int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) { | ||||
|     struct mpd_entity *entity; | ||||
|     const struct mpd_playlist *pl; | ||||
|     unsigned int entity_count = 0; | ||||
| @@ -905,7 +882,7 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter | ||||
|                     if (plName != NULL) { | ||||
|                         plName ++; | ||||
|                     } else { | ||||
|                      plName = strdup(entityName); | ||||
|                         plName = strdup(entityName); | ||||
|                     } | ||||
|                     if (strncmp(filter,"-",1) == 0 || strncasecmp(filter,plName,1) == 0 || | ||||
|                         ( strncmp(filter,"0",1) == 0 && isalpha(*plName) == 0 ) | ||||
| @@ -941,8 +918,7 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter) | ||||
| { | ||||
| int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter) { | ||||
|     struct mpd_pair *pair; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
| @@ -994,8 +970,7 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char * | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) | ||||
| { | ||||
| int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) { | ||||
|     struct mpd_song *song; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
| @@ -1046,8 +1021,7 @@ int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) | ||||
| { | ||||
| int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { | ||||
|     struct mpd_playlist *pl; | ||||
|     unsigned int entity_count = 0; | ||||
|     unsigned int entities_returned = 0; | ||||
| @@ -1093,8 +1067,7 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) | ||||
| { | ||||
| int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { | ||||
|     struct mpd_song *song; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
| @@ -1147,8 +1120,7 @@ int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *sear | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) | ||||
| { | ||||
| int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) { | ||||
|     struct mpd_song *song; | ||||
|     int len; | ||||
|     struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); | ||||
| @@ -1178,8 +1150,7 @@ int mympd_search_add(char *buffer,char *mpdtagtype, char *searchstr) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) | ||||
| { | ||||
| int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { | ||||
|     struct mpd_song *song; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
| @@ -1232,8 +1203,7 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_get_stats(char *buffer) | ||||
| { | ||||
| int mympd_get_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]; | ||||
| @@ -1262,8 +1232,7 @@ int mympd_get_stats(char *buffer) | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| void mympd_disconnect() | ||||
| { | ||||
| void mympd_disconnect() { | ||||
|     mpd.conn_state = MPD_DISCONNECT; | ||||
|     mympd_poll(NULL); | ||||
| } | ||||
|   | ||||
							
								
								
									
										112
									
								
								src/mympd.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/mympd.c
									
									
									
									
									
								
							| @@ -37,6 +37,7 @@ | ||||
| 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 | ||||
| @@ -44,14 +45,14 @@ static void signal_handler(int 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 */ | ||||
|     } | ||||
| } | ||||
| @@ -82,7 +83,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | ||||
|         case MG_EV_CLOSE: { | ||||
|             if (is_websocket(nc)) { | ||||
|               #ifdef DEBUG | ||||
|               fprintf(stdout,"Websocket connection closed\n"); | ||||
|               printf("Websocket connection closed\n"); | ||||
|               #endif | ||||
|               mympd_close_handler(nc); | ||||
|             } | ||||
| @@ -96,35 +97,59 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data) { | ||||
|     switch(ev) { | ||||
|         case MG_EV_HTTP_REQUEST: { | ||||
|  | ||||
|             printf("Redirecting to %s\n", s_redirect); | ||||
|             mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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"; | ||||
|     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); | ||||
|  | ||||
|     static struct option long_options[] = { | ||||
|         {"host",         required_argument, 0, 'h'}, | ||||
|         {"port",         required_argument, 0, 'p'}, | ||||
|         {"webport",      required_argument, 0, 'w'}, | ||||
|         {"user",         required_argument, 0, 'u'}, | ||||
|         {"version",      no_argument,       0, 'v'}, | ||||
|         {"help",         no_argument,       0,  0 }, | ||||
|         {"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, "D:h:p:w:u:vm:s:i:c:t:", | ||||
|     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': | ||||
| @@ -139,6 +164,18 @@ int main(int argc, char **argv) | ||||
|             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; | ||||
| @@ -155,7 +192,7 @@ int main(int argc, char **argv) | ||||
|             case 'v': | ||||
|                 fprintf(stdout, "myMPD  %d.%d.%d\n" | ||||
|                         "Copyright (C) 2018 Juergen Mang <mail@jcgames.de>\n" | ||||
|                         "Built " __DATE__ " "__TIME__ "\n", | ||||
|                         "Built " __DATE__ " "__TIME__"\n", | ||||
|                         MYMPD_VERSION_MAJOR, MYMPD_VERSION_MINOR, MYMPD_VERSION_PATCH); | ||||
|                 return EXIT_SUCCESS; | ||||
|                 break; | ||||
| @@ -163,7 +200,11 @@ int main(int argc, char **argv) | ||||
|                 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 [8080]\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" | ||||
| @@ -184,10 +225,29 @@ int main(int argc, char **argv) | ||||
|      | ||||
|     mg_mgr_init(&mgr, NULL); | ||||
|  | ||||
|     nc = mg_bind(&mgr, webport, ev_handler); | ||||
|     if (nc == NULL) { | ||||
|        fprintf(stderr, "Error starting server on port %s\n", webport); | ||||
|        return EXIT_FAILURE; | ||||
|     if (ssl == true) { | ||||
|         snprintf(s_redirect, 200, "https://%s:%s/", hostname, sslport); | ||||
|         nc_http = mg_bind(&mgr, webport, ev_handler_http); | ||||
|         if (nc_http == NULL) { | ||||
|            fprintf(stderr, "Error starting server on port %s\n", 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.error_string = &err; | ||||
|         nc = mg_bind_opt(&mgr, sslport, ev_handler, bind_opts); | ||||
|         if (nc == NULL) { | ||||
|             fprintf(stderr, "Error starting server on port %s: %s\n", sslport, err); | ||||
|             return EXIT_FAILURE; | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         nc = mg_bind(&mgr, webport, ev_handler); | ||||
|         if (nc == NULL) { | ||||
|            fprintf(stderr, "Error starting server on port %s\n", webport ); | ||||
|            return EXIT_FAILURE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if(run_as_user != NULL) { | ||||
| @@ -197,7 +257,7 @@ int main(int argc, char **argv) | ||||
|             printf("Unknown user\n"); | ||||
|             return EXIT_FAILURE; | ||||
|         } else if (setgid(pw->pw_gid) != 0) { | ||||
|             printf("setgid() failed"); | ||||
|             printf("setgid() failed\n"); | ||||
|             return EXIT_FAILURE; | ||||
|         } else if (setuid(pw->pw_uid) != 0) { | ||||
|             printf("setuid() failed\n"); | ||||
| @@ -211,15 +271,21 @@ int main(int argc, char **argv) | ||||
|       return EXIT_FAILURE; | ||||
|     } | ||||
|      | ||||
|     if (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 port %s\n", webport); | ||||
|     while (s_signal_received == 0) { | ||||
|         mg_mgr_poll(&mgr, 200); | ||||
|         current_timer = time(NULL); | ||||
|         if(current_timer - last_timer) | ||||
|         { | ||||
|         if (current_timer - last_timer) { | ||||
|             last_timer = current_timer; | ||||
|             mympd_poll(&mgr); | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jürgen Mang
					Jürgen Mang