mirror of
https://github.com/SuperBFG7/ympd
synced 2024-12-26 19:10:25 +00:00
Merge pull request #16 from jcorporation/backend-upgrade
Merge Backend upgrade branche
This commit is contained in:
commit
17987ada08
@ -2,19 +2,18 @@ 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 "2")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "3")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "3")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES RELEASE)
|
||||
set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs")
|
||||
set(DEBUG "OFF")
|
||||
else()
|
||||
set(ASSETS_PATH "${PROJECT_SOURCE_DIR}/htdocs")
|
||||
set(DEBUG "ON")
|
||||
endif()
|
||||
|
||||
option(WITH_IPV6 "enable IPv6 support" ON)
|
||||
option(WITH_SSL "enable SSL support" ON)
|
||||
|
||||
find_package(LibMPDClient REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@ -23,38 +22,36 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic")
|
||||
if(WITH_IPV6)
|
||||
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6)
|
||||
endif()
|
||||
if(WITH_SSL)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
# list(APPEND LIB_LIST ${OPENSSL_LIBRARIES})
|
||||
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL)
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic ")
|
||||
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")
|
||||
|
||||
file(GLOB RESOURCES
|
||||
RELATIVE ${PROJECT_SOURCE_DIR}
|
||||
htdocs/js/*
|
||||
htdocs/assets/*
|
||||
htdocs/css/*.css
|
||||
htdocs/fonts/*
|
||||
htdocs/index.html
|
||||
htdocs/player.html
|
||||
)
|
||||
if(WITH_IPV6)
|
||||
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS)
|
||||
endif()
|
||||
|
||||
set(SOURCES
|
||||
src/mympd.c
|
||||
src/mpd_client.c
|
||||
src/mongoose.c
|
||||
src/json_encode.c
|
||||
src/mongoose/mongoose.c
|
||||
src/frozen/frozen.c
|
||||
)
|
||||
|
||||
add_executable(mympd ${SOURCES})
|
||||
target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES})
|
||||
target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
install(TARGETS mympd DESTINATION bin)
|
||||
install(FILES mympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
|
||||
install(DIRECTORY htdocs DESTINATION share/${PROJECT_NAME})
|
||||
install(FILES htdocs/index.html DESTINATION share/${PROJECT_NAME}/htdocs/)
|
||||
install(FILES htdocs/player.html DESTINATION share/${PROJECT_NAME}/htdocs/)
|
||||
install(FILES htdocs/js/modernizr-custom.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/js/player.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/js/bootstrap.bundle.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/js/bootstrap-notify.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/js/bootstrap-slider.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/js/jquery-3.3.1.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/js/mpd.min.js DESTINATION share/${PROJECT_NAME}/htdocs/js/)
|
||||
install(FILES htdocs/css/bootstrap.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/)
|
||||
install(FILES htdocs/css/bootstrap-slider.min.css DESTINATION share/${PROJECT_NAME}/htdocs/css/)
|
||||
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}/)
|
||||
|
35
README.md
35
README.md
@ -5,7 +5,7 @@ myMPD is a lightweight MPD web client that runs without a dedicated webserver or
|
||||
It's tuned for minimal resource usage and requires only very litte dependencies.
|
||||
myMPD is a fork of ympd.
|
||||
|
||||
This fork provides a reworked ui based on Bootstrap 4.
|
||||
This fork provides a reworked ui based on Bootstrap 4 and a modernized backend.
|
||||
|
||||
![image](https://jcgames.de/stuff/myMPD/screenshots.gif)
|
||||
|
||||
@ -16,7 +16,6 @@ UI Components
|
||||
- Bootstrap Slider: https://github.com/seiyria/bootstrap-slider
|
||||
- Material Design Icons: https://material.io/tools/icons/?style=baseline
|
||||
- jQuery: https://jquery.com/
|
||||
- js-cookie: https://github.com/js-cookie/js-cookie
|
||||
|
||||
Backend
|
||||
-------
|
||||
@ -27,52 +26,32 @@ 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, libmpdclient (dev), and OpenSSL (dev) are available from all major distributions.
|
||||
2. create build directory ```cd /path/to/src; mkdir build; cd build```
|
||||
3. create makefile ```cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE```
|
||||
4. build ```make```
|
||||
5. install ```sudo make install```
|
||||
6. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories
|
||||
7. Configure your mpd with http stream output to use the local player
|
||||
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
|
||||
|
||||
Run flags
|
||||
---------
|
||||
```
|
||||
Usage: ./mympd [OPTION]...
|
||||
|
||||
-D, --digest <htdigest> path to htdigest file for authorization
|
||||
(realm mympd) [no authorization]
|
||||
-h, --host <host> connect to mpd at host [localhost]
|
||||
-p, --port <port> connect to mpd at port [6600]
|
||||
-l, --localport <port> skip authorization for local port
|
||||
-w, --webport [ip:]<port> listen interface/port for webserver [8080]
|
||||
-w, --webport <port> listen port for webserver [80]
|
||||
-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 Support
|
||||
-----------
|
||||
To run myMPD with SSL support:
|
||||
|
||||
- create a certificate (key and cert in the same file), example:
|
||||
```
|
||||
# openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1000 -nodes -sha256
|
||||
# cat key.pem cert.pem > ssl.pem
|
||||
```
|
||||
- tell myMPD to use a webport using SSL and where to find the certificate:
|
||||
```
|
||||
# ./mympd -w "ssl://8081:/path/to/ssl.pem"
|
||||
```
|
||||
|
||||
Copyright
|
||||
---------
|
||||
ympd: 2013-2014 <andy@ndyk.de>
|
||||
|
@ -5,5 +5,4 @@ MPD_PASSWORD=
|
||||
WEB_PORT=80
|
||||
MYMPD_USER=nobody
|
||||
COVERIMAGE=--coverimage folder.jpg
|
||||
#DIGEST=--digest /path/to/htdigest
|
||||
#LOCALPORT=--localport 80
|
||||
STATEFILE=--statefile /var/lib/mympd/mympd.state
|
@ -8,11 +8,10 @@ Environment=MPD_PORT=6600
|
||||
Environment=MPD_PASSWORD=
|
||||
Environment=WEB_PORT=80
|
||||
Environment=MYMPD_USER=nobody
|
||||
Environment=DIGEST=
|
||||
Environment=LOCALPORT=
|
||||
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 $DIGEST $LOCALPORT
|
||||
ExecStart=/usr/bin/mympd --user $MYMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $COVERIMAGE $STATEFILE
|
||||
Type=simple
|
||||
|
||||
[Install]
|
||||
|
321
htdocs/css/bootstrap-slider.css
vendored
321
htdocs/css/bootstrap-slider.css
vendored
@ -1,321 +0,0 @@
|
||||
/*! =======================================================
|
||||
VERSION 10.0.2
|
||||
========================================================= */
|
||||
/*! =========================================================
|
||||
* bootstrap-slider.js
|
||||
*
|
||||
* Maintainers:
|
||||
* Kyle Kemp
|
||||
* - Twitter: @seiyria
|
||||
* - Github: seiyria
|
||||
* Rohit Kalkur
|
||||
* - Twitter: @Rovolutionary
|
||||
* - Github: rovolution
|
||||
*
|
||||
* =========================================================
|
||||
*
|
||||
* bootstrap-slider is released under the MIT License
|
||||
* Copyright (c) 2017 Kyle Kemp, Rohit Kalkur, and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* ========================================================= */
|
||||
.slider {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
.slider.slider-horizontal {
|
||||
width: 210px;
|
||||
height: 20px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-track {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
margin-top: -5px;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-selection,
|
||||
.slider.slider-horizontal .slider-track-low,
|
||||
.slider.slider-horizontal .slider-track-high {
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick,
|
||||
.slider.slider-horizontal .slider-handle {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick.triangle,
|
||||
.slider.slider-horizontal .slider-handle.triangle {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
border-width: 0 10px 10px 10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-bottom-color: #2e6da4;
|
||||
margin-top: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick-container {
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick-label-container {
|
||||
white-space: nowrap;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick-label-container .slider-tick-label {
|
||||
padding-top: 4px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
.slider.slider-horizontal .tooltip {
|
||||
-ms-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.slider.slider-horizontal.slider-rtl .slider-track {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
.slider.slider-horizontal.slider-rtl .slider-tick,
|
||||
.slider.slider-horizontal.slider-rtl .slider-handle {
|
||||
margin-left: initial;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.slider.slider-horizontal.slider-rtl .slider-tick-container {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
.slider.slider-horizontal.slider-rtl .tooltip {
|
||||
-ms-transform: translateX(50%);
|
||||
transform: translateX(50%);
|
||||
}
|
||||
.slider.slider-vertical {
|
||||
height: 210px;
|
||||
width: 20px;
|
||||
}
|
||||
.slider.slider-vertical .slider-track {
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
left: 25%;
|
||||
top: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-selection {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-track-low,
|
||||
.slider.slider-vertical .slider-track-high {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick,
|
||||
.slider.slider-vertical .slider-handle {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick.triangle,
|
||||
.slider.slider-vertical .slider-handle.triangle {
|
||||
border-width: 10px 0 10px 10px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border-left-color: #2e6da4;
|
||||
border-right-color: #2e6da4;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick-label-container {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick-label-container .slider-tick-label {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.slider.slider-vertical .tooltip {
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.slider.slider-vertical.slider-rtl .slider-track {
|
||||
left: initial;
|
||||
right: 25%;
|
||||
}
|
||||
.slider.slider-vertical.slider-rtl .slider-selection {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
.slider.slider-vertical.slider-rtl .slider-tick.triangle,
|
||||
.slider.slider-vertical.slider-rtl .slider-handle.triangle {
|
||||
border-width: 10px 10px 10px 0;
|
||||
}
|
||||
.slider.slider-vertical.slider-rtl .slider-tick-label-container .slider-tick-label {
|
||||
padding-left: initial;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.slider.slider-disabled .slider-handle {
|
||||
background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%);
|
||||
background-image: -o-linear-gradient(top, #dfdfdf 0%, #bebebe 100%);
|
||||
background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0);
|
||||
}
|
||||
.slider.slider-disabled .slider-track {
|
||||
background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%);
|
||||
background-image: -o-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%);
|
||||
background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.slider input {
|
||||
display: none;
|
||||
}
|
||||
.slider .tooltip.top {
|
||||
margin-top: -36px;
|
||||
}
|
||||
.slider .tooltip-inner {
|
||||
white-space: nowrap;
|
||||
max-width: none;
|
||||
}
|
||||
.slider .hide {
|
||||
display: none;
|
||||
}
|
||||
.slider-track {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-selection {
|
||||
position: absolute;
|
||||
background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-selection.tick-slider-selection {
|
||||
background-image: -webkit-linear-gradient(top, #8ac1ef 0%, #82b3de 100%);
|
||||
background-image: -o-linear-gradient(top, #8ac1ef 0%, #82b3de 100%);
|
||||
background-image: linear-gradient(to bottom, #8ac1ef 0%, #82b3de 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef', endColorstr='#ff82b3de', GradientType=0);
|
||||
}
|
||||
.slider-track-low,
|
||||
.slider-track-high {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #337ab7;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
filter: none;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
.slider-handle.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.slider-handle.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-handle.custom {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-handle.custom::before {
|
||||
line-height: 20px;
|
||||
font-size: 20px;
|
||||
content: '\2605';
|
||||
color: #726204;
|
||||
}
|
||||
.slider-tick {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
filter: none;
|
||||
opacity: 0.8;
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
.slider-tick.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.slider-tick.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-tick.custom {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-tick.custom::before {
|
||||
line-height: 20px;
|
||||
font-size: 20px;
|
||||
content: '\2605';
|
||||
color: #726204;
|
||||
}
|
||||
.slider-tick.in-selection {
|
||||
background-image: -webkit-linear-gradient(top, #8ac1ef 0%, #82b3de 100%);
|
||||
background-image: -o-linear-gradient(top, #8ac1ef 0%, #82b3de 100%);
|
||||
background-image: linear-gradient(to bottom, #8ac1ef 0%, #82b3de 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef', endColorstr='#ff82b3de', GradientType=0);
|
||||
opacity: 1;
|
||||
}
|
8981
htdocs/css/bootstrap.css
vendored
8981
htdocs/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1
htdocs/css/bootstrap.min.css
vendored
1
htdocs/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -82,8 +82,12 @@ tbody {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
#queue-buttons, #BrowsePlaylistsButtons, #SearchButtons, #BrowseFilesystemButtons, #BrowseDatabaseButtons {
|
||||
margin-bottom:20px;
|
||||
.card-toolbar {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.card-toolbar > div, .card-toolbar > form {
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@ -135,10 +139,6 @@ main {
|
||||
color:#6c757d !important;
|
||||
}
|
||||
|
||||
#filter-toolbar {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
#btn-outputs-block > button {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
@ -194,8 +194,13 @@ main {
|
||||
|
||||
.card-img-top {
|
||||
min-height:250px;
|
||||
background-image:url('/assets/coverimage-notavailable.png');
|
||||
background-repeat:no-repeat;
|
||||
background-color:#d45500;
|
||||
background-color:#eee;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
button.active {
|
||||
color: #fff;
|
||||
background-color: #28a745 !important;
|
||||
border-color: #28a745 !important;
|
||||
}
|
1
htdocs/css/mpd.min.css
vendored
Normal file
1
htdocs/css/mpd.min.css
vendored
Normal file
@ -0,0 +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}#volumeslider{width:104px}button{overflow:hidden}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}#counter{font-size:22px;margin-top:-2px;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}}tbody{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'}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}.slider-selection{background:#28a745!important}#progressbar .slider-track{height:20px!important}#progressbar{width:100%}#volumebar{width:160px}.slider-handle{visibility:hidden!important}[data-notify="title"]{font-size:120%}.header-logo{font-size:2rem;float:left;margin-right:5px}#BrowseFilesystemFilterLetters>button,#BrowseDatabaseFilterLetters>button,#BrowsePlaylistsFilterLetters>button{min-width:28px}.col-md{min-width:260px;max-width:260px}.card-img-top{min-height:250px;background-repeat:no-repeat;background-color:#eee;cursor:pointer}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}
|
@ -7,11 +7,11 @@
|
||||
<meta name="author" content="mail@jcgames.de">
|
||||
<title>myMPD</title>
|
||||
|
||||
<link href="css/bootstrap.css" rel="stylesheet">
|
||||
<link href="css/bootstrap-slider.css" rel="stylesheet">
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="css/bootstrap-slider.min.css" rel="stylesheet">
|
||||
<link href="css/mpd.css" rel="stylesheet">
|
||||
<link href="assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">
|
||||
<script src="js/modernizr-custom.js"></script>
|
||||
<script src="js/modernizr-custom.min.js"></script>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
|
||||
@ -20,7 +20,7 @@
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand navbar-dark fixed-top bg-dark">
|
||||
<div class="dropdown col-auto mr-auto" id="mainMenu">
|
||||
<div class="dropdown col-auto mr-auto pl-0" id="mainMenu">
|
||||
<a class="dropdown-toggle navbar-brand" data-toggle="dropdown" href="#">
|
||||
<span class="material-icons header-logo">play_circle_outline</span>myMPD
|
||||
</a>
|
||||
@ -31,28 +31,28 @@
|
||||
<div class="dropdown-divider"></div>
|
||||
<a id="nav-addstream" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#addstream">Add Stream</a>
|
||||
<a id="nav-updatedb" class="dropdown-item text-light bg-dark" href="#" onclick="updateDB(event);">Update Database</a>
|
||||
<a id="nav-localplayer" class="dropdown-item text-light bg-dark" href="#" data-toggle="dropdown" onclick="window.open('/player.html','LocalPlayer');">Local Player</a>
|
||||
<a id="nav-localplayer" class="dropdown-item text-light bg-dark" href="#" data-toggle="dropdown" onclick="window.open('/player.html#'+settings.mpdstream,'LocalPlayer');">Local Player</a>
|
||||
<a id="nav-settings" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#settings">Settings</a>
|
||||
<a id="nav-about" class="dropdown-item text-light bg-dark" href="#" data-toggle="modal" data-target="#about">About</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-toolbar col-auto" role="toolbar">
|
||||
<div class="btn-toolbar col-auto pl-0 pr-0" role="toolbar">
|
||||
<div class="btn-group mr-2" role="group">
|
||||
<button id="btnPrev" type="button" class="btn btn-secondary" onclick="socket.send('MPD_API_SET_PREV');">
|
||||
<button id="btnPrev" type="button" class="btn btn-secondary pl-2 pr-2" onclick="clickPrev();;">
|
||||
<span class="material-icons">skip_previous</span>
|
||||
</button>
|
||||
<button id="btnStop" type="button" class="btn btn-secondary" onclick="clickStop();">
|
||||
<button id="btnStop" type="button" class="btn btn-secondary pl-2 pr-2" onclick="clickStop();">
|
||||
<span class="material-icons">stop</span>
|
||||
</button>
|
||||
<button id="btnPlay" type="button" class="btn btn-secondary" onclick="clickPlay();">
|
||||
<button id="btnPlay" type="button" class="btn btn-secondary pl-2 pr-2" onclick="clickPlay();">
|
||||
<span class="material-icons">pause</span>
|
||||
</button>
|
||||
<button id="btnNext" type="button" class="btn btn-secondary" onclick="socket.send('MPD_API_SET_NEXT');">
|
||||
<button id="btnNext" type="button" class="btn btn-secondary pl-2 pr-2" onclick="clickNext();">
|
||||
<span class="material-icons">skip_next</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle pl-2 pr-2" type="button" data-toggle="dropdown">
|
||||
<span id="volume-icon" class="material-icons">volume_up</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right bg-dark">
|
||||
@ -102,7 +102,7 @@
|
||||
<span id="panel-heading-queue" class="text pull-right"></span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="btn-toolbar collapse show" id="queue-buttons" role="toolbar">
|
||||
<div class="btn-toolbar collapse show card-toolbar" id="queue-buttons" role="toolbar">
|
||||
<div id="trashmode" class="btn-group mr-2">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"><span class="material-icons">delete</span></button>
|
||||
<div class="dropdown-menu bg-dark px-2" id="trashmodebtns">
|
||||
@ -111,7 +111,7 @@
|
||||
<span class="material-icons float-left">vertical_align_top</span>
|
||||
<span class="ml-3">Delete upward</span>
|
||||
</button>
|
||||
<button id="btntrashmodesingle" type="button" class="btn btn-success btn-block">
|
||||
<button id="btntrashmodesingle" type="button" class="btn btn-secondary active btn-block">
|
||||
<span class="material-icons float-left">delete</span>
|
||||
<span class="ml-3">Delete single</span>
|
||||
</button>
|
||||
@ -142,7 +142,7 @@
|
||||
</button>
|
||||
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchqueuetag">
|
||||
<h6 class="dropdown-header text-light">Search in Tag</h6>
|
||||
<button type="button" class="btn btn-success btn-block">Any Tag</button>
|
||||
<button type="button" class="btn btn-secondary btn-block active">Any Tag</button>
|
||||
<button type="button" class="btn btn-secondary btn-block">Title</button>
|
||||
<button type="button" class="btn btn-secondary btn-block">Artist</button>
|
||||
<button type="button" class="btn btn-secondary btn-block">Album</button>
|
||||
@ -218,7 +218,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body hide" id="cardBrowsePlaylists">
|
||||
<div class="btn-toolbar collapse show" id="BrowsePlaylistsButtons" role="toolbar">
|
||||
<div class="btn-toolbar collapse show card-toolbar" id="BrowsePlaylistsButtons" role="toolbar">
|
||||
<div class="btn-group mr-2">
|
||||
<button id="BrowsePlaylistsFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button>
|
||||
<div class="dropdown-menu bg-dark px-2" id="BrowsePlaylistsFilterLetters">
|
||||
@ -273,7 +273,7 @@
|
||||
|
||||
<div class="card-body hide" id="cardBrowseDatabase">
|
||||
|
||||
<div class="btn-toolbar collapse show" id="BrowseDatabaseButtons" role="toolbar">
|
||||
<div class="btn-toolbar collapse show card-toolbar" id="BrowseDatabaseButtons" role="toolbar">
|
||||
<div class="btn-group mr-2">
|
||||
<button id="btnBrowseDatabaseArtist" type="button" class="btn btn-secondary hide">« Artists</button>
|
||||
</div>
|
||||
@ -332,7 +332,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body hide" id="cardBrowseFilesystem">
|
||||
<div class="btn-toolbar collapse show" id="BrowseFilesystemButtons" role="toolbar">
|
||||
<div class="btn-toolbar collapse show card-toolbar" id="BrowseFilesystemButtons" role="toolbar">
|
||||
<div class="btn-group mr-2 pull-right">
|
||||
<button id="BrowseFilesystemAddAllSongs" class="btn btn-secondary">Add all</button>
|
||||
</div>
|
||||
@ -401,7 +401,7 @@
|
||||
<span id="panel-heading-search" class="text pull-right"></span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="btn-toolbar collapse show" id="SearchButtons" role="toolbar">
|
||||
<div class="btn-toolbar collapse show card-toolbar" id="SearchButtons" role="toolbar">
|
||||
<form id="search2" role="search">
|
||||
<div class="input-group mr-2">
|
||||
<input type="text" class="form-control" placeholder="Search" id="searchstr2"/>
|
||||
@ -412,7 +412,7 @@
|
||||
</button>
|
||||
<div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchtags2">
|
||||
<h6 class="dropdown-header text-light">Search in Tag</h6>
|
||||
<button type="button" class="btn btn-success btn-block">Any Tag</button>
|
||||
<button type="button" class="btn btn-secondary active btn-block">Any Tag</button>
|
||||
<button type="button" class="btn btn-secondary btn-block">Title</button>
|
||||
<button type="button" class="btn btn-secondary btn-block">Artist</button>
|
||||
<button type="button" class="btn btn-secondary btn-block">Album</button>
|
||||
@ -510,8 +510,9 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="needs-validation" id="settingsFrm" novalidate>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<div class="form-group col-md-6" data-toggle="buttons">
|
||||
<button id="btnrandom" type="button" class="btn btn-secondary btn-block" title="Random">
|
||||
Random
|
||||
</button>
|
||||
@ -540,12 +541,13 @@
|
||||
<div class="input-group-text bg-secondary text-light border-secondary">Crossfade</div>
|
||||
</div>
|
||||
<input id="inputCrossfade" type="text" class="form-control border-secondary" value="">
|
||||
<div class="invalid-feedback">Must be a number.</div>
|
||||
</div>
|
||||
<div class="form-group input-group col-md-6 border-secondary">
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text bg-secondary text-light border-secondary">Replaygain</div>
|
||||
</div>
|
||||
<select id="selectReplaygain" class="form-control border-secondary">
|
||||
<select id="selectReplaygain" class="form-control custom-select border-secondary">
|
||||
<option value="off">Off</option>
|
||||
<option value="track">Track</option>
|
||||
<option value="album">Album</option>
|
||||
@ -558,12 +560,14 @@
|
||||
<div class="input-group-text bg-secondary text-light border-secondary">Mixramp DB</div>
|
||||
</div>
|
||||
<input id="inputMixrampdb" type="text" class="form-control border-secondary" value="">
|
||||
<div class="invalid-feedback">Must be a number.</div>
|
||||
</div>
|
||||
<div class="form-group input-group col-md-6 border-secondary">
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text bg-secondary text-light border-secondary">Mixramp Delay</div>
|
||||
</div>
|
||||
<input id="inputMixrampdelay" type="text" class="form-control border-secondary" value="">
|
||||
<div class="invalid-feedback">Must be a number.</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
@ -579,6 +583,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
@ -685,9 +690,7 @@
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<script src="js/jquery-3.3.1.min.js"></script>
|
||||
<script src="js/js.cookie-2.2.0.min.js"></script>
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
<script src="js/bootstrap-slider.min.js"></script>
|
||||
<script src="js/bootstrap-notify.min.js"></script>
|
||||
|
349
htdocs/js/bootstrap-notify.js
vendored
349
htdocs/js/bootstrap-notify.js
vendored
@ -1,349 +0,0 @@
|
||||
/*
|
||||
* Project: Bootstrap Notify = v3.1.3
|
||||
* Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
|
||||
* Author: Mouse0270 aka Robert McIntosh
|
||||
* License: MIT License
|
||||
* Website: https://github.com/mouse0270/bootstrap-growl
|
||||
*/
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node/CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
// Create the defaults once
|
||||
var defaults = {
|
||||
element: 'body',
|
||||
position: null,
|
||||
type: "info",
|
||||
allow_dismiss: true,
|
||||
newest_on_top: false,
|
||||
showProgressbar: false,
|
||||
placement: {
|
||||
from: "top",
|
||||
align: "right"
|
||||
},
|
||||
offset: 20,
|
||||
spacing: 10,
|
||||
z_index: 1031,
|
||||
delay: 5000,
|
||||
timer: 1000,
|
||||
url_target: '_blank',
|
||||
mouse_over: null,
|
||||
animate: {
|
||||
enter: 'animated fadeInDown',
|
||||
exit: 'animated fadeOutUp'
|
||||
},
|
||||
onShow: null,
|
||||
onShown: null,
|
||||
onClose: null,
|
||||
onClosed: null,
|
||||
icon_type: 'class',
|
||||
template: '<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'
|
||||
};
|
||||
|
||||
String.format = function() {
|
||||
var str = arguments[0];
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
function Notify ( element, content, options ) {
|
||||
// Setup Content of Notify
|
||||
var content = {
|
||||
content: {
|
||||
message: typeof content == 'object' ? content.message : content,
|
||||
title: content.title ? content.title : '',
|
||||
icon: content.icon ? content.icon : '',
|
||||
url: content.url ? content.url : '#',
|
||||
target: content.target ? content.target : '-'
|
||||
}
|
||||
};
|
||||
|
||||
options = $.extend(true, {}, content, options);
|
||||
this.settings = $.extend(true, {}, defaults, options);
|
||||
this._defaults = defaults;
|
||||
if (this.settings.content.target == "-") {
|
||||
this.settings.content.target = this.settings.url_target;
|
||||
}
|
||||
this.animations = {
|
||||
start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
|
||||
end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
|
||||
}
|
||||
|
||||
if (typeof this.settings.offset == 'number') {
|
||||
this.settings.offset = {
|
||||
x: this.settings.offset,
|
||||
y: this.settings.offset
|
||||
};
|
||||
}
|
||||
this.init();
|
||||
};
|
||||
|
||||
$.extend(Notify.prototype, {
|
||||
init: function () {
|
||||
var self = this;
|
||||
|
||||
this.buildNotify();
|
||||
if (this.settings.content.icon) {
|
||||
this.setIcon();
|
||||
}
|
||||
if (this.settings.content.url != "#") {
|
||||
this.styleURL();
|
||||
}
|
||||
this.placement();
|
||||
this.bind();
|
||||
|
||||
this.notify = {
|
||||
$ele: this.$ele,
|
||||
update: function(command, update) {
|
||||
var commands = {};
|
||||
if (typeof command == "string") {
|
||||
commands[command] = update;
|
||||
}else{
|
||||
commands = command;
|
||||
}
|
||||
for (var command in commands) {
|
||||
switch (command) {
|
||||
case "type":
|
||||
this.$ele.removeClass('alert-' + self.settings.type);
|
||||
this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
|
||||
self.settings.type = commands[command];
|
||||
this.$ele.addClass('alert-' + commands[command]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[command]);
|
||||
break;
|
||||
case "icon":
|
||||
var $icon = this.$ele.find('[data-notify="icon"]');
|
||||
if (self.settings.icon_type.toLowerCase() == 'class') {
|
||||
$icon.removeClass(self.settings.content.icon).addClass(commands[command]);
|
||||
}else{
|
||||
if (!$icon.is('img')) {
|
||||
$icon.find('img');
|
||||
}
|
||||
$icon.attr('src', commands[command]);
|
||||
}
|
||||
break;
|
||||
case "progress":
|
||||
var newDelay = self.settings.delay - (self.settings.delay * (commands[command] / 100));
|
||||
this.$ele.data('notify-delay', newDelay);
|
||||
this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[command]).css('width', commands[command] + '%');
|
||||
break;
|
||||
case "url":
|
||||
this.$ele.find('[data-notify="url"]').attr('href', commands[command]);
|
||||
break;
|
||||
case "target":
|
||||
this.$ele.find('[data-notify="url"]').attr('target', commands[command]);
|
||||
break;
|
||||
default:
|
||||
this.$ele.find('[data-notify="' + command +'"]').html(commands[command]);
|
||||
};
|
||||
}
|
||||
var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
|
||||
self.reposition(posX);
|
||||
},
|
||||
close: function() {
|
||||
self.close();
|
||||
}
|
||||
};
|
||||
},
|
||||
buildNotify: function () {
|
||||
var content = this.settings.content;
|
||||
this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
|
||||
this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
|
||||
if (!this.settings.allow_dismiss) {
|
||||
this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
|
||||
}
|
||||
if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
|
||||
this.$ele.find('[data-notify="progressbar"]').remove();
|
||||
}
|
||||
},
|
||||
setIcon: function() {
|
||||
if (this.settings.icon_type.toLowerCase() == 'class') {
|
||||
this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
|
||||
}else{
|
||||
if (this.$ele.find('[data-notify="icon"]').is('img')) {
|
||||
this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
|
||||
}else{
|
||||
this.$ele.find('[data-notify="icon"]').append('<img src="'+this.settings.content.icon+'" alt="Notify Icon" />');
|
||||
}
|
||||
}
|
||||
},
|
||||
styleURL: function() {
|
||||
this.$ele.find('[data-notify="url"]').css({
|
||||
backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
|
||||
height: '100%',
|
||||
left: '0px',
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
width: '100%',
|
||||
zIndex: this.settings.z_index + 1
|
||||
});
|
||||
this.$ele.find('[data-notify="dismiss"]').css({
|
||||
position: 'absolute',
|
||||
right: '10px',
|
||||
top: '5px',
|
||||
zIndex: this.settings.z_index + 2
|
||||
});
|
||||
},
|
||||
placement: function() {
|
||||
var self = this,
|
||||
offsetAmt = this.settings.offset.y,
|
||||
css = {
|
||||
display: 'inline-block',
|
||||
margin: '0px auto',
|
||||
position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
|
||||
transition: 'all .5s ease-in-out',
|
||||
zIndex: this.settings.z_index
|
||||
},
|
||||
hasAnimation = false,
|
||||
settings = this.settings;
|
||||
|
||||
$('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function() {
|
||||
return offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
|
||||
});
|
||||
if (this.settings.newest_on_top == true) {
|
||||
offsetAmt = this.settings.offset.y;
|
||||
}
|
||||
css[this.settings.placement.from] = offsetAmt+'px';
|
||||
|
||||
switch (this.settings.placement.align) {
|
||||
case "left":
|
||||
case "right":
|
||||
css[this.settings.placement.align] = this.settings.offset.x+'px';
|
||||
break;
|
||||
case "center":
|
||||
css.left = 0;
|
||||
css.right = 0;
|
||||
break;
|
||||
}
|
||||
this.$ele.css(css).addClass(this.settings.animate.enter);
|
||||
$.each(Array('webkit', 'moz', 'o', 'ms', ''), function(index, prefix) {
|
||||
self.$ele[0].style[prefix+'AnimationIterationCount'] = 1;
|
||||
});
|
||||
|
||||
$(this.settings.element).append(this.$ele);
|
||||
|
||||
if (this.settings.newest_on_top == true) {
|
||||
offsetAmt = (parseInt(offsetAmt)+parseInt(this.settings.spacing)) + this.$ele.outerHeight();
|
||||
this.reposition(offsetAmt);
|
||||
}
|
||||
|
||||
if ($.isFunction(self.settings.onShow)) {
|
||||
self.settings.onShow.call(this.$ele);
|
||||
}
|
||||
|
||||
this.$ele.one(this.animations.start, function(event) {
|
||||
hasAnimation = true;
|
||||
}).one(this.animations.end, function(event) {
|
||||
if ($.isFunction(self.settings.onShown)) {
|
||||
self.settings.onShown.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
if (!hasAnimation) {
|
||||
if ($.isFunction(self.settings.onShown)) {
|
||||
self.settings.onShown.call(this);
|
||||
}
|
||||
}
|
||||
}, 600);
|
||||
},
|
||||
bind: function() {
|
||||
var self = this;
|
||||
|
||||
this.$ele.find('[data-notify="dismiss"]').on('click', function() {
|
||||
self.close();
|
||||
})
|
||||
|
||||
this.$ele.mouseover(function(e) {
|
||||
$(this).data('data-hover', "true");
|
||||
}).mouseout(function(e) {
|
||||
$(this).data('data-hover', "false");
|
||||
});
|
||||
this.$ele.data('data-hover', "false");
|
||||
|
||||
if (this.settings.delay > 0) {
|
||||
self.$ele.data('notify-delay', self.settings.delay);
|
||||
var timer = setInterval(function() {
|
||||
var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
|
||||
if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over == "pause") || self.settings.mouse_over != "pause") {
|
||||
var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
|
||||
self.$ele.data('notify-delay', delay);
|
||||
self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
|
||||
}
|
||||
if (delay <= -(self.settings.timer)) {
|
||||
clearInterval(timer);
|
||||
self.close();
|
||||
}
|
||||
}, self.settings.timer);
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
var self = this,
|
||||
$successors = null,
|
||||
posX = parseInt(this.$ele.css(this.settings.placement.from)),
|
||||
hasAnimation = false;
|
||||
|
||||
this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
|
||||
self.reposition(posX);
|
||||
|
||||
if ($.isFunction(self.settings.onClose)) {
|
||||
self.settings.onClose.call(this.$ele);
|
||||
}
|
||||
|
||||
this.$ele.one(this.animations.start, function(event) {
|
||||
hasAnimation = true;
|
||||
}).one(this.animations.end, function(event) {
|
||||
$(this).remove();
|
||||
if ($.isFunction(self.settings.onClosed)) {
|
||||
self.settings.onClosed.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
if (!hasAnimation) {
|
||||
self.$ele.remove();
|
||||
if (self.settings.onClosed) {
|
||||
self.settings.onClosed(self.$ele);
|
||||
}
|
||||
}
|
||||
}, 600);
|
||||
},
|
||||
reposition: function(posX) {
|
||||
var self = this,
|
||||
notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
|
||||
$elements = this.$ele.nextAll(notifies);
|
||||
if (this.settings.newest_on_top == true) {
|
||||
$elements = this.$ele.prevAll(notifies);
|
||||
}
|
||||
$elements.each(function() {
|
||||
$(this).css(self.settings.placement.from, posX);
|
||||
posX = (parseInt(posX)+parseInt(self.settings.spacing)) + $(this).outerHeight();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$.notify = function ( content, options ) {
|
||||
var plugin = new Notify( this, content, options );
|
||||
return plugin.notify;
|
||||
};
|
||||
$.notifyDefaults = function( options ) {
|
||||
defaults = $.extend(true, {}, defaults, options);
|
||||
return defaults;
|
||||
};
|
||||
$.notifyClose = function( command ) {
|
||||
if (typeof command === "undefined" || command == "all") {
|
||||
$('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
|
||||
}else{
|
||||
$('[data-notify-position="'+command+'"]').find('[data-notify="dismiss"]').trigger('click');
|
||||
}
|
||||
};
|
||||
|
||||
}));
|
1878
htdocs/js/bootstrap-slider.js
vendored
1878
htdocs/js/bootstrap-slider.js
vendored
File diff suppressed because it is too large
Load Diff
6444
htdocs/js/bootstrap.bundle.js
vendored
6444
htdocs/js/bootstrap.bundle.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1
htdocs/js/bootstrap.bundle.min.js
vendored
1
htdocs/js/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,165 +0,0 @@
|
||||
/*!
|
||||
* JavaScript Cookie v2.2.0
|
||||
* https://github.com/js-cookie/js-cookie
|
||||
*
|
||||
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
|
||||
* Released under the MIT license
|
||||
*/
|
||||
;(function (factory) {
|
||||
var registeredInModuleLoader = false;
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
registeredInModuleLoader = true;
|
||||
}
|
||||
if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
registeredInModuleLoader = true;
|
||||
}
|
||||
if (!registeredInModuleLoader) {
|
||||
var OldCookies = window.Cookies;
|
||||
var api = window.Cookies = factory();
|
||||
api.noConflict = function () {
|
||||
window.Cookies = OldCookies;
|
||||
return api;
|
||||
};
|
||||
}
|
||||
}(function () {
|
||||
function extend () {
|
||||
var i = 0;
|
||||
var result = {};
|
||||
for (; i < arguments.length; i++) {
|
||||
var attributes = arguments[ i ];
|
||||
for (var key in attributes) {
|
||||
result[key] = attributes[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function init (converter) {
|
||||
function api (key, value, attributes) {
|
||||
var result;
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
if (arguments.length > 1) {
|
||||
attributes = extend({
|
||||
path: '/'
|
||||
}, api.defaults, attributes);
|
||||
|
||||
if (typeof attributes.expires === 'number') {
|
||||
var expires = new Date();
|
||||
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
|
||||
attributes.expires = expires;
|
||||
}
|
||||
|
||||
// We're using "expires" because "max-age" is not supported by IE
|
||||
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
|
||||
|
||||
try {
|
||||
result = JSON.stringify(value);
|
||||
if (/^[\{\[]/.test(result)) {
|
||||
value = result;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (!converter.write) {
|
||||
value = encodeURIComponent(String(value))
|
||||
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
|
||||
} else {
|
||||
value = converter.write(value, key);
|
||||
}
|
||||
|
||||
key = encodeURIComponent(String(key));
|
||||
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
|
||||
key = key.replace(/[\(\)]/g, escape);
|
||||
|
||||
var stringifiedAttributes = '';
|
||||
|
||||
for (var attributeName in attributes) {
|
||||
if (!attributes[attributeName]) {
|
||||
continue;
|
||||
}
|
||||
stringifiedAttributes += '; ' + attributeName;
|
||||
if (attributes[attributeName] === true) {
|
||||
continue;
|
||||
}
|
||||
stringifiedAttributes += '=' + attributes[attributeName];
|
||||
}
|
||||
return (document.cookie = key + '=' + value + stringifiedAttributes);
|
||||
}
|
||||
|
||||
// Read
|
||||
|
||||
if (!key) {
|
||||
result = {};
|
||||
}
|
||||
|
||||
// To prevent the for loop in the first place assign an empty array
|
||||
// in case there are no cookies at all. Also prevents odd result when
|
||||
// calling "get()"
|
||||
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||
var rdecode = /(%[0-9A-Z]{2})+/g;
|
||||
var i = 0;
|
||||
|
||||
for (; i < cookies.length; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var cookie = parts.slice(1).join('=');
|
||||
|
||||
if (!this.json && cookie.charAt(0) === '"') {
|
||||
cookie = cookie.slice(1, -1);
|
||||
}
|
||||
|
||||
try {
|
||||
var name = parts[0].replace(rdecode, decodeURIComponent);
|
||||
cookie = converter.read ?
|
||||
converter.read(cookie, name) : converter(cookie, name) ||
|
||||
cookie.replace(rdecode, decodeURIComponent);
|
||||
|
||||
if (this.json) {
|
||||
try {
|
||||
cookie = JSON.parse(cookie);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (key === name) {
|
||||
result = cookie;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
api.set = api;
|
||||
api.get = function (key) {
|
||||
return api.call(api, key);
|
||||
};
|
||||
api.getJSON = function () {
|
||||
return api.apply({
|
||||
json: true
|
||||
}, [].slice.call(arguments));
|
||||
};
|
||||
api.defaults = {};
|
||||
|
||||
api.remove = function (key, attributes) {
|
||||
api(key, '', extend(attributes, {
|
||||
expires: -1
|
||||
}));
|
||||
};
|
||||
|
||||
api.withConverter = init;
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
return init(function () {});
|
||||
}));
|
3
htdocs/js/js.cookie-2.2.0.min.js
vendored
3
htdocs/js/js.cookie-2.2.0.min.js
vendored
@ -1,3 +0,0 @@
|
||||
/*! js-cookie v2.2.0 | MIT */
|
||||
|
||||
!function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function e(){for(var e=0,n={};e<arguments.length;e++){var o=arguments[e];for(var t in o)n[t]=o[t]}return n}function n(o){function t(n,r,i){var c;if("undefined"!=typeof document){if(arguments.length>1){if("number"==typeof(i=e({path:"/"},t.defaults,i)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(e){}r=o.write?o.write(r,n):encodeURIComponent(r+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=(n=(n=encodeURIComponent(n+"")).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var s="";for(var f in i)i[f]&&(s+="; "+f,!0!==i[f]&&(s+="="+i[f]));return document.cookie=n+"="+r+s}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,u=0;u<p.length;u++){var l=p[u].split("="),C=l.slice(1).join("=");this.json||'"'!==C.charAt(0)||(C=C.slice(1,-1));try{var m=l[0].replace(d,decodeURIComponent);if(C=o.read?o.read(C,m):o(C,m)||C.replace(d,decodeURIComponent),this.json)try{C=JSON.parse(C)}catch(e){}if(n===m){c=C;break}n||(c[m]=C)}catch(e){}}return c}}return t.set=t,t.get=function(e){return t.call(t,e)},t.getJSON=function(){return t.apply({json:!0},[].slice.call(arguments))},t.defaults={},t.remove=function(n,o){t(n,"",e(o,{expires:-1}))},t.withConverter=n,t}return n(function(){})});
|
1312
htdocs/js/mpd.js
1312
htdocs/js/mpd.js
File diff suppressed because it is too large
Load Diff
85
htdocs/js/mpd.min.js
vendored
Normal file
85
htdocs/js/mpd.min.js
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
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,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.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 c=0,d={next:function(){if(c<a.length){var e=c++;return{value:b(e,a[e]),done:!1}}d.next=function(){return{done:!0,value:void 0}};return d.next()}};d[Symbol.iterator]=function(){return d};return d};
|
||||
$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,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.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e<d;e++){var f=a[e];if(b.call(c,f,e,a))return{i:e,v:f}}return{i:-1,v:void 0}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,c){return $jscomp.findInternal(this,a,c).v}},"es6","es3");
|
||||
var socket,last_song="",last_state,last_outputs,current_song={},isTouch=Modernizr.touch?1:0,playstate="",progressBar,volumeBar,settings={},app={apps:{Playback:{state:"0/-/"},Queue:{state:"0/Any Tag/"},Browse:{active:"Database",tabs:{Filesystem:{state:"0/-/"},Playlists:{state:"0/-/"},Database:{active:"Artist",views:{Artist:{state:"0/-/"},Album:{state:"0/-/"}}}}},Search:{state:"0/Any Tag/"}},current:{app:"Playback",tab:void 0,view:void 0,page:0,filter:"",search:""},last:{app:void 0,tab:void 0,view:void 0},
|
||||
prepare:function(){if(app.current.app!=app.last.app||app.current.tab!=app.last.tab||app.current.view!=app.last.view)$("#navbar-bottom > div").removeClass("active"),$("#cardPlayback").addClass("hide"),$("#cardQueue").addClass("hide"),$("#cardBrowse").addClass("hide"),$("#cardSearch").addClass("hide"),$("#panel-heading-browse > ul > li > a").removeClass("active"),$("#cardBrowsePlaylists").addClass("hide"),$("#cardBrowseDatabase").addClass("hide"),$("#cardBrowseFilesystem").addClass("hide"),$("#card"+
|
||||
app.current.app).removeClass("hide"),$("#nav"+app.current.app).addClass("active"),void 0!=app.current.tab&&($("#card"+app.current.app+app.current.tab).removeClass("hide"),$("#card"+app.current.app+"Nav"+app.current.tab).addClass("active"))},goto:function(a,b,c,d){app.apps[a].tabs?(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==d?app.apps[a].tabs[b].views[c].state:d)):a="/"+a+"/"+b+"!"+(void 0==d?app.apps[a].tabs[b].state:
|
||||
d)):a="/"+a+"!"+(void 0==d?app.apps[a].state:d);location.hash=a},route:function(){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.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.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.page=parseInt(params[5]);app.current.filter=params[6];app.current.search=params[7];app.prepare();if("Playback"==app.current.app)sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);else if("Queue"==app.current.app)app.last.app!=
|
||||
app.current.app&&(2>app.current.search.length&&setPagination(app.current.page),$("#searchqueuetag > button").each(function(){$(this).removeClass("active");$(this).text()==app.current.filter&&($(this).addClass("active"),$("#searchqueuetagdesc").text($(this).text()))})),getQueue();else if("Browse"==app.current.app&&"Playlists"==app.current.tab)sendAPI({cmd:"MPD_API_GET_PLAYLISTS",data:{offset:app.current.page,filter:app.current.filter}},parsePlaylists);else if("Browse"==app.current.app&&"Database"==
|
||||
app.current.tab&&"Artist"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTS",data:{offset:app.current.page,filter:app.current.filter}},parseListDBtags);else if("Browse"==app.current.app&&"Database"==app.current.tab&&"Album"==app.current.view)sendAPI({cmd:"MPD_API_GET_ARTISTALBUMS",data:{offset:app.current.page,filter:app.current.filter,albumartist:app.current.search}},parseListDBtags);else if("Browse"==app.current.app&&"Filesystem"==app.current.tab){$("#BrowseBreadcrumb").empty().append('<li class="breadcrumb-item"><a uri="">root</a></li>');
|
||||
sendAPI({cmd:"MPD_API_GET_FILESYSTEM",data:{offset:app.current.page,path:app.current.search?app.current.search:"/",filter:app.current.filter}},parseFilesystem);var a=$("#browseFilesystemAddAllSongs");app.current.search?(a.off(),a.on("click",function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:app.current.search}})}),a.removeAttr("disabled").removeClass("disabled")):a.attr("disabled","disabled").addClass("disabled");var b=app.current.search.split("/"),c="";$.each(b,function(a,e){b.length-1==a?$("#BrowseBreadcrumb").append('<li class="breadcrumb-item active">'+
|
||||
e+"</li>"):(c+=e,$("#BrowseBreadcrumb").append('<li class="breadcrumb-item"><a uri="'+c+'">'+e+"</a></li>"),c+="/")})}else"Search"==app.current.app?(app.last.app!=app.current.app&&(""!=app.current.search?$("#SearchList > tbody").append('<tr><td><span class="material-icons">search</span></td><td colspan="3">Searching</td><td></td><td></td></tr>'):setPagination(app.current.page),$("#searchstr2").val(app.current.search)),$("#searchtags2 > button").each(function(){$(this).removeClass("active");$(this).text()==
|
||||
app.current.filter&&($(this).addClass("active"),$("#searchtags2desc").text($(this).text()))}),2<=app.current.search.length?sendAPI({cmd:"MPD_API_SEARCH",data:{mpdtag:app.current.filter,offset:app.current.page,searchstr:app.current.search}},parseSearch):($("#SearchList > tbody").empty(),$("#searchAddAllSongs").attr("disabled","disabled").addClass("disabled"))):app.goto("Playback");app.last.app=app.current.app;app.last.tab=app.current.tab;app.last.view=app.current.view}else app.goto("Playback")}};
|
||||
$(document).ready(function(){getSettings();sendAPI({cmd:"MPD_API_GET_OUTPUTNAMES"},parseOutputnames);webSocketConnect();volumeBar=$("#volumebar").slider();volumeBar.slider("setValue",0);volumeBar.slider("on","slideStop",function(a){sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:a}})});progressBar=$("#progressbar").slider();progressBar.slider("setValue",0);progressBar.slider("on","slideStop",function(a){current_song&&0<=current_song.currentSongId&&sendAPI({cmd:"MPD_API_SET_SEEK",data:{songid:current_song.currentSongId,
|
||||
seek:Math.ceil(a/100*current_song.totalTime)}})});$("#about").on("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_STATS"},parseStats)});$("#settings").on("shown.bs.modal",function(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings);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")});
|
||||
$("#addstream").on("shown.bs.modal",function(){$("#streamurl").focus()});$("#addstream form").on("submit",function(a){addStream()});$("#mainMenu").on("shown.bs.dropdown",function(){$("#search > input").val("");$("#search > input").focus()});add_filter("#BrowseFilesystemFilterLetters");add_filter("#BrowseDatabaseFilterLetters");add_filter("#BrowsePlaylistsFilterLetters");window.addEventListener("hashchange",app.route,!1)});
|
||||
function webSocketConnect(){socket="undefined"!=typeof MozWebSocket?new MozWebSocket(get_appropriate_ws_url()):new WebSocket(get_appropriate_ws_url());try{socket.onopen=function(){console.log("connected");showNotification("Connected to myMPD","","","success");$("#modalConnectionError").modal("hide");app.route()},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").modal("show");setTimeout(function(){console.log("reconnect");webSocketConnect()},3E3)}}catch(a){alert("<p>Error"+a)}}
|
||||
function get_appropriate_ws_url(){var a=document.URL;if("https"==a.substring(0,5)){var b="wss://";a=a.substr(8)}else b="ws://","http"==a.substring(0,4)&&(a=a.substr(7));a=a.split("#");var c=/\/$/.test(a[0])?"":"/";return b+a[0]+c+"ws"}
|
||||
function parseStats(a){$("#mpdstats_artists").text(a.data.artists);$("#mpdstats_albums").text(a.data.albums);$("#mpdstats_songs").text(a.data.songs);$("#mpdstats_dbplaytime").text(beautifyDuration(a.data.dbplaytime));$("#mpdstats_playtime").text(beautifyDuration(a.data.playtime));$("#mpdstats_uptime").text(beautifyDuration(a.data.uptime));var b=new Date(1E3*a.data.dbupdated);$("#mpdstats_dbupdated").text(b.toUTCString());$("#mympdVersion").text(a.data.mympd_version);$("#mpdVersion").text(a.data.mpd_version)}
|
||||
function parseSettings(a){a.data.random?$("#btnrandom").addClass("active").attr("aria-pressed","true"):$("#btnrandom").removeClass("active").attr("aria-pressed","false");a.data.consume?$("#btnconsume").addClass("active").attr("aria-pressed","true"):$("#btnconsume").removeClass("active").attr("aria-pressed","false");a.data.single?$("#btnsingle").addClass("active").attr("aria-pressed","true"):$("#btnsingle").removeClass("active").attr("aria-pressed","false");a.data.repeat?$("#btnrepeat").addClass("active").attr("aria-pressed",
|
||||
"true"):$("#btnrepeat").removeClass("active").attr("aria-pressed","false");void 0!=a.data.crossfade?$("#inputCrossfade").removeAttr("disabled").val(a.data.crossfade):$("#inputCrossfade").attr("disabled","disabled");void 0!=a.data.mixrampdb?$("#inputMixrampdb").removeAttr("disabled").val(a.data.mixrampdb):$("#inputMixrampdb").attr("disabled","disabled");void 0!=a.data.mixrampdelay?$("#inputMixrampdelay").removeAttr("disabled").val(a.data.mixrampdelay):$("#inputMixrampdb").attr("disabled","disabled");
|
||||
$("#selectReplaygain").val(a.data.replaygain);notificationsSupported()?a.data.notificationWeb?($("#btnnotifyWeb").addClass("active").attr("aria-pressed","true"),Notification.requestPermission(function(b){"permission"in Notification||(Notification.permission=b);"granted"===b?$("#btnnotifyWeb").addClass("active").attr("aria-pressed","true"):($("#btnnotifyWeb").removeClass("active").attr("aria-pressed","false"),a.data.notificationWeb=0)})):$("#btnnotifyWeb").removeClass("active").attr("aria-pressed",
|
||||
"false"):($("#btnnotifyWeb").addClass("disabled"),$("#btnnotifyWeb").removeClass("active").attr("aria-pressed","false"));a.data.notificationPage?$("#btnnotifyPage").addClass("active").attr("aria-pressed","true"):$("#btnnotifyPage").removeClass("active").attr("aria-pressed","false");settings=a.data;setLocalStream(a.data.mpdhost,a.data.streamport)}function getSettings(){sendAPI({cmd:"MPD_API_GET_SETTINGS"},parseSettings)}
|
||||
function parseOutputnames(a){$("#btn-outputs-block button").remove();Object.keys(a.data).length?$.each(a.data,function(a,c){$('<button id="btnoutput'+a+'" class="btn btn-secondary btn-block" onclick="toggleoutput(this, '+a+')"><span class="material-icons float-left">volume_up</span> '+c+"</button>").appendTo($("#btn-outputs-block"))}):$("#btn-outputs-block").addClass("hide");last_outputs=""}
|
||||
function parseState(a){updatePlayIcon(a);updateVolumeIcon(a.data.volume);if(JSON.stringify(a)!==JSON.stringify(last_state)){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,d=Math.floor(a.data.elapsedTime/60),e=a.data.elapsedTime-60*d;volumeBar.slider("setValue",a.data.volume);progressBar.slider("setValue",Math.floor(100*a.data.elapsedTime/a.data.totalTime));b=d+":"+(10>e?"0":"")+e+" / "+b+":"+(10>
|
||||
c?"0":"")+c;$("#counter").text(b);last_state&&($("#QueueList > tbody > tr[trackid="+last_state.data.currentsongid+"] > td").eq(4).text(last_state.data.totalTime),$("#QueueList > tbody > tr[trackid="+last_state.data.currentsongid+"] > td").eq(0).removeClass("material-icons").text(last_state.data.songpos));$("#QueueList > tbody > tr").removeClass("active").removeClass("font-weight-bold");$("#QueueList > tbody > tr[trackid="+a.data.currentsongid+"] > td").eq(4).text(b);$("#QueueList > tbody > tr[trackid="+
|
||||
a.data.currentsongid+"] > td").eq(0).addClass("material-icons").text("play_arrow");$("#QueueList > tbody > tr[trackid="+a.data.currentsongid+"]").addClass("active").addClass("font-weight-bold");void 0!=last_state&&a.data.queue_version==last_state.data.queue_version||sendAPI({cmd:"MPD_API_GET_CURRENT_SONG"},songChange);last_state=a;$.each(a.data.outputs,function(a,b){b?$("#btnoutput"+a).addClass("active"):$("#btnoutput"+a).removeClass("active")});last_outputs=a.data.outputs}}
|
||||
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){$("#panel-heading-queue").empty();0<a.totalEntities&&$("#panel-heading-queue").text(a.totalEntities+" Songs");0<a.totalTime&&$("#panel-heading-queue").append(" \u2013 "+beautifyDuration(a.totalTime));var b=0,c=document.getElementById(app.current.app+"List").getElementsByTagName("tbody")[0].getElementsByTagName("tr"),d;for(d in a.data){b++;var e=Math.floor(a.data[d].duration/60),f=a.data[d].duration-60*e;e='<tr trackid="'+a.data[d].id+'"><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>"+e+":"+(10>f?"0":"")+f+"</td><td></td></tr>";b<=c.length?$(c[b-1]).attr("trackid")!=a.data[d].id&&$(c[b-1]).replaceWith(e):$("#"+app.current.app+"List > tbody").append(e)}for(d=c.length;d>b;d--)$(c[c.length-1]).remove();"queuesearch"==a.type&&0==b&&$("#QueueList > tbody").append('<tr><td><span class="material-icons">error_outline</span></td><td colspan="3">No results, please refine your search!</td><td></td><td></td></tr>');
|
||||
setPagination(a.totalEntities);if(isTouch)$("#QueueList > tbody > tr > td:last-child").append('<a class="pull-right btn-group-hover color-darkgrey" href="#/Queue!'+app.current.page+"/"+app.current.filter+"/"+app.current.search+'" onclick="delQueueSong($(this).parents(\'tr\'),event);"><span class="material-icons">delete</span></a>');else $("#QueueList > tbody > tr").on({mouseover:function(){var a=$(this);$("#btntrashmodeup").hasClass("active")&&(a=$("#QueueList > tbody > tr:lt("+($(this).index()+1)+
|
||||
")"));$("#btntrashmodedown").hasClass("active")&&(a=$("#QueueList > tbody > tr:gt("+($(this).index()-1)+")"));$.each(a,function(){0==$(this).children().last().has("a").length&&$(this).children().last().append('<a class="pull-right btn-group-hover color-darkgrey" href="#/Queue!'+app.current.page+"/"+app.current.filter+"/"+app.current.search+'" onclick="delQueueSong($(this).parents(\'tr\'),event);"><span class="material-icons">delete</span></a>').find("a").fadeTo("fast",1)})},mouseleave:function(){var a=
|
||||
$(this);$("#btntrashmodeup").hasClass("active")&&(a=$("#QueueList > tbody > tr:lt("+($(this).index()+1)+")"));$("#btntrashmodedown").hasClass("active")&&(a=$("#QueueList > tbody > tr:gt("+($(this).index()-1)+")"));$.each(a,function(){$(this).children().last().find("a").stop().remove()})}});$("#QueueList > tbody > tr").on({click:function(){$("#queueList > tbody > tr").removeClass("active");sendAPI({cmd:"MPD_API_PLAY_TRACK",data:{track:$(this).attr("trackid")}});$(this).addClass("active")}})}}
|
||||
function parseSearch(a){"Search"===app.current.app&&($("#panel-heading-search").text(a.totalEntities+" Songs found"),0<a.totalEntities?$("#searchAddAllSongs").removeAttr("disabled").removeClass("disabled"):$("#searchAddAllSongs").attr("disabled","disabled").addClass("disabled"),parseFilesystem(a))}
|
||||
function parseFilesystem(a){function b(a,b,c){$(a).append('<a role="button" class="pull-right btn-group-hover"><span class="material-icons">'+c+"</span></a>").find("a").click(function(a){a.stopPropagation();sendAPI({cmd:b,data:{uri:decodeURI($(this).parents("tr").attr("uri"))}});showNotification('"'+$("td:nth-last-child(3)",$(this).parents("tr")).text()+'" added',"","","success")})}if("Browse"===app.current.app||"Filesystem"===app.current.tab||"Search"===app.current.app){var c=0,d=document.getElementById(app.current.app+
|
||||
(void 0==app.current.tab?"":app.current.tab)+"List").getElementsByTagName("tbody")[0].getElementsByTagName("tr"),e;for(e in a.data){c++;var f="",g="";switch(a.data[e].type){case "directory":g=encodeURI(a.data[e].dir);f='<tr uri="'+g+'" class="dir"><td><span class="material-icons">folder_open</span></td><td colspan="3"><a>'+basename(a.data[e].dir)+"</a></td><td></td><td></td></tr>";break;case "song":f=Math.floor(a.data[e].duration/60);var h=a.data[e].duration-60*f;g=encodeURI(a.data[e].uri);f='<tr uri="'+
|
||||
g+'" class="song"><td><span class="material-icons">music_note</span></td><td>'+a.data[e].title+"</td><td>"+a.data[e].artist+"</td><td>"+a.data[e].album+"</td><td>"+f+":"+(10>h?"0":"")+h+"</td><td></td></tr>";break;case "playlist":g=encodeURI(a.data[e].plist),f='<tr uri="'+g+'" class="plist"><td><span class="material-icons">list</span></td><td colspan="3"><a>'+basename(a.data[e].plist)+"</a></td><td></td><td></td></tr>"}c<=d.length?$(d[c-1]).attr("uri")!=g&&$(d[c-1]).replaceWith(f):$("#"+app.current.app+
|
||||
(void 0==app.current.tab?"":app.current.tab)+"List > tbody").append(f)}for(e=d.length;e>c;e--)$(d[d.length-1]).remove();setPagination(a.totalEntities);0==c&&$("#"+app.current.app+app.current.tab+"List > tbody").append('<tr><td><span class="material-icons">error_outline</span></td><td colspan="3">No results</td><td></td><td></td></tr>');if(isTouch)b($("#"+app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List > tbody > tr.dir > td:last-child"),"MPD_API_ADD_TRACK","playlist_add"),b($("#"+
|
||||
app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List > tbody > tr.song > td:last-child"),"MPD_API_ADD_TRACK","playlist_add");else $("#"+app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List > tbody > tr").on({mouseenter:function(){$(this).is(".dir")?b($(this).children().last(),"MPD_API_ADD_TRACK","playlist_add"):$(this).is(".song")&&b($(this).children().last(),"MPD_API_ADD_TRACK","playlist_add")},mouseleave:function(){$(this).children().last().find("a").stop().remove()}});
|
||||
$("#"+app.current.app+(void 0==app.current.tab?"":app.current.tab)+"List > tbody > tr").on({click:function(){switch($(this).attr("class")){case "dir":app.current.page=0;app.current.search=$(this).attr("uri");$("#BrowseFilesystemList > a").attr("href","#/Browse/Filesystem!"+app.current.page+"/"+app.current.filter+"/"+app.current.search);app.goto("Browse","Filesystem",void 0,app.current.page+"/"+app.current.filter+"/"+app.current.search);break;case "song":sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:decodeURI($(this).attr("uri"))}});
|
||||
showNotification('"'+$("td:nth-last-child(5)",this).text()+'" added',"","","success");break;case "plist":sendAPI({cmd:"MPD_API_ADD_PLAYLIST",data:{plist:decodeURI($(this).attr("uri"))}}),showNotification('"'+$("td:nth-last-child(3)",this).text()+'" added',"","","success")}}});$("#BrowseBreadcrumb > li > a").on({click:function(){app.current.page=0;app.current.search=$(this).attr("uri");$("#BrowseFilesystemList > a").attr("href","#/Browse/Filesystem!"+app.current.page+"/"+app.current.filter+"/"+app.current.search);
|
||||
app.goto("Browse","Filesystem",void 0,app.current.page+"/"+app.current.filter+"/"+app.current.search)}});doSetFilterLetter("#BrowseFilesystemFilter")}}
|
||||
function parsePlaylists(a){if("Browse"===app.current.app||"Playlists"===app.current.tab){var b=0,c=document.getElementById(app.current.app+app.current.tab+"List").getElementsByTagName("tbody")[0].getElementsByTagName("tr"),d;for(d in a.data){b++;var e=new Date(1E3*a.data[d].last_modified),f=encodeURI(a.data[d].plist);e='<tr uri="'+f+'"><td><span class="material-icons">list</span></td><td><a>'+basename(a.data[d].plist)+"</a></td><td>"+e.toUTCString()+"</td><td></td></tr>";b<=c.length?$(c[b-1]).attr("uri")!=
|
||||
f&&$(c[b-1]).replaceWith(e):$("#"+app.current.app+app.current.tab+"List > tbody").append(e)}for(d=c.length;d>b;d--)$(c[c.length-1]).remove();setPagination(a.totalEntities);if(isTouch)$("#"+app.current.app+app.current.tab+"List > tbody > tr > td:last-child").append('<a class="pull-right btn-group-hover color-darkgrey" href="#/Browse/Playlists!'+app.current.page+"/"+app.current.filter+"/"+app.current.search+'" onclick="delPlaylist($(this).parents(\'tr\'));"><span class="material-icons">delete</span></a>');
|
||||
else $("#"+app.current.app+app.current.tab+"List > tbody > tr").on({mouseover:function(){0==$(this).children().last().has("a").length&&$(this).children().last().append('<a class="pull-right btn-group-hover color-darkgrey" href="#/Browse/Playlists!'+app.current.page+"/"+app.current.filter+"/"+app.current.search+'" onclick="delPlaylist($(this).parents(\'tr\'));"><span class="material-icons">delete</span></a>')},mouseleave:function(){$(this);$(this).children().last().find("a").stop().remove()}});$("#"+
|
||||
app.current.app+app.current.tab+"List > tbody > tr").on({click:function(){sendAPI({cmd:"MPD_API_ADD_PLAYLIST",data:{plist:decodeURI($(this).attr("uri"))}});showNotification('"'+$("td:nth-last-child(3)",this).text()+'" added',"","","success")}});0==b&&$("#"+app.current.app+app.current.tab+"List > tbody").append('<tr><td><span class="material-icons">error_outline</span></td><td colspan="3">No playlists found.</td><td></td><td></td></tr>');doSetFilterLetter("#browsePlaylistsFilter")}}
|
||||
function parseListDBtags(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Artist"===app.current.view){if("AlbumArtist"==a.tagtype){$("#BrowseDatabaseAlbumCards").addClass("hide");$("#BrowseDatabaseArtistList").removeClass("hide");$("#btnBrowseDatabaseArtist").addClass("hide");var b=0,c=document.getElementById(app.current.app+app.current.tab+app.current.view+"List").getElementsByTagName("tbody")[0].getElementsByTagName("tr"),d;for(d in a.data){b++;var e=encodeURI(a.data[d].value),f=
|
||||
'<tr uri="'+e+'"><td><span class="material-icons">album</span></td><td><a>'+a.data[d].value+"</a></td></tr>";b<=c.length?$(c[b-1]).attr("uri")!=e&&$(c[b-1]).replaceWith(f):$("#"+app.current.app+app.current.tab+app.current.view+"List > tbody").append(f)}for(d=c.length;d>b;d--)$(c[c.length-1]).remove();setPagination(a.totalEntities);$("#"+app.current.app+app.current.tab+app.current.view+"List > tbody > tr").on({click:function(){app.goto("Browse","Database","Album","0/-/"+$(this).attr("uri"))}});0==
|
||||
b&&$("#"+app.current.app+app.current.tab+app.current.view+"List > tbody").append('<tr><td><span class="material-icons">error_outline</span></td><td colspan="3">No entries found.</td><td></td><td></td></tr>')}else if("Album"==a.tagtype){$("#BrowseDatabaseArtistList").addClass("hide");$("#BrowseDatabaseAlbumCards").removeClass("hide");$("#btnBrowseDatabaseArtist").removeClass("hide");b=0;c=document.getElementById("BrowseDatabaseAlbumCards").querySelectorAll(".col-md");for(d in a.data)b++,e=genId(a.data[d].value),
|
||||
f='<div class="col-md mr-0" id="'+e+'"><div class="card mb-4" id="card'+e+'"> <img class="card-img-top" src="" alt=""> <div class="card-body"> <h5 class="card-title">'+a.searchstr+'</h5> <h4 class="card-title">'+a.data[d].value+'</h4> <table class="table table-sm table-hover" id="tbl'+e+'"><tbody></tbody></table </div></div></div>',b<=c.length?c[b-1].id!=e&&$(c[b-1]).replaceWith(f):$("#BrowseDatabaseAlbumCards").append(f),(b>c.length||c[b-1].id!=e)&&sendAPI({cmd:"MPD_API_GET_ARTISTALBUMTITLES",
|
||||
data:{albumartist:a.searchstr,album:a.data[d].value}},parseListTitles);for(d=c.length;d>b;d--)$(c[d-1]).remove();setPagination(a.totalEntities)}doSetFilterLetter("#BrowseDatabaseFilter")}}
|
||||
function parseListTitles(a){if("Browse"===app.current.app||"Database"===app.current.tab||"Album"===app.current.view){var b=genId(a.album),c=$("#card"+b+" > div > table > tbody");$("#card"+b+" > img").attr("src",a.cover).attr("uri",a.data[0].uri.replace(/\/[^\/]+$/,"")).attr("data-album",encodeURI(a.album));var d="",e;for(e in a.data)d+='<tr uri="'+encodeURI(a.data[e].uri)+'" class="song"><td>'+a.data[e].track+"</td><td>"+a.data[e].title+"</td></tr>";c.append(d);$("#card"+b+" > img").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",
|
||||
data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+decodeURI($(this).attr("data-album"))+'" added',"","","success")}});$("#tbl"+b+" > tbody > tr").on({click:function(){sendAPI({cmd:"MPD_API_ADD_TRACK",data:{track:decodeURI($(this).attr("uri"))}});showNotification('"'+$("td:nth-last-child(1)",this).text()+'" added',"","","success")}})}}
|
||||
function setPagination(a){var b=Math.ceil(a/settings.max_elements_per_page),c=app.current.app+(void 0==app.current.tab?"":app.current.tab);0==b&&(b=1);$("#"+c+"PaginationTopPage").text("Page "+(app.current.page/settings.max_elements_per_page+1)+" / "+b);$("#"+c+"PaginationBottomPage").text("Page "+(app.current.page/settings.max_elements_per_page+1)+" / "+b);if(1<b){$("#"+c+"PaginationTopPage").removeClass("disabled").removeAttr("disabled");$("#"+c+"PaginationBottomPage").removeClass("disabled").removeAttr("disabled");
|
||||
$("#"+c+"PaginationTopPages").empty();$("#"+c+"PaginationBottomPages").empty();for(var d=0;d<b;d++)$("#"+c+"PaginationTopPages").append('<button onclick="gotoPage('+d*settings.max_elements_per_page+',this,event)" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+(d+1)+"</button>"),$("#"+c+"PaginationBottomPages").append('<button onclick="gotoPage('+d*settings.max_elements_per_page+',this,event)" type="button" class="mr-1 mb-1 btn-sm btn btn-secondary">'+(d+1)+"</button>")}else $("#"+c+"PaginationTopPage").addClass("disabled").attr("disabled",
|
||||
"disabled"),$("#"+c+"PaginationBottomPage").addClass("disabled").attr("disabled","disabled");a>app.current.page+settings.max_elements_per_page?($("#"+c+"PaginationTopNext").removeClass("disabled").removeAttr("disabled"),$("#"+c+"PaginationBottomNext").removeClass("disabled").removeAttr("disabled"),$("#"+c+"ButtonsBottom").removeClass("hide")):($("#"+c+"PaginationTopNext").addClass("disabled").attr("disabled","disabled"),$("#"+c+"PaginationBottomNext").addClass("disabled").attr("disabled","disabled"),
|
||||
$("#"+c+"ButtonsBottom").addClass("hide"));0<app.current.page?($("#"+c+"PaginationTopPrev").removeClass("disabled").removeAttr("disabled"),$("#"+c+"PaginationBottomPrev").removeClass("disabled").removeAttr("disabled")):($("#"+c+"PaginationTopPrev").addClass("disabled").attr("disabled","disabled"),$("#"+c+"PaginationBottomPrev").addClass("disabled").attr("disabled","disabled"))}
|
||||
function updateVolumeIcon(a){-1==a?($("#volumePrct").text("Volumecontrol disabled"),$("#volumeControl").addClass("hide")):($("#volumeControl").removeClass("hidden"),$("#volumePrct").text(a+" %"),0==a?$("#volume-icon").text("volume_off"):50>a?$("#volume-icon").text("volume_down"):$("#volume-icon").text("volume_up"))}
|
||||
function updatePlayIcon(a){1==a.data.state?($("#btnPlay > span").text("play_arrow"),playstate="stop"):2==a.data.state?($("#btnPlay > span").text("pause"),playstate="play"):($("#btnPlay > span").text("play_arrow"),playstate="pause");-1==a.data.nextsongpos?$("#btnNext").addClass("disabled").attr("disabled","disabled"):$("#btnNext").removeClass("disabled").removeAttr("disabled");0>=a.data.songpos?$("#btnPrev").addClass("disabled").attr("disabled","disabled"):$("#btnPrev").removeClass("disabled").removeAttr("disabled");
|
||||
0==a.data.queue_length?$("#btnPlay").addClass("disabled").attr("disabled","disabled"):$("#btnPlay").removeClass("disabled").removeAttr("disabled")}function sendAPI(a,b){$.ajax({url:"/api",contentType:"application/json",method:"POST",data:JSON.stringify(a),success:b})}function updateDB(a){sendAPI({cmd:"MPD_API_UPDATE_DB"});showNotification("Updating MPD Database...","","","success");a.preventDefault()}
|
||||
function clickPlay(){"play"!=playstate?sendAPI({cmd:"MPD_API_SET_PLAY"}):sendAPI({cmd:"MPD_API_SET_PAUSE"})}function clickStop(){sendAPI({cmd:"MPD_API_SET_STOP"})}function clickPrev(){sendAPI({cmd:"MPD_API_SET_PREV"})}function clickNext(){sendAPI({cmd:"MPD_API_SET_NEXT"})}function setLocalStream(a,b){var c="http://";c="127.0.0.1"==a||"localhost"==a?c+window.location.hostname:c+a;settings.mpdstream=c+(":"+b+"/")}
|
||||
function delQueueSong(a,b){b.stopPropagation();$("#btntrashmodeup").hasClass("active")?sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:0,end:a.index()+1}}):$("#btntrashmodesingle").hasClass("active")?sendAPI({cmd:"MPD_API_RM_TRACK",data:{track:a.attr("trackid")}}):$("#btntrashmodedown").hasClass("active")&&sendAPI({cmd:"MPD_API_RM_RANGE",data:{start:a.index(),end:-1}})}function delPlaylist(a){sendAPI({cmd:"MPD_API_RM_PLAYLIST",data:{plist:decodeURI(a.attr("uri"))}});a.remove()}
|
||||
function basename(a){return a.split("/").reverse()[0]}$("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#cardBrowseNavDatabase").on("click",function(a){app.goto("Browse","Database");a.preventDefault()});$("#btnBrowseDatabaseArtist").on("click",function(a){app.goto("Browse","Database","Artist");a.preventDefault()});$("#cardBrowseNavPlaylists").on("click",function(a){app.goto("Browse","Playlists");a.preventDefault()});
|
||||
$("#cardBrowseNavFilesystem").on("click",function(a){app.goto("Browse","Filesystem");a.preventDefault()});$("#navPlayback").on("click",function(a){app.goto("Playback");a.preventDefault()});$("#navQueue").on("click",function(a){app.goto("Queue");a.preventDefault()});$("#navBrowse").on("click",function(a){app.goto("Browse");a.preventDefault()});$("#navSearch").on("click",function(a){app.goto("Search");a.preventDefault()});
|
||||
function confirmSettings(){var a=!0;if(!$("#inputCrossfade").is(":disabled")){var b=parseInt($("#inputCrossfade").val());isNaN(b)?(document.getElementById("inputCrossfade").classList.add("is-invalid"),a=!1):$("#inputCrossfade").val(b)}$("#inputMixrampdb").is(":disabled")||(b=parseFloat($("#inputMixrampdb").val()),isNaN(b)?(document.getElementById("inputMixrampdb").classList.add("is-invalid"),a=!1):$("#inputMixrampdb").val(b));$("#inputMixrampdelay").is(":disabled")||("nan"==$("#inputMixrampdelay").val()&&
|
||||
$("#inputMixrampdelay").val("-1"),b=parseFloat($("#inputMixrampdelay").val()),isNaN(b)?(document.getElementById("inputMixrampdelay").classList.add("is-invalid"),a=!1):$("#inputMixrampdelay").val(b));1==a?(sendAPI({cmd:"MPD_API_SET_SETTINGS",data:{consume:$("#btnconsume").hasClass("active")?1:0,random:$("#btnrandom").hasClass("active")?1:0,single:$("#btnsingle").hasClass("active")?1:0,repeat:$("#btnrepeat").hasClass("active")?1:0,replaygain:$("#selectReplaygain").val(),crossfade:$("#inputCrossfade").val(),
|
||||
mixrampdb:$("#inputMixrampdb").val(),mixrampdelay:$("#inputMixrampdelay").val(),notificationWeb:$("#btnnotifyWeb").hasClass("active")?1:0,notificationPage:$("#btnnotifyPage").hasClass("active")?1:0}},getSettings),$("#settings").modal("hide")):document.getElementById("settingsFrm").classList.add("was-validated")}function toggleoutput(a,b){sendAPI({cmd:"MPD_API_TOGGLE_OUTPUT",data:{output:b,state:$(a).hasClass("active")?0:1}})}
|
||||
$("#trashmodebtns > button").on("click",function(a){$("#trashmodebtns").children("button").removeClass("active");$(this).addClass("active")});$("#search > input").keypress(function(a){13==a.which&&$("#mainMenu > a").dropdown("toggle")});$("#search").submit(function(){app.goto("Search",void 0,void 0,app.current.page+"/Any Tag/"+$("#search > input").val());return!1});$("#search2").submit(function(){return!1});
|
||||
function addAllFromSearch(){if(2<=app.current.search.length){sendAPI({cmd:"MPD_API_SEARCH_ADD",data:{filter:app.current.filter,searchstr:+app.current.search}});var a=$("#SearchList >tbody >tr").length;showNotification("Added "+a+" songs from search","","","success")}}$("#searchstr2").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto("Search",void 0,void 0,app.current.page+"/"+app.current.filter+"/"+app.current.search)});
|
||||
$("#searchtags2 > button").on("click",function(a){$("#searchtags2 > button").removeClass("active");$(this).removeClass("btn-secondary").addClass("active");app.current.filter=$(this).text();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)});
|
||||
$("#searchqueuestr").keyup(function(a){app.current.page=0;app.current.search=$(this).val();app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)});
|
||||
$("#searchqueuetag > button").on("click",function(a){$("#searchqueuetag > button").removeClass("active");$(this).removeClass("btn-secondary").addClass("active");app.current.filter=$(this).text();$("#searchqueuetagdesc").text(app.current.filter);app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)});$("#searchqueue").submit(function(){return!1});$("#searchqueue").submit(function(){return!1});
|
||||
function scrollToTop(){document.body.scrollTop=0;document.documentElement.scrollTop=0}function gotoPage(a,b,c){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}app.goto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search);c.preventDefault()}
|
||||
function addStream(){0<$("#streamurl").val().length&&sendAPI({cmd:"MPD_API_ADD_TRACK",data:{uri:$("#streamurl").val()}});$("#streamurl").val("");$("#addstream").modal("hide")}function saveQueue(){0<$("#playlistname").val().length&&sendAPI({cmd:"MPD_API_SAVE_QUEUE",data:{plist:$("#playlistname").val()}});$("#savequeue").modal("hide")}
|
||||
function showNotification(a,b,c,d){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&$.notify({title:a,message:c},{type:d,offset:{y:60,x:20},template:'<div data-notify="container" class="alert alert-{0}" role="alert"><span data-notify="title">{1}</span> <span data-notify="message">{2}</span></div>'})}function notificationsSupported(){return"Notification"in window}
|
||||
function songChange(a){if(last_song!=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid){var b="",c="",d="myMPD: ";$("#album-cover").css("backgroundImage",'url("'+a.data.cover+'")');"undefined"!=typeof a.data.artist&&0<a.data.artist.length&&"-"!=a.data.artist?(b+=a.data.artist,c+="<br/>"+a.data.artist,d+=a.data.artist+" - ",$("#artist").text(a.data.artist)):$("#artist").text("");"undefined"!=typeof a.data.album&&0<a.data.album.length&&"-"!=a.data.album?(b+=" - "+a.data.album,
|
||||
c+="<br/>"+a.data.album,$("#album").text(a.data.album)):$("#album").text("");"undefined"!=typeof a.data.title&&0<a.data.title.length?(d+=a.data.title,$("#currenttrack").text(a.data.title)):$("#currenttrack").text("");document.title=d;showNotification(a.data.title,b,c,"success");last_song=a.data.title+a.data.artist+a.data.album+a.data.uri+a.data.currentsongid}}
|
||||
$(document).keydown(function(a){if("INPUT"!=a.target.tagName){switch(a.which){case 37:sendAPI({cmd:"MPD_API_SET_PREV"});break;case 39:sendAPI({cmd:"MPD_API_SET_NEXT"});break;case 32:clickPlay();break;default:return}a.preventDefault()}});function setFilterLetter(a){app.goto(app.current.app,app.current.tab,app.current.view,"0/"+a+"/"+app.current.search)}
|
||||
function doSetFilterLetter(a){$(a+"Letters > button").removeClass("active");"0"==app.current.filter?($(a).text("Filter: #"),$(a+"Letters > button").each(function(){"#"==$(this).text()&&$(this).addClass("active")})):"-"!=app.current.filter?($(a).text("Filter: "+app.current.filter),$(a+"Letters > button").each(function(){$(this).text()==app.current.filter&&$(this).addClass("active")})):$(a).text("Filter")}
|
||||
function add_filter(a){$(a).append('<button class="mr-1 mb-1 btn btn-sm btn-secondary" onclick="setFilterLetter(\'-\');"><span class="material-icons" style="font-size:14px;">delete</span></button>');$(a).append('<button class="mr-1 mb-1 btn btn-sm btn-secondary" onclick="setFilterLetter(\'0\');">#</button>');for(i=65;90>=i;i++){var b=String.fromCharCode(i);$(a).append('<button class="mr-1 mb-1 btn-sm btn btn-secondary" onclick="setFilterLetter(\''+b+"');\">"+b+"</button>")}}
|
||||
function chVolume(a){a=volumeBar.slider("getValue")+a;0>a?a=0:100<a&&(a=100);volumeBar.slider("setValue",a);sendAPI({cmd:"MPD_API_SET_VOLUME",data:{volume:a}})}function beautifyDuration(a){var b=Math.floor(a/86400),c=Math.floor(a/3600)-24*b,d=Math.floor(a/60)-60*c-1440*b;a=a-86400*b-3600*c-60*d;return(0<b?b+"\u2009d ":"")+(0<c?c+"\u2009h "+(10>d?"0":""):"")+d+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}function genId(a){return"id"+a.replace(/[^\w]/g,"")};
|
5
htdocs/js/player.js
Normal file
5
htdocs/js/player.js
Normal file
@ -0,0 +1,5 @@
|
||||
var mpdstream = decodeURI(location.hash).replace(/^#/,'');
|
||||
player.src = mpdstream;
|
||||
console.log('playing mpd stream: ' + player.src);
|
||||
player.load();
|
||||
player.play();
|
1
htdocs/js/player.min.js
vendored
Normal file
1
htdocs/js/player.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var mpdstream=decodeURI(location.hash).replace(/^#/,"");player.src=mpdstream;console.log("playing mpd stream: "+player.src);player.load();player.play();
|
@ -29,15 +29,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="js/jquery-3.3.1.min.js"></script>
|
||||
<script src="js/js.cookie-2.2.0.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var mpdstream = Cookies.get('mpdstream');
|
||||
player.src = mpdstream;
|
||||
console.log('playing mpd stream: ' + player.src);
|
||||
player.load();
|
||||
player.play();
|
||||
</script>
|
||||
<script type="text/javascript" src="js/player.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
6
mkdebug.sh
Executable file
6
mkdebug.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#/bin/sh
|
||||
|
||||
[ -d debug ] || mkdir debug
|
||||
cd debug
|
||||
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=DEBUG ..
|
||||
make
|
43
mkrelease.sh
Executable file
43
mkrelease.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#/bin/sh
|
||||
|
||||
if [ -f buildtools/closure-compiler.jar ]
|
||||
then
|
||||
[ 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
|
||||
else
|
||||
[ 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
|
||||
fi
|
||||
|
||||
if [ -f buildtools/closure-stylesheets.jar ]
|
||||
then
|
||||
[ 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
|
||||
[ htdocs/css/mpd.css -nt htdocs/css/mpd.min.css ] && \
|
||||
cp htdocs/css/mpd.css htdocs/css/mpd.min.css
|
||||
fi
|
||||
|
||||
[ -d release ] || mkdir release
|
||||
cd release
|
||||
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE ..
|
||||
make
|
||||
sudo make install
|
||||
cd ..
|
||||
|
||||
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 chown nobody /var/lib/mympd
|
||||
|
||||
[ -d /etc/systemd/system ] && \
|
||||
sudo cp -v contrib/mympd.service /etc/systemd/system/
|
||||
|
||||
[ -d /etc/default ] && \
|
||||
sudo cp -v contrib/mympd.default /etc/default/mympd
|
||||
|
||||
echo "myMPD installed"
|
9
mympd.1
9
mympd.1
@ -18,12 +18,6 @@ connect to mpd at host, defaults to localhost
|
||||
\fB\-p\fR, \fB\-\-port PORT\fR
|
||||
connect to mpd at port, defaults to 6600
|
||||
.TP
|
||||
\fB\-D\fR, \fB\-\-digest HTDIGEST\fR
|
||||
path to htdigest file for authorization
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-localport PORT\fR
|
||||
skip authorization for local port
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-webport PORT\fR
|
||||
specifies the port for the webserver to listen to, defaults to 8080
|
||||
.TP
|
||||
@ -39,6 +33,9 @@ specifies the password to use when connecting to mpd
|
||||
\fB-i\fR, \fB\-\-coverimage FILENAME\fR
|
||||
filename for coverimage [folder.jpg]
|
||||
.TP
|
||||
\fB-t\fR, \fB\-\-statefile FILENAME\fR
|
||||
filename for mympd state [/var/lib/mympd/mympd.state]
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
print version and exit
|
||||
.TP
|
||||
|
@ -30,4 +30,5 @@
|
||||
#define MYMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH}
|
||||
#define MYMPD_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}"
|
||||
#define SRC_PATH "${ASSETS_PATH}"
|
||||
#cmakedefine DEBUG
|
||||
#endif
|
||||
|
16
src/frozen/LICENSE
Normal file
16
src/frozen/LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This code is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
You are free to use this code under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
Alternatively, you can license this code under a commercial
|
||||
license, as set out in <http://cesanta.com/>.
|
1410
src/frozen/frozen.c
Normal file
1410
src/frozen/frozen.c
Normal file
File diff suppressed because it is too large
Load Diff
313
src/frozen/frozen.h
Normal file
313
src/frozen/frozen.h
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
* Copyright (c) 2018 Cesanta Software Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CS_FROZEN_FROZEN_H_
|
||||
#define CS_FROZEN_FROZEN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_WIN32) && _MSC_VER < 1700
|
||||
typedef int bool;
|
||||
enum { false = 0, true = 1 };
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
/* JSON token type */
|
||||
enum json_token_type {
|
||||
JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */
|
||||
JSON_TYPE_STRING,
|
||||
JSON_TYPE_NUMBER,
|
||||
JSON_TYPE_TRUE,
|
||||
JSON_TYPE_FALSE,
|
||||
JSON_TYPE_NULL,
|
||||
JSON_TYPE_OBJECT_START,
|
||||
JSON_TYPE_OBJECT_END,
|
||||
JSON_TYPE_ARRAY_START,
|
||||
JSON_TYPE_ARRAY_END,
|
||||
|
||||
JSON_TYPES_CNT
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure containing token type and value. Used in `json_walk()` and
|
||||
* `json_scanf()` with the format specifier `%T`.
|
||||
*/
|
||||
struct json_token {
|
||||
const char *ptr; /* Points to the beginning of the value */
|
||||
int len; /* Value length */
|
||||
enum json_token_type type; /* Type of the token, possible values are above */
|
||||
};
|
||||
|
||||
#define JSON_INVALID_TOKEN \
|
||||
{ 0, 0, JSON_TYPE_INVALID }
|
||||
|
||||
/* Error codes */
|
||||
#define JSON_STRING_INVALID -1
|
||||
#define JSON_STRING_INCOMPLETE -2
|
||||
|
||||
/*
|
||||
* Callback-based SAX-like API.
|
||||
*
|
||||
* Property name and length is given only if it's available: i.e. if current
|
||||
* event is an object's property. In other cases, `name` is `NULL`. For
|
||||
* example, name is never given:
|
||||
* - For the first value in the JSON string;
|
||||
* - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END
|
||||
*
|
||||
* E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`,
|
||||
* the sequence of callback invocations will be as follows:
|
||||
*
|
||||
* - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL
|
||||
* - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123"
|
||||
* - type: JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULL
|
||||
* - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1"
|
||||
* - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2"
|
||||
* - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL
|
||||
* - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true"
|
||||
* - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\":
|
||||
*true }"
|
||||
* - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, {
|
||||
*\"baz\": true } ]"
|
||||
* - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123,
|
||||
*\"bar\": [ 1, 2, { \"baz\": true } ] }"
|
||||
*/
|
||||
typedef void (*json_walk_callback_t)(void *callback_data, const char *name,
|
||||
size_t name_len, const char *path,
|
||||
const struct json_token *token);
|
||||
|
||||
/*
|
||||
* Parse `json_string`, invoking `callback` in a way similar to SAX parsers;
|
||||
* see `json_walk_callback_t`.
|
||||
* Return number of processed bytes, or a negative error code.
|
||||
*/
|
||||
int json_walk(const char *json_string, int json_string_length,
|
||||
json_walk_callback_t callback, void *callback_data);
|
||||
|
||||
/*
|
||||
* JSON generation API.
|
||||
* struct json_out abstracts output, allowing alternative printing plugins.
|
||||
*/
|
||||
struct json_out {
|
||||
int (*printer)(struct json_out *, const char *str, size_t len);
|
||||
union {
|
||||
struct {
|
||||
char *buf;
|
||||
size_t size;
|
||||
size_t len;
|
||||
} buf;
|
||||
void *data;
|
||||
FILE *fp;
|
||||
} u;
|
||||
};
|
||||
|
||||
extern int json_printer_buf(struct json_out *, const char *, size_t);
|
||||
extern int json_printer_file(struct json_out *, const char *, size_t);
|
||||
|
||||
#define JSON_OUT_BUF(buf, len) \
|
||||
{ \
|
||||
json_printer_buf, { \
|
||||
{ buf, len, 0 } \
|
||||
} \
|
||||
}
|
||||
#define JSON_OUT_FILE(fp) \
|
||||
{ \
|
||||
json_printer_file, { \
|
||||
{ (char *) fp, 0, 0 } \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);
|
||||
|
||||
/*
|
||||
* Generate formatted output into a given sting buffer.
|
||||
* This is a superset of printf() function, with extra format specifiers:
|
||||
* - `%B` print json boolean, `true` or `false`. Accepts an `int`.
|
||||
* - `%Q` print quoted escaped string or `null`. Accepts a `const char *`.
|
||||
* - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *`
|
||||
* - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`.
|
||||
* - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`.
|
||||
* - `%M` invokes a json_printf_callback_t function. That callback function
|
||||
* can consume more parameters.
|
||||
*
|
||||
* Return number of bytes printed. If the return value is bigger than the
|
||||
* supplied buffer, that is an indicator of overflow. In the overflow case,
|
||||
* overflown bytes are not printed.
|
||||
*/
|
||||
int json_printf(struct json_out *, const char *fmt, ...);
|
||||
int json_vprintf(struct json_out *, const char *fmt, va_list ap);
|
||||
|
||||
/*
|
||||
* Same as json_printf, but prints to a file.
|
||||
* File is created if does not exist. File is truncated if already exists.
|
||||
*/
|
||||
int json_fprintf(const char *file_name, const char *fmt, ...);
|
||||
int json_vfprintf(const char *file_name, const char *fmt, va_list ap);
|
||||
|
||||
/*
|
||||
* Print JSON into an allocated 0-terminated string.
|
||||
* Return allocated string, or NULL on error.
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* char *str = json_asprintf("{a:%H}", 3, "abc");
|
||||
* printf("%s\n", str); // Prints "616263"
|
||||
* free(str);
|
||||
* ```
|
||||
*/
|
||||
char *json_asprintf(const char *fmt, ...);
|
||||
char *json_vasprintf(const char *fmt, va_list ap);
|
||||
|
||||
/*
|
||||
* Helper %M callback that prints contiguous C arrays.
|
||||
* Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt
|
||||
* Return number of bytes printed.
|
||||
*/
|
||||
int json_printf_array(struct json_out *, va_list *ap);
|
||||
|
||||
/*
|
||||
* Scan JSON string `str`, performing scanf-like conversions according to `fmt`.
|
||||
* This is a `scanf()` - like function, with following differences:
|
||||
*
|
||||
* 1. Object keys in the format string may be not quoted, e.g. "{key: %d}"
|
||||
* 2. Order of keys in an object is irrelevant.
|
||||
* 3. Several extra format specifiers are supported:
|
||||
* - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`),
|
||||
* expects boolean `true` or `false`.
|
||||
* - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned
|
||||
* string is malloc-ed, caller must free() the string.
|
||||
* - %V: consumes `char **`, `int *`. Expects base64-encoded string.
|
||||
* Result string is base64-decoded, malloced and NUL-terminated.
|
||||
* The length of result string is stored in `int *` placeholder.
|
||||
* Caller must free() the result.
|
||||
* - %H: consumes `int *`, `char **`.
|
||||
* Expects a hex-encoded string, e.g. "fa014f".
|
||||
* Result string is hex-decoded, malloced and NUL-terminated.
|
||||
* The length of the result string is stored in `int *` placeholder.
|
||||
* Caller must free() the result.
|
||||
* - %M: consumes custom scanning function pointer and
|
||||
* `void *user_data` parameter - see json_scanner_t definition.
|
||||
* - %T: consumes `struct json_token *`, fills it out with matched token.
|
||||
*
|
||||
* Return number of elements successfully scanned & converted.
|
||||
* Negative number means scan error.
|
||||
*/
|
||||
int json_scanf(const char *str, int str_len, const char *fmt, ...);
|
||||
int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap);
|
||||
|
||||
/* json_scanf's %M handler */
|
||||
typedef void (*json_scanner_t)(const char *str, int len, void *user_data);
|
||||
|
||||
/*
|
||||
* Helper function to scan array item with given path and index.
|
||||
* Fills `token` with the matched JSON token.
|
||||
* Return -1 if no array element found, otherwise non-negative token length.
|
||||
*/
|
||||
int json_scanf_array_elem(const char *s, int len, const char *path, int index,
|
||||
struct json_token *token);
|
||||
|
||||
/*
|
||||
* Unescape JSON-encoded string src,slen into dst, dlen.
|
||||
* src and dst may overlap.
|
||||
* If destination buffer is too small (or zero-length), result string is not
|
||||
* written but the length is counted nevertheless (similar to snprintf).
|
||||
* Return the length of unescaped string in bytes.
|
||||
*/
|
||||
int json_unescape(const char *src, int slen, char *dst, int dlen);
|
||||
|
||||
/*
|
||||
* Escape a string `str`, `str_len` into the printer `out`.
|
||||
* Return the number of bytes printed.
|
||||
*/
|
||||
int json_escape(struct json_out *out, const char *str, size_t str_len);
|
||||
|
||||
/*
|
||||
* Read the whole file in memory.
|
||||
* Return malloc-ed file content, or NULL on error. The caller must free().
|
||||
*/
|
||||
char *json_fread(const char *file_name);
|
||||
|
||||
/*
|
||||
* Update given JSON string `s,len` by changing the value at given `json_path`.
|
||||
* The result is saved to `out`. If `json_fmt` == NULL, that deletes the key.
|
||||
* If path is not present, missing keys are added. Array path without an
|
||||
* index pushes a value to the end of an array.
|
||||
* Return 1 if the string was changed, 0 otherwise.
|
||||
*
|
||||
* Example: s is a JSON string { "a": 1, "b": [ 2 ] }
|
||||
* json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] }
|
||||
* json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 }
|
||||
* json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] }
|
||||
* json_setf(s, len, out, ".b", NULL); // { "a": 1 }
|
||||
*/
|
||||
int json_setf(const char *s, int len, struct json_out *out,
|
||||
const char *json_path, const char *json_fmt, ...);
|
||||
|
||||
int json_vsetf(const char *s, int len, struct json_out *out,
|
||||
const char *json_path, const char *json_fmt, va_list ap);
|
||||
|
||||
/*
|
||||
* Pretty-print JSON string `s,len` into `out`.
|
||||
* Return number of processed bytes in `s`.
|
||||
*/
|
||||
int json_prettify(const char *s, int len, struct json_out *out);
|
||||
|
||||
/*
|
||||
* Prettify JSON file `file_name`.
|
||||
* Return number of processed bytes, or negative number of error.
|
||||
* On error, file content is not modified.
|
||||
*/
|
||||
int json_prettify_file(const char *file_name);
|
||||
|
||||
/*
|
||||
* Iterate over an object at given JSON `path`.
|
||||
* On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL
|
||||
* for `key`, or `val`, in which case they won't be populated.
|
||||
* Return an opaque value suitable for the next iteration, or NULL when done.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```c
|
||||
* void *h = NULL;
|
||||
* struct json_token key, val;
|
||||
* while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) {
|
||||
* printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
void *json_next_key(const char *s, int len, void *handle, const char *path,
|
||||
struct json_token *key, struct json_token *val);
|
||||
|
||||
/*
|
||||
* Iterate over an array at given JSON `path`.
|
||||
* Similar to `json_next_key`, but fills array index `idx` instead of `key`.
|
||||
*/
|
||||
void *json_next_elem(const char *s, int len, void *handle, const char *path,
|
||||
int *idx, struct json_token *val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CS_FROZEN_FROZEN_H_ */
|
@ -1,49 +0,0 @@
|
||||
/* myMPD
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/ympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "http_server.h"
|
||||
|
||||
int callback_http(struct mg_connection *c)
|
||||
{
|
||||
const struct embedded_file *req_file;
|
||||
|
||||
if(!strcmp(c->uri, "/"))
|
||||
req_file = find_embedded_file("/index.html");
|
||||
else
|
||||
req_file = find_embedded_file(c->uri);
|
||||
|
||||
if(req_file)
|
||||
{
|
||||
mg_send_header(c, "Content-Type", req_file->mimetype);
|
||||
mg_send_data(c, req_file->data, req_file->size);
|
||||
|
||||
return MG_TRUE;
|
||||
}
|
||||
|
||||
mg_send_status(c, 404);
|
||||
mg_printf_data(c, "Not Found");
|
||||
return MG_TRUE;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/* myMPD
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/ympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __HTTP_SERVER_H__
|
||||
#define __HTTP_SERVER_H__
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
struct embedded_file {
|
||||
const char *name;
|
||||
const unsigned char *data;
|
||||
const char *mimetype;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
const struct embedded_file *find_embedded_file(const char *name);
|
||||
int callback_http(struct mg_connection *c);
|
||||
|
||||
#endif
|
||||
|
@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
// Copyright (c) 2013 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This library is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// You are free to use this library under the terms of the GNU General
|
||||
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// Alternatively, you can license this library under a commercial
|
||||
// license, as set out in <http://cesanta.com/products.html>.
|
||||
|
||||
// json encoder 'frozen' from https://github.com/cesanta/frozen
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "json_encode.h"
|
||||
|
||||
int json_emit_int(char *buf, int buf_len, long int value) {
|
||||
return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%ld", value);
|
||||
}
|
||||
|
||||
int json_emit_double(char *buf, int buf_len, double value) {
|
||||
return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%g", value);
|
||||
}
|
||||
|
||||
int json_emit_quoted_str(char *buf, int buf_len, const char *str) {
|
||||
int i = 0, j = 0, ch;
|
||||
|
||||
#define EMIT(x) do { if (j < buf_len) buf[j++] = x; } while (0)
|
||||
|
||||
EMIT('"');
|
||||
while ((ch = str[i++]) != '\0' && j < buf_len) {
|
||||
switch (ch) {
|
||||
case '"': EMIT('\\'); EMIT('"'); break;
|
||||
case '\\': EMIT('\\'); EMIT('\\'); break;
|
||||
case '\b': EMIT('\\'); EMIT('b'); break;
|
||||
case '\f': EMIT('\\'); EMIT('f'); break;
|
||||
case '\n': EMIT('\\'); EMIT('n'); break;
|
||||
case '\r': EMIT('\\'); EMIT('r'); break;
|
||||
case '\t': EMIT('\\'); EMIT('t'); break;
|
||||
default: EMIT(ch);
|
||||
}
|
||||
}
|
||||
EMIT('"');
|
||||
EMIT(0);
|
||||
|
||||
return j == 0 ? 0 : j - 1;
|
||||
}
|
||||
|
||||
int json_emit_raw_str(char *buf, int buf_len, const char *str) {
|
||||
return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%s", str);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/* ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __JSON_ENCODE_H__
|
||||
#define __JSON_ENCODE_H__
|
||||
|
||||
int json_emit_int(char *buf, int buf_len, long int value);
|
||||
int json_emit_double(char *buf, int buf_len, double value);
|
||||
int json_emit_quoted_str(char *buf, int buf_len, const char *str);
|
||||
int json_emit_raw_str(char *buf, int buf_len, const char *str);
|
||||
|
||||
#endif
|
5406
src/mongoose.c
5406
src/mongoose.c
File diff suppressed because it is too large
Load Diff
152
src/mongoose.h
152
src/mongoose.h
@ -1,152 +0,0 @@
|
||||
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
// Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This software is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// You are free to use this software under the terms of the GNU General
|
||||
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// Alternatively, you can license this software under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
|
||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||
#define MONGOOSE_HEADER_INCLUDED
|
||||
|
||||
#define MONGOOSE_VERSION "5.6"
|
||||
|
||||
#include <stdio.h> // required for FILE
|
||||
#include <stddef.h> // required for size_t
|
||||
#include <sys/types.h> // required for time_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// This structure contains information about HTTP request.
|
||||
struct mg_connection {
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
|
||||
char remote_ip[48]; // Max IPv6 string length is 45 characters
|
||||
char local_ip[48]; // Local IP address
|
||||
unsigned short remote_port; // Client's port
|
||||
unsigned short local_port; // Local port number
|
||||
|
||||
int num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[30];
|
||||
|
||||
char *content; // POST (or websocket message) data, or NULL
|
||||
size_t content_len; // Data length
|
||||
|
||||
int is_websocket; // Connection is a websocket connection
|
||||
int status_code; // HTTP status code for HTTP error handler
|
||||
int wsbits; // First byte of the websocket frame
|
||||
void *server_param; // Parameter passed to mg_create_server()
|
||||
void *connection_param; // Placeholder for connection-specific data
|
||||
void *callback_param;
|
||||
};
|
||||
|
||||
struct mg_server; // Opaque structure describing server instance
|
||||
enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
|
||||
enum mg_event {
|
||||
MG_POLL = 100, // Callback return value is ignored
|
||||
MG_CONNECT, // If callback returns MG_FALSE, connect fails
|
||||
MG_AUTH, // If callback returns MG_FALSE, authentication fails
|
||||
MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req
|
||||
MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection
|
||||
MG_RECV, // Mongoose has received POST data chunk.
|
||||
// Callback should return a number of bytes to discard from
|
||||
// the receive buffer, or -1 to close the connection.
|
||||
MG_CLOSE, // Connection is closed, callback return value is ignored
|
||||
MG_WS_HANDSHAKE, // New websocket connection, handshake request
|
||||
MG_WS_CONNECT, // New websocket connection established
|
||||
MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err
|
||||
};
|
||||
typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
|
||||
|
||||
// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
|
||||
enum {
|
||||
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||
WEBSOCKET_OPCODE_PING = 0x9,
|
||||
WEBSOCKET_OPCODE_PONG = 0xa
|
||||
};
|
||||
|
||||
// Server management functions
|
||||
struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
|
||||
void mg_destroy_server(struct mg_server **);
|
||||
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||
time_t mg_poll_server(struct mg_server *, int milliseconds);
|
||||
const char **mg_get_valid_option_names(void);
|
||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||
void mg_copy_listeners(struct mg_server *from, struct mg_server *to);
|
||||
struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
|
||||
void mg_wakeup_server(struct mg_server *);
|
||||
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
|
||||
struct mg_connection *mg_connect(struct mg_server *, const char *);
|
||||
|
||||
// Connection management functions
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
||||
size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
size_t mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
size_t mg_write(struct mg_connection *, const void *buf, size_t len);
|
||||
size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||
|
||||
size_t mg_websocket_write(struct mg_connection *, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
|
||||
const char *fmt, ...);
|
||||
|
||||
void mg_send_file(struct mg_connection *, const char *path, const char *);
|
||||
void mg_send_file_data(struct mg_connection *, int fd);
|
||||
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
|
||||
int mg_parse_multipart(const char *buf, int buf_len,
|
||||
char *var_name, int var_name_len,
|
||||
char *file_name, int file_name_len,
|
||||
const char **data, int *data_len);
|
||||
|
||||
|
||||
// Utility functions
|
||||
void *mg_start_thread(void *(*func)(void *), void *param);
|
||||
char *mg_md5(char buf[33], ...);
|
||||
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||
size_t mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
|
||||
int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, int);
|
||||
int mg_terminate_ssl(struct mg_connection *c, const char *cert);
|
||||
int mg_forward(struct mg_connection *c, const char *addr);
|
||||
void *mg_mmap(FILE *fp, size_t size);
|
||||
void mg_munmap(void *p, size_t size);
|
||||
|
||||
|
||||
// Templates support
|
||||
struct mg_expansion {
|
||||
const char *keyword;
|
||||
void (*handler)(struct mg_connection *);
|
||||
};
|
||||
void mg_template(struct mg_connection *, const char *text,
|
||||
struct mg_expansion *expansions);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // MONGOOSE_HEADER_INCLUDED
|
16
src/mongoose/LICENSE
Normal file
16
src/mongoose/LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013-2016 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This software is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
You are free to use this software under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
Alternatively, you can license this software under a commercial
|
||||
license, as set out in <https://www.cesanta.com/license>.
|
16133
src/mongoose/mongoose.c
Normal file
16133
src/mongoose/mongoose.c
Normal file
File diff suppressed because it is too large
Load Diff
6222
src/mongoose/mongoose.h
Normal file
6222
src/mongoose/mongoose.h
Normal file
File diff suppressed because it is too large
Load Diff
1249
src/mpd_client.c
1249
src/mpd_client.c
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
/* myMPD
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/ympd
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
@ -25,15 +25,16 @@
|
||||
#ifndef __MPD_CLIENT_H__
|
||||
#define __MPD_CLIENT_H__
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "mongoose/mongoose.h"
|
||||
|
||||
#define RETURN_ERROR_AND_RECOVER(X) do { \
|
||||
fprintf(stderr, "MPD X: %s\n", mpd_connection_get_error_message(mpd.conn)); \
|
||||
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}", \
|
||||
mpd_connection_get_error_message(mpd.conn)); \
|
||||
len = json_printf(&out, "{ type:error, data : %Q }", \
|
||||
mpd_connection_get_error_message(mpd.conn) \
|
||||
); \
|
||||
if (!mpd_connection_clear_error(mpd.conn)) \
|
||||
mpd.conn_state = MPD_FAILURE; \
|
||||
return cur - buffer; \
|
||||
return len; \
|
||||
} while(0)
|
||||
|
||||
|
||||
@ -44,7 +45,7 @@
|
||||
#define GEN_STR(X) #X,
|
||||
#define MPD_CMDS(X) \
|
||||
X(MPD_API_GET_QUEUE) \
|
||||
X(MPD_API_GET_BROWSE) \
|
||||
X(MPD_API_GET_FILESYSTEM) \
|
||||
X(MPD_API_ADD_TRACK) \
|
||||
X(MPD_API_ADD_PLAY_TRACK) \
|
||||
X(MPD_API_ADD_PLAYLIST) \
|
||||
@ -66,25 +67,19 @@
|
||||
X(MPD_API_SET_NEXT) \
|
||||
X(MPD_API_SET_PREV) \
|
||||
X(MPD_API_UPDATE_DB) \
|
||||
X(MPD_API_GET_OUTPUTS) \
|
||||
X(MPD_API_GET_OUTPUTNAMES) \
|
||||
X(MPD_API_TOGGLE_OUTPUT) \
|
||||
X(MPD_API_TOGGLE_RANDOM) \
|
||||
X(MPD_API_TOGGLE_CONSUME) \
|
||||
X(MPD_API_TOGGLE_SINGLE) \
|
||||
X(MPD_API_SET_CROSSFADE) \
|
||||
X(MPD_API_TOGGLE_REPEAT) \
|
||||
X(MPD_API_GET_SETTINGS) \
|
||||
X(MPD_API_SEND_SHUFFLE) \
|
||||
X(MPD_API_GET_STATS) \
|
||||
X(MPD_API_SET_MIXRAMPDB) \
|
||||
X(MPD_API_SET_MIXRAMPDELAY) \
|
||||
X(MPD_API_GET_PLAYLISTS) \
|
||||
X(MPD_API_RM_PLAYLIST) \
|
||||
X(MPD_API_SET_REPLAYGAIN) \
|
||||
X(MPD_API_GET_ARTISTALBUMS) \
|
||||
X(MPD_API_GET_ARTISTALBUMTITLES) \
|
||||
X(MPD_API_GET_ARTISTS) \
|
||||
X(MPD_API_GET_CURRENT_SONG)
|
||||
X(MPD_API_GET_CURRENT_SONG) \
|
||||
X(MPD_API_WELCOME) \
|
||||
X(MPD_API_GET_SETTINGS) \
|
||||
X(MPD_API_SET_SETTINGS)
|
||||
|
||||
enum mpd_cmd_ids {
|
||||
MPD_CMDS(GEN_ENUM)
|
||||
@ -100,10 +95,9 @@ enum mpd_conn_states {
|
||||
|
||||
struct t_mpd {
|
||||
int port;
|
||||
int local_port;
|
||||
char host[128];
|
||||
char *password;
|
||||
char *gpass;
|
||||
char *statefile;
|
||||
|
||||
struct mpd_connection *conn;
|
||||
enum mpd_conn_states conn_state;
|
||||
@ -120,28 +114,33 @@ struct t_mpd {
|
||||
int streamport;
|
||||
char coverimage[40];
|
||||
|
||||
static int is_websocket(const struct mg_connection *nc) {
|
||||
return nc->flags & MG_F_IS_WEBSOCKET;
|
||||
}
|
||||
|
||||
struct t_mpd_client_session {
|
||||
int song_id;
|
||||
int next_song_id;
|
||||
unsigned queue_version;
|
||||
};
|
||||
|
||||
void mpd_poll(struct mg_server *s);
|
||||
int callback_mpd(struct mg_connection *c);
|
||||
int mpd_close_handler(struct mg_connection *c);
|
||||
int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version);
|
||||
int mpd_put_outputs(char *buffer, int putnames);
|
||||
int mpd_put_current_song(char *buffer);
|
||||
int mpd_put_queue(char *buffer, unsigned int offset);
|
||||
int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter);
|
||||
int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
|
||||
int mpd_search_add(char *buffer, char *mpdtagtype, char *searchstr);
|
||||
int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
|
||||
void mympd_poll(struct mg_mgr *s);
|
||||
void callback_mympd(struct mg_connection *nc, const struct mg_str msg);
|
||||
int mympd_close_handler(struct mg_connection *c);
|
||||
int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version);
|
||||
int mympd_put_outputnames(char *buffer);
|
||||
int mympd_put_current_song(char *buffer);
|
||||
int mympd_put_queue(char *buffer, unsigned int offset);
|
||||
int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter);
|
||||
int mympd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
|
||||
int mympd_search_add(char *buffer, char *mpdtagtype, char *searchstr);
|
||||
int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr);
|
||||
int mympd_put_welcome(char *buffer);
|
||||
int mympd_get_stats(char *buffer);
|
||||
int mympd_put_settings(char *buffer);
|
||||
int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter);
|
||||
int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album);
|
||||
int mympd_put_playlists(char *buffer, unsigned int offset, char *filter);
|
||||
void mpd_disconnect();
|
||||
void mympd_disconnect();
|
||||
#endif
|
||||
|
||||
|
179
src/mympd.c
179
src/mympd.c
@ -1,6 +1,6 @@
|
||||
/* myMPD
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/ympd
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
@ -28,80 +28,91 @@
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "http_server.h"
|
||||
#include "mongoose/mongoose.h"
|
||||
#include "mpd_client.h"
|
||||
#include "config.h"
|
||||
|
||||
extern char *optarg;
|
||||
static sig_atomic_t s_signal_received = 0;
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
|
||||
int force_exit = 0;
|
||||
|
||||
void bye()
|
||||
{
|
||||
force_exit = 1;
|
||||
static void signal_handler(int sig_num) {
|
||||
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||
s_signal_received = sig_num;
|
||||
}
|
||||
|
||||
static int server_callback(struct mg_connection *c, enum mg_event ev) {
|
||||
int result = MG_FALSE;
|
||||
FILE *fp = NULL;
|
||||
static void handle_api(struct mg_connection *nc, struct http_message *hm) {
|
||||
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)) {
|
||||
mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
switch(ev) {
|
||||
case MG_CLOSE:
|
||||
mpd_close_handler(c);
|
||||
return MG_TRUE;
|
||||
case MG_REQUEST:
|
||||
if (c->is_websocket) {
|
||||
c->content[c->content_len] = '\0';
|
||||
if(c->content_len)
|
||||
return callback_mpd(c);
|
||||
else
|
||||
return MG_TRUE;
|
||||
} else
|
||||
return MG_FALSE;
|
||||
case MG_AUTH:
|
||||
// no auth for websockets since mobile safari does not support it
|
||||
if ( (mpd.gpass == NULL) || (c->is_websocket) || ((mpd.local_port > 0) && (c->local_port == mpd.local_port)) )
|
||||
return MG_TRUE;
|
||||
else {
|
||||
if ( (fp = fopen(mpd.gpass, "r")) != NULL ) {
|
||||
result = mg_authorize_digest(c, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||
#ifdef DEBUG
|
||||
fprintf(stdout,"New Websocket connection\n");
|
||||
#endif
|
||||
struct mg_str d = {(char *) "{\"cmd\":\"MPD_API_WELCOME\"}", 25 };
|
||||
callback_mympd(nc, d);
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
#ifdef DEBUG
|
||||
printf("HTTP request: %.*s\n",hm->uri.len,hm->uri.p);
|
||||
#endif
|
||||
if (mg_vcmp(&hm->uri, "/api") == 0) {
|
||||
handle_api(nc, hm);
|
||||
}
|
||||
return result;
|
||||
default:
|
||||
return MG_FALSE;
|
||||
else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
if (is_websocket(nc)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stdout,"Websocket connection closed\n");
|
||||
#endif
|
||||
mympd_close_handler(nc);
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stdout,"HTTP Close\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n, option_index = 0;
|
||||
struct mg_server *server = mg_create_server(NULL, server_callback);
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
unsigned int current_timer = 0, last_timer = 0;
|
||||
char *run_as_user = NULL;
|
||||
char const *error_msg = NULL;
|
||||
char *webport = "8080";
|
||||
|
||||
atexit(bye);
|
||||
mg_set_option(server, "document_root", SRC_PATH);
|
||||
|
||||
mg_set_option(server, "auth_domain", "mympd");
|
||||
char *webport = "80";
|
||||
mpd.port = 6600;
|
||||
mpd.local_port = 0;
|
||||
mpd.gpass = NULL;
|
||||
strcpy(mpd.host, "127.0.0.1");
|
||||
streamport = 8000;
|
||||
strcpy(coverimage, "folder.jpg");
|
||||
mpd.statefile="/var/lib/mympd/mympd.state";
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"digest", required_argument, 0, 'D'},
|
||||
{"host", required_argument, 0, 'h'},
|
||||
{"port", required_argument, 0, 'p'},
|
||||
{"localport", required_argument, 0, 'l'},
|
||||
{"webport", required_argument, 0, 'w'},
|
||||
{"user", required_argument, 0, 'u'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
@ -109,14 +120,15 @@ int main(int argc, char **argv)
|
||||
{"mpdpass", required_argument, 0, 'm'},
|
||||
{"streamport", required_argument, 0, 's'},
|
||||
{"coverimage", required_argument, 0, 'i'},
|
||||
{"statefile", required_argument, 0, 't'},
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:vm:s:i:",
|
||||
while((n = getopt_long(argc, argv, "D:h:p:w:u:vm:s:i:c:t:",
|
||||
long_options, &option_index)) != -1) {
|
||||
switch (n) {
|
||||
case 'D':
|
||||
mpd.gpass = strdup(optarg);
|
||||
case 't':
|
||||
mpd.statefile = strdup(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
strncpy(mpd.host, optarg, sizeof(mpd.host));
|
||||
@ -124,9 +136,6 @@ int main(int argc, char **argv)
|
||||
case 'p':
|
||||
mpd.port = atoi(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
mpd.local_port = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
webport = strdup(optarg);
|
||||
break;
|
||||
@ -152,58 +161,70 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: %s [OPTION]...\n\n"
|
||||
" -D, --digest <htdigest>\tpath to htdigest file for authorization\n"
|
||||
" \t(realm ympd) [no authorization]\n"
|
||||
" -h, --host <host>\t\tconnect to mpd at host [localhost]\n"
|
||||
" -p, --port <port>\t\tconnect to mpd at port [6600]\n"
|
||||
" -l, --localport <port>\t\tskip authorization for local port\n"
|
||||
" -w, --webport [ip:]<port>\tlisten interface/port for webserver [8080]\n"
|
||||
" -u, --user <username>\t\tdrop priviliges to user after socket bind\n"
|
||||
" -v, --version\t\t\tget version\n"
|
||||
" -m, --mpdpass <password>\tspecifies the password to use when connecting to mpd\n"
|
||||
" -s, --streamport <port>\tconnect to mpd http stream at port [8000]\n"
|
||||
" -i, --coverimage <filename>\tfilename for coverimage [folder.jpg]\n"
|
||||
" -t, --statefile <filename>\tfilename for mympd state [/var/lib/mympd/mympd.state]\n"
|
||||
" --help\t\t\t\tthis help\n"
|
||||
, argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(error_msg)
|
||||
{
|
||||
fprintf(stderr, "Mongoose error: %s\n", error_msg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
error_msg = mg_set_option(server, "listening_port", webport);
|
||||
if(error_msg) {
|
||||
fprintf(stderr, "Mongoose error: %s\n", error_msg);
|
||||
return EXIT_FAILURE;
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
setvbuf(stderr, NULL, _IOLBF, 0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* drop privilges at last to ensure proper port binding */
|
||||
if(run_as_user != NULL) {
|
||||
error_msg = mg_set_option(server, "run_as_user", run_as_user);
|
||||
free(run_as_user);
|
||||
if(error_msg)
|
||||
{
|
||||
fprintf(stderr, "Mongoose error: %s\n", error_msg);
|
||||
printf("Droping privileges\n");
|
||||
struct passwd *pw;
|
||||
if ((pw = getpwnam(run_as_user)) == NULL) {
|
||||
printf("Unknown user\n");
|
||||
return EXIT_FAILURE;
|
||||
} else if (setgid(pw->pw_gid) != 0) {
|
||||
printf("setgid() failed");
|
||||
return EXIT_FAILURE;
|
||||
} else if (setuid(pw->pw_uid) != 0) {
|
||||
printf("setuid() failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (getuid() == 0) {
|
||||
printf("myMPD should not be run with root privileges\n");
|
||||
mg_mgr_free(&mgr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (!force_exit) {
|
||||
mg_poll_server(server, 200);
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
s_http_server_opts.document_root = SRC_PATH;
|
||||
|
||||
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)
|
||||
{
|
||||
last_timer = current_timer;
|
||||
mpd_poll(server);
|
||||
mympd_poll(&mgr);
|
||||
}
|
||||
}
|
||||
|
||||
mpd_disconnect();
|
||||
mg_destroy_server(&server);
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
mympd_disconnect();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user