mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-24 18:37:40 +00:00 
			
		
		
		
	Merge branch 'master' into docker
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| language: c | ||||
|  | ||||
| sudo: required | ||||
| dist: precise | ||||
| dist: trusty | ||||
|  | ||||
| compiler:  | ||||
|   - gcc | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| cmake_minimum_required(VERSION 2.6) | ||||
|  | ||||
| project (ympd) | ||||
| project (ympd C) | ||||
| set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") | ||||
| set(CPACK_PACKAGE_VERSION_MAJOR "1") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "2") | ||||
| @@ -24,8 +24,8 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}	${LIBMPDCLIENT_I | ||||
|  | ||||
| include(CheckCSourceCompiles) | ||||
|  | ||||
| set(CMAKE_C_FLAGS "-std=gnu99 -Wall") | ||||
| set(CMAKE_C_FLAGS_DEBUG "-ggdb -pedantic") | ||||
| 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() | ||||
|   | ||||
							
								
								
									
										13
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| FROM alpine:3.5 | ||||
| WORKDIR /app/build | ||||
| COPY . /app | ||||
| RUN apk add --no-cache g++ make cmake libmpdclient-dev openssl-dev | ||||
| RUN cmake .. | ||||
| RUN make | ||||
|  | ||||
| FROM alpine:3.5 | ||||
| RUN apk add  --no-cache libmpdclient openssl | ||||
| EXPOSE 8080 | ||||
| COPY --from=0 /app/build/ympd /usr/bin/ympd | ||||
| COPY --from=0 /app/build/mkdata /usr/bin/mkdata | ||||
| CMD ympd | ||||
| @@ -13,11 +13,12 @@ Dependencies | ||||
| ------------ | ||||
|  - libmpdclient 2: http://www.musicpd.org/libs/libmpdclient/ | ||||
|  - cmake 2.6: http://cmake.org/ | ||||
|  - OpenSSL: https://www.openssl.org/ | ||||
|  | ||||
| Unix Build Instructions | ||||
| ----------------------- | ||||
|  | ||||
| 1. install dependencies, cmake and libmpdclient are available from all major distributions. | ||||
| 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``` | ||||
| 4. build ```make``` | ||||
| @@ -28,11 +29,12 @@ Run flags | ||||
| ``` | ||||
| Usage: ./ympd [OPTION]... | ||||
|  | ||||
|  -d, --digest <htdigest>    path to htdigest file for authorization | ||||
|  -D, --digest <htdigest>       path to htdigest file for authorization | ||||
|                                (realm ympd) [no authorization] | ||||
|  -h, --host <host>             connect to mpd at host [localhost] | ||||
|  -p, --port <port>             connect to mpd at port [6600] | ||||
|  -w, --webport [ip:]<port>     listen interface/port for webserver [8080] | ||||
|  -d, --dirbletoken <apitoken>  Dirble API token | ||||
|  -u, --user <username>         drop priviliges to user after socket bind | ||||
|  -V, --version                 get version | ||||
|  --help                        this help | ||||
|   | ||||
| @@ -24,7 +24,9 @@ YMPD_USER=nobody | ||||
| MPD_HOST=localhost | ||||
| MPD_PORT=6600 | ||||
| WEB_PORT=8080 | ||||
| DIRBLE_API_TOKEN=2e223c9909593b94fc6577361a | ||||
| #DIGEST=--digest /path/to/htdigest | ||||
| #LOCALPORT=8080 | ||||
|  | ||||
|  | ||||
| # Exit if the package is not installed | ||||
| @@ -36,7 +38,7 @@ WEB_PORT=8080 | ||||
| # Load the VERBOSE setting and other rcS variables | ||||
| [ -f /etc/default/rcS ] && . /etc/default/rcS | ||||
|  | ||||
| DAEMON_OPT="--user $YMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $DIGEST" | ||||
| DAEMON_OPT="--user $YMPD_USER --mpdpass '$MPD_PASSWORD' --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT --dirbletoken $DIRBLE_API_TOKEN $DIGEST $LOCALPORT" | ||||
|  | ||||
| do_start() | ||||
| { | ||||
|   | ||||
| @@ -1,5 +1,8 @@ | ||||
| MPD_HOST=localhost | ||||
| MPD_PORT=6600 | ||||
| MPD_PASSWORD= | ||||
| WEB_PORT=8080 | ||||
| YMPD_USER=nobody | ||||
| DIRBLE_API_TOKEN=2e223c9909593b94fc6577361a | ||||
| #DIGEST=--digest /path/to/htdigest | ||||
| #LOCALPORT=--localport 8080 | ||||
|   | ||||
							
								
								
									
										28
									
								
								contrib/ympd.freebsd
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								contrib/ympd.freebsd
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| # PROVIDE: ympd | ||||
| # REQUIRE: DAEMON musicpd | ||||
| # KEYWORD: shutdown | ||||
|  | ||||
| # Add the following line to /etc/rc.conf to enable ympd: | ||||
| # | ||||
| # ympd_enable="YES" | ||||
|  | ||||
| . /etc/rc.subr | ||||
|  | ||||
| name="ympd" | ||||
| rcvar="${name}_enable" | ||||
| command="/usr/local/bin/${name}" | ||||
| pidfile="/var/run/${name}.pid" | ||||
| start_cmd="ympd_start" | ||||
|  | ||||
| load_rc_config "${name}" | ||||
| : ${ympd_enable:="NO"} | ||||
|  | ||||
| ympd_start() | ||||
| { | ||||
| 	check_startmsgs && echo "Starting ${name}." | ||||
| 	/usr/sbin/daemon -f -p "${pidfile}" "${command}" "${rc_flags}" | ||||
| } | ||||
|  | ||||
| run_rc_command "$1" | ||||
| @@ -5,11 +5,14 @@ Requires=network.target local-fs.target | ||||
| [Service] | ||||
| Environment=MPD_HOST=localhost | ||||
| Environment=MPD_PORT=6600 | ||||
| Environment=MPD_PASSWORD= | ||||
| Environment=WEB_PORT=8080 | ||||
| Environment=YMPD_USER=nobody | ||||
| Environment=DIRBLE_API_TOKEN=2e223c9909593b94fc6577361a | ||||
| Environment=DIGEST= | ||||
| Environment=LOCALPORT= | ||||
| EnvironmentFile=/etc/default/ympd | ||||
| ExecStart=/usr/bin/ympd --user $YMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $DIGEST | ||||
| ExecStart=/usr/bin/ympd --user $YMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT --dirbletoken $DIRBLE_API_TOKEN $DIGEST $LOCALPORT | ||||
| Type=simple | ||||
|  | ||||
| [Install] | ||||
|   | ||||
| @@ -27,6 +27,17 @@ button { | ||||
|   margin-top: 2px; | ||||
| } | ||||
|  | ||||
| #breadcrump { | ||||
|   display: block; | ||||
|  | ||||
|   overflow: auto; | ||||
|   white-space: nowrap; | ||||
| } | ||||
|  | ||||
| #breadcrump > li > a{ | ||||
|   cursor: pointer; | ||||
| } | ||||
|  | ||||
| #counter { | ||||
|   font-size: 24px; | ||||
|   margin-top: -6px; | ||||
| @@ -61,8 +72,50 @@ button { | ||||
|   } | ||||
| } | ||||
|  | ||||
| #salamisandwich td:nth-child(3), th:nth-child(3) { | ||||
| h1 { | ||||
|   display: block; | ||||
|  | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
|   white-space: nowrap; | ||||
| } | ||||
|  | ||||
| td:nth-child(4), th:nth-child(4) { | ||||
|   /* This *has* to be placed before | ||||
|      any t[dh]:nth-last-child(2) for | ||||
|      the override to work. */ | ||||
|   min-width: 50%; | ||||
| } | ||||
|  | ||||
| td:nth-last-child(2), th:nth-last-child(2) { | ||||
|   text-align: right; | ||||
|   width: 4em; | ||||
| } | ||||
|  | ||||
| #salamisandwich td:nth-child(4) span { | ||||
|   font-size: 90%; | ||||
|  | ||||
|   display: block; | ||||
| } | ||||
|  | ||||
| td:nth-child(2), td:nth-child(3) { | ||||
|   min-width: 25%; | ||||
|   max-width: 10em; | ||||
|  | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
|   white-space: nowrap; | ||||
| } | ||||
|  | ||||
| @media only screen and (max-width: 600px) { | ||||
| 	td:nth-child(2), td:nth-child(3) { | ||||
| 	  min-width: 0; | ||||
| 	  max-width: 0; | ||||
| 	} | ||||
| 	td:nth-child(4), th:nth-child(4) { | ||||
| 	  min-width: 10%; | ||||
| 	  white-space: normal; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| tbody { | ||||
| @@ -94,3 +147,17 @@ td:last-child, td:first-child { | ||||
| button { | ||||
|   overflow: hidden; | ||||
| } | ||||
|  | ||||
| #trashmode span:last-child { | ||||
|   display:inline-block; | ||||
|   text-align:left; | ||||
|   width:2.8em; | ||||
| } | ||||
|  | ||||
| #filter > a.active { | ||||
|   font-weight: bold; | ||||
|   pointer-events: none; | ||||
|   cursor: default; | ||||
|   text-decoration: none; | ||||
|   color: black; | ||||
| } | ||||
|   | ||||
| @@ -98,15 +98,16 @@ | ||||
|  | ||||
|         <div class="panel panel-primary"> | ||||
|           <!-- Default panel contents --> | ||||
|           <div class="panel-heading"><b id="panel-heading">Queue</b></div> | ||||
|           <div class="panel-heading"><b id="panel-heading">Queue</b> | ||||
|                                      <b id="panel-heading-info" class="text pull-right"></b></div> | ||||
|           <div class="panel-body"> | ||||
|             <h1> | ||||
|               <span id="track-icon" onclick="clickPlay();" class="glyphicon glyphicon-play"></span> | ||||
|               <span id="currenttrack"></span> | ||||
|             </h1> | ||||
|             <h4> | ||||
|               <span id="album" class="text"></span> | ||||
|               <span id="artist" class="text pull-right"></span> | ||||
|               <span id="artist" class="text"></span> | ||||
|               <span id="album" class="text pull-right"></span> | ||||
|             </h4> | ||||
|             <p id="counter" class="text pull-right">  </p> | ||||
|  | ||||
| @@ -118,7 +119,7 @@ | ||||
|           <ol id="breadcrump" class="breadcrumb"> | ||||
|           </ol> | ||||
|  | ||||
|           <div class="col-md-12"> | ||||
|           <div class="col-md-12" id="filter"> | ||||
|           <button id="add-all-songs" class="btn btn-primary pull-right">Add all</button> | ||||
|           </div> | ||||
|  | ||||
| @@ -128,10 +129,10 @@ | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 <th>#</th> | ||||
|                 <th>Title</th> | ||||
|                 <th>Album</th> | ||||
|                 <th>Artist</th> | ||||
|                 <th>Duration</th> | ||||
|                 <th>Album</th> | ||||
|                 <th>Title</th> | ||||
|                 <th>Length</th> | ||||
|                 <th></th> | ||||
|               </tr> | ||||
|             </thead> | ||||
| @@ -195,32 +196,27 @@ | ||||
|           <div id="trashmode" class="btn-group-vertical btn-block btn-group-lg" data-toggle="radio"> | ||||
|             <button id="btntrashmodeup" type="button" class="btn btn-default"> | ||||
|               <span class="glyphicon glyphicon-chevron-up"></span> | ||||
|               <span class="glyphicon glyphicon-trash"></span> up     | ||||
|               <span class="glyphicon glyphicon-trash"></span> <span>Up</span> | ||||
|             </button> | ||||
|             <button id="btntrashmodesingle" type="button" class="btn btn-default active"> | ||||
|               <span class="glyphicon glyphicon-star-empty"></span> | ||||
|               <span class="glyphicon glyphicon-trash"></span> single | ||||
|               <span class="glyphicon glyphicon-trash"></span> <span>Single</span> | ||||
|             </button> | ||||
|             <button id="btntrashmodedown" type="button" class="btn btn-default"> | ||||
|               <span class="glyphicon glyphicon-chevron-down"></span> | ||||
|               <span class="glyphicon glyphicon-trash"></span> down  | ||||
|               <span class="glyphicon glyphicon-trash"></span> <span>Down</span> | ||||
|             </button> | ||||
|           </div> | ||||
|  | ||||
|           <div id="btn-responsive-block" class="btn-group-vertical btn-block btn-group-lg"> | ||||
|             <button type="button" class="btn btn-default" onclick="socket.send('MPD_API_RM_ALL');"> | ||||
|               <span class="glyphicon glyphicon-trash"></span> Clear queue | ||||
|               <span class="glyphicon glyphicon-trash"></span> Clear Queue | ||||
|             </button> | ||||
|             <a href="#" data-toggle="modal" data-target="#savequeue" class="btn btn-default"> | ||||
|               <span class="glyphicon glyphicon-save"></span> Save queue | ||||
|               <span class="glyphicon glyphicon-save"></span> Save Queue | ||||
|             </a> | ||||
|           </div> | ||||
|  | ||||
|             <div id="btn-responsive-block" class="btn-group-vertical btn-block btn-group-lg" data-toggle="buttons"> | ||||
|               <button type="button" class="btn btn-default" id="btnnotify"> | ||||
|                <span class="glyphicon glyphicon-comment"></span> Notifications | ||||
|               </button> | ||||
|             </div> | ||||
|         </div> | ||||
|       </div><!-- /.col-md-2 --> | ||||
|     </div><!-- /.row --> | ||||
| @@ -242,6 +238,19 @@ | ||||
|           <h6><a href="http://cesanta.com/docs.html">Mongoose</a> <small>GPLv2</small></h6> | ||||
|           <h6><a href="http://www.musicpd.org/libs/libmpdclient/">libMPDClient</a> <small>BSD License</small></h6> | ||||
|           <hr /> | ||||
|           <div class="row"> | ||||
|             <div class="form-group col-md-6"> | ||||
|               <button type="button" class="btn btn-default btn-block" onclick="updateDB();"> | ||||
|                 <span class="glyphicon glyphicon-refresh"></span> Update Database | ||||
|               </button> | ||||
|             </div> | ||||
|             <div class="form-group col-md-6" data-toggle="buttons"> | ||||
|               <button type="button" class="btn btn-default btn-block" id="btnnotify"> | ||||
|                 <span class="glyphicon glyphicon-comment"></span> Enable Notifications | ||||
|               </button> | ||||
|             </div> | ||||
|           </div> | ||||
|           <hr /> | ||||
|           <form role="form"> | ||||
|             <div class="row"> | ||||
|               <div class="form-group col-md-9"> | ||||
| @@ -260,7 +269,7 @@ | ||||
|               </div> | ||||
|               <div class="form-group col-md-6"> | ||||
|                 <label class="control-label" for="mpd_pw_con">MPD Password (Confirmation)</label> | ||||
|                 <input type="password" class="form-control" id="mpd_pw_con"  placeholder="Password confirmation" | ||||
|                 <input type="password" class="form-control" id="mpd_pw_con"  placeholder="Confirmation" | ||||
|                 data-placement="right" data-toggle="popover" data-content="Password does not match!" | ||||
|                 data-trigger="manual" disabled /> | ||||
|               </div> | ||||
| @@ -305,7 +314,7 @@ | ||||
|         <div class="modal-body"> | ||||
|           <form role="form"> | ||||
|             <div class="row"> | ||||
|               <div class="form-group col-md-9"> | ||||
|               <div class="form-group col-md-12"> | ||||
|                 <label class="control-label" for="streamurl">Stream URL</label> | ||||
|                 <input type="text" class="form-control" id="streamurl" /> | ||||
|               </div> | ||||
|   | ||||
							
								
								
									
										266
									
								
								htdocs/js/mpd.js
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								htdocs/js/mpd.js
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| /* ympd | ||||
|    (c) 2013-2014 Andrew Karpow <andy@ndyk.de> | ||||
|    This project's homepage is: http://www.ympd.org | ||||
|    This project's homepage is: https://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 | ||||
| @@ -23,7 +23,7 @@ var last_state; | ||||
| var last_outputs; | ||||
| var current_app; | ||||
| var pagination = 0; | ||||
| var browsepath; | ||||
| var browsepath = ""; | ||||
| var lastSongTitle = ""; | ||||
| var current_song = new Object(); | ||||
| var MAX_ELEMENTS_PER_PAGE = 512; | ||||
| @@ -31,6 +31,9 @@ var dirble_selected_cat = ""; | ||||
| var dirble_catid = ""; | ||||
| var dirble_page = 1; | ||||
| var isTouch = Modernizr.touch ? 1 : 0; | ||||
| var filter = ""; | ||||
| var dirble_api_token = ""; | ||||
| var dirble_stations = false; | ||||
|  | ||||
| var app = $.sammy(function() { | ||||
|  | ||||
| @@ -38,11 +41,14 @@ var app = $.sammy(function() { | ||||
|         current_app = 'queue'; | ||||
|  | ||||
|         $('#breadcrump').addClass('hide'); | ||||
|         $('#filter').addClass('hide'); | ||||
|         $('#salamisandwich').removeClass('hide').find("tr:gt(0)").remove(); | ||||
|         $('#dirble_panel').addClass('hide'); | ||||
|         socket.send('MPD_API_GET_QUEUE,'+pagination); | ||||
|  | ||||
|         $('#panel-heading').text("Queue"); | ||||
|         $('#panel-heading-info').empty(); | ||||
|  | ||||
|         $('#queue').addClass('active'); | ||||
|     } | ||||
|  | ||||
| @@ -65,7 +71,8 @@ var app = $.sammy(function() { | ||||
|         browsepath = this.params['splat'][1]; | ||||
|         pagination = parseInt(this.params['splat'][0]); | ||||
|         current_app = 'browse'; | ||||
|         $('#breadcrump').removeClass('hide').empty().append("<li><a href=\"#/browse/0/\">root</a></li>"); | ||||
|         $('#breadcrump').removeClass('hide').empty().append("<li><a uri=\"\" onclick=\"set_filter('')\">root</a></li>"); | ||||
| 		add_filter(); | ||||
|         $('#salamisandwich').removeClass('hide').find("tr:gt(0)").remove(); | ||||
|         $('#dirble_panel').addClass('hide'); | ||||
|         socket.send('MPD_API_GET_BROWSE,'+pagination+','+(browsepath ? browsepath : "/")); | ||||
| @@ -89,7 +96,7 @@ var app = $.sammy(function() { | ||||
|             } | ||||
|  | ||||
|             full_path = full_path + chunk; | ||||
|             $('#breadcrump').append("<li><a href=\"#/browse/0/" + full_path + "\">"+chunk+"</a></li>"); | ||||
|             $('#breadcrump').append("<li><a uri=\"" + full_path + "\">"+chunk+"</a></li>"); | ||||
|             full_path += "/"; | ||||
|         }); | ||||
|         $('#browse').addClass('active'); | ||||
| @@ -107,7 +114,6 @@ var app = $.sammy(function() { | ||||
|         $('#panel-heading').text("Search: "+searchstr); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     this.get(/\#\/dirble\/(\d+)\/(\d+)/, function() { | ||||
|          | ||||
|         if (TOKEN === "") context.redirect("#/0"); | ||||
| @@ -122,6 +128,8 @@ var app = $.sammy(function() { | ||||
|         $('#dirble_right').find("tr:gt(0)").remove(); | ||||
|  | ||||
|         $('#panel-heading').text("Dirble"); | ||||
|         $('#panel-heading-info').empty(); | ||||
|  | ||||
|         $('#dirble').addClass('active'); | ||||
|  | ||||
|         $('#next').addClass('hide'); | ||||
| @@ -132,9 +140,10 @@ var app = $.sammy(function() { | ||||
|         dirble_catid = this.params['splat'][0]; | ||||
|         dirble_page = this.params['splat'][1]; | ||||
|  | ||||
|         dirble_load_stations(); | ||||
|     }); | ||||
|         dirble_stations = true; | ||||
|  | ||||
|         if (dirble_api_token) { dirble_load_stations(); } | ||||
|     }); | ||||
|  | ||||
|     this.get(/\#\/dirble\//, function() { | ||||
|          | ||||
| @@ -150,9 +159,13 @@ var app = $.sammy(function() { | ||||
|         $('#dirble_right').find("tr:gt(0)").remove(); | ||||
|  | ||||
|         $('#panel-heading').text("Dirble"); | ||||
|         $('#panel-heading-info').empty(); | ||||
|  | ||||
|         $('#dirble').addClass('active'); | ||||
|  | ||||
|         dirble_load_categories(); | ||||
|         dirble_stations = false; | ||||
|  | ||||
|         if (dirble_api_token) { dirble_load_categories(); } | ||||
|     }); | ||||
|  | ||||
|     this.get("/", function(context) { | ||||
| @@ -249,7 +262,6 @@ $(document).ready(function(){ | ||||
|     if (TOKEN === "") $('#dirble').addClass('hide'); | ||||
| }); | ||||
|  | ||||
|  | ||||
| function webSocketConnect() { | ||||
|     if (typeof MozWebSocket != "undefined") { | ||||
|         socket = new MozWebSocket(get_appropriate_ws_url()); | ||||
| @@ -267,7 +279,9 @@ function webSocketConnect() { | ||||
|  | ||||
|             app.run(); | ||||
|             /* emit initial request for output names */ | ||||
|             socket.send("MPD_API_GET_OUTPUTS"); | ||||
|             socket.send('MPD_API_GET_OUTPUTS'); | ||||
|             /* emit initial request for dirble api token */ | ||||
|             socket.send('MPD_API_GET_DIRBLEAPITOKEN'); | ||||
|         } | ||||
|  | ||||
|         socket.onmessage = function got_packet(msg) { | ||||
| @@ -278,10 +292,22 @@ function webSocketConnect() { | ||||
|              | ||||
|  | ||||
|             switch (obj.type) { | ||||
|                 case "queue": | ||||
|                 case 'queue': | ||||
|                     if(current_app !== 'queue') | ||||
|                         break; | ||||
|  | ||||
|                     if (obj.totalTime > 0) { | ||||
|                         var hours = Math.floor(obj.totalTime / 3600); | ||||
|                         var minutes = Math.floor(obj.totalTime / 60) - hours * 60; | ||||
|                         var seconds = obj.totalTime - hours * 3600 - minutes * 60; | ||||
|  | ||||
|                         $('#panel-heading-info').text('Total: ' + | ||||
|                             (hours > 0 ? hours + '\u2009h ' + (minutes < 10 ? '0' : '') : '') + | ||||
|                             minutes + '\u2009m ' + (seconds < 10 ? '0' : '') + seconds + '\u2009s'); | ||||
|                     } else { | ||||
|                         $('#panel-heading-info').empty(); | ||||
|                     } | ||||
|  | ||||
|                     $('#salamisandwich > tbody').empty(); | ||||
|                     for (var song in obj.data) { | ||||
|                         var minutes = Math.floor(obj.data[song].duration / 60); | ||||
| @@ -289,9 +315,9 @@ function webSocketConnect() { | ||||
|  | ||||
|                         $('#salamisandwich > tbody').append( | ||||
|                             "<tr trackid=\"" + obj.data[song].id + "\"><td>" + (obj.data[song].pos + 1) + "</td>" + | ||||
|                                 "<td>"+ obj.data[song].title +"</td>" + | ||||
|                                 "<td>"+ obj.data[song].album +"</td>" + | ||||
|                                 "<td>"+ obj.data[song].artist +"</td>" +  | ||||
|                                 "<td>"+ obj.data[song].album +"</td>" + | ||||
|                                 "<td>"+ obj.data[song].title +"</td>" + | ||||
|                                 "<td>"+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds + | ||||
|                         "</td><td></td></tr>"); | ||||
|                     } | ||||
| @@ -352,14 +378,14 @@ function webSocketConnect() { | ||||
|                     }; | ||||
|                      | ||||
|                     //Make queue table sortable | ||||
|                     $("#salamisandwich > tbody").sortable({ | ||||
|                     $('#salamisandwich > tbody').sortable({ | ||||
|                       helper: fixHelperModified, | ||||
|                       stop: function(event,ui) {renumber_table('#salamisandwich',ui.item)} | ||||
|                     }).disableSelection(); | ||||
|                     break; | ||||
|                 case "search": | ||||
|                 case 'search': | ||||
|                     $('#wait').modal('hide'); | ||||
|                 case "browse": | ||||
|                 case 'browse': | ||||
|                     if(current_app !== 'browse' && current_app !== 'search') | ||||
|                         break; | ||||
|  | ||||
| @@ -372,45 +398,65 @@ function webSocketConnect() { | ||||
|                     } | ||||
|                     for (var item in obj.data) { | ||||
|                         switch(obj.data[item].type) { | ||||
|                             case "directory": | ||||
|                             case 'directory': | ||||
|                                 var clazz = 'dir'; | ||||
|                                 if (filter !== "") { | ||||
|                                     var first = basename(obj.data[item].dir)[0]; | ||||
|                                     if (filter === "num" && isNaN(first)) { | ||||
|                                         clazz += ' hide'; | ||||
|                                     } else if (filter >= "A" && filter <= "Z" && first.toUpperCase() !== filter) { | ||||
|                                         clazz += ' hide'; | ||||
|                                     } else if (filter === "plist") { | ||||
|                                         clazz += ' hide'; | ||||
|                                     } | ||||
|                                 } | ||||
|                                 $('#salamisandwich > tbody').append( | ||||
|                                     "<tr uri=\"" + encodeURI(obj.data[item].dir) + "\" class=\"dir\">" + | ||||
|                                     "<tr uri=\"" + encodeURI(obj.data[item].dir) + "\" class=\"" + clazz + "\">" + | ||||
|                                     "<td><span class=\"glyphicon glyphicon-folder-open\"></span></td>" + | ||||
|                                     "<td><a>" + basename(obj.data[item].dir) + "</a></td>" +  | ||||
|                                     "<td></td><td></td>" + | ||||
|                                     "<td colspan=\"3\"><a>" + basename(obj.data[item].dir) + "</a></td>" + | ||||
|                                     "<td></td><td></td></tr>" | ||||
|                                 ); | ||||
|                                 break; | ||||
|                             case "playlist": | ||||
|                             case 'playlist': | ||||
|                                 var clazz = 'plist'; | ||||
|                                 if ( (filter !== "") && (filter !== "plist") ) { | ||||
|                                     clazz += ' hide'; | ||||
|                                 } | ||||
|                                 $('#salamisandwich > tbody').append( | ||||
|                                     "<tr uri=\"" + encodeURI(obj.data[item].plist) + "\" class=\"plist\">" + | ||||
|                                     "<tr uri=\"" + encodeURI(obj.data[item].plist) + "\" class=\"" + clazz + "\">" + | ||||
|                                     "<td><span class=\"glyphicon glyphicon-list\"></span></td>" + | ||||
| 									"<td><a>" + basename(obj.data[item].plist) + "</a></td>" + | ||||
|                                     "<td></td><td></td>" + | ||||
|                                     "<td colspan=\"3\"><a>" + basename(obj.data[item].plist) + "</a></td>" + | ||||
|                                     "<td></td><td></td></tr>" | ||||
|                                 ); | ||||
|                                 break; | ||||
|                             case "song": | ||||
|                             case 'song': | ||||
|                                 var minutes = Math.floor(obj.data[item].duration / 60); | ||||
|                                 var seconds = obj.data[item].duration - minutes * 60; | ||||
|  | ||||
|                                 if (obj.data[item].artist == null) { | ||||
|                                     var artist = "<td colspan=\"2\">"; | ||||
|                                 } else { | ||||
|                                     var artist = "<td>" + obj.data[item].artist + | ||||
|                                                      "<span>" + obj.data[item].album + "</span></td><td>"; | ||||
|                                 } | ||||
|  | ||||
|                                 $('#salamisandwich > tbody').append( | ||||
|                                     "<tr uri=\"" + encodeURI(obj.data[item].uri) + "\" class=\"song\">" + | ||||
|                                     "<td><span class=\"glyphicon glyphicon-music\"></span></td>" +  | ||||
|                                     "<td>" + obj.data[item].title  + "</td>" + | ||||
|                                     "<td>" + obj.data[item].album  + "</td>" + | ||||
|                                     "<td>" + obj.data[item].artist + "</td>" +  | ||||
|                                     "<td>" + obj.data[item].album  + "</td>" + | ||||
|                                     "<td>" + obj.data[item].title  + "</td>" + | ||||
|                                     "<td>" + minutes + ":" + (seconds < 10 ? '0' : '') + seconds + | ||||
|                                     "</td><td></td></tr>" | ||||
|                                 ); | ||||
|                                 break; | ||||
|                             case "wrap": | ||||
|                             case 'wrap': | ||||
|                                 if(current_app == 'browse') { | ||||
|                                     $('#next').removeClass('hide'); | ||||
|                                 } else { | ||||
|                                     $('#salamisandwich > tbody').append( | ||||
|                                         "<tr><td><span class=\"glyphicon glyphicon-remove\"></span></td>" + | ||||
|                                         "<td>Too many results, please refine your search!</td>" +  | ||||
|                                         "<td colspan=\"3\">Too many results, please refine your search!</td>" + | ||||
|                                         "<td></td><td></td></tr>" | ||||
|                                     ); | ||||
|                                 } | ||||
| @@ -431,7 +477,7 @@ function webSocketConnect() { | ||||
|                                 socket.send(onClickAction + "," + decodeURI($(this).parents("tr").attr("uri"))); | ||||
|                             $('.top-right').notify({ | ||||
|                                 message:{ | ||||
|                                     text: $('td:nth-child(2)', $(this).parents("tr")).text() + " added" | ||||
|                                     text: "\"" + $('td:nth-last-child(3)', $(this).parents("tr")).text() + "\" added" | ||||
|                                 } }).show(); | ||||
|                             }).fadeTo('fast',1); | ||||
|                     } | ||||
| @@ -459,13 +505,15 @@ function webSocketConnect() { | ||||
|                                     pagination = 0; | ||||
|                                     browsepath = $(this).attr("uri"); | ||||
|                                     $("#browse > a").attr("href", '#/browse/'+pagination+'/'+browsepath); | ||||
| 									$('#filter > a').attr("href", '#/browse/'+pagination+'/'+browsepath); | ||||
|                                     app.setLocation('#/browse/'+pagination+'/'+browsepath); | ||||
| 									set_filter(''); | ||||
|                                     break; | ||||
|                                 case 'song': | ||||
|                                     socket.send("MPD_API_ADD_TRACK," + decodeURI($(this).attr("uri"))); | ||||
|                                     $('.top-right').notify({ | ||||
|                                         message:{ | ||||
|                                             text: $('td:nth-child(2)', this).text() + " added" | ||||
|                                             text: "\"" + $('td:nth-last-child(3)', this).text() + "\" added" | ||||
|                                         } | ||||
|                                     }).show(); | ||||
|                                     break; | ||||
| @@ -473,7 +521,7 @@ function webSocketConnect() { | ||||
|                                     socket.send("MPD_API_ADD_PLAYLIST," + decodeURI($(this).attr("uri"))); | ||||
|                                     $('.top-right').notify({ | ||||
|                                         message:{ | ||||
|                                             text: "Playlist " + $('td:nth-child(2)', this).text() + " added" | ||||
|                                             text: "\"" + $('td:nth-last-child(3)', this).text() + "\" added" | ||||
|                                         } | ||||
|                                     }).show(); | ||||
|                                     break; | ||||
| @@ -481,8 +529,19 @@ function webSocketConnect() { | ||||
|                         } | ||||
|                     }); | ||||
|  | ||||
| 					$('#breadcrump > li > a').on({ | ||||
| 						click: function() { | ||||
| 							pagination = 0; | ||||
| 							browsepath = $(this).attr("uri"); | ||||
| 							$("#browse > a").attr("href", '#/browse/'+pagination+'/'+browsepath); | ||||
| 							$('#filter > a').attr("href", '#/browse/'+pagination+'/'+browsepath); | ||||
| 							app.setLocation('#/browse/'+pagination+'/'+browsepath); | ||||
| 							set_filter(''); | ||||
| 						} | ||||
| 					}); | ||||
|  | ||||
|                     break; | ||||
|                 case "state": | ||||
|                 case 'state': | ||||
|                     updatePlayIcon(obj.data.state); | ||||
|                     updateVolumeIcon(obj.data.volume); | ||||
|  | ||||
| @@ -536,16 +595,20 @@ function webSocketConnect() { | ||||
|  | ||||
|                     last_state = obj; | ||||
|                     break; | ||||
|                 case "outputnames": | ||||
|                 case 'outputnames': | ||||
|                     $('#btn-outputs-block button').remove(); | ||||
|                     if ( Object.keys(obj.data).length ) { | ||||
| 		        $.each(obj.data, function(id, name){ | ||||
|                             var btn = $('<button id="btnoutput'+id+'" class="btn btn-default" onclick="toggleoutput(this, '+id+')"><span class="glyphicon glyphicon-volume-up"></span> '+name+'</button>'); | ||||
|                             btn.appendTo($('#btn-outputs-block')); | ||||
|                         }); | ||||
| 		    } else { | ||||
|                         $('#btn-outputs-block').addClass('hide'); | ||||
| 		    } | ||||
|                     /* remove cache, since the buttons have been recreated */ | ||||
|                     last_outputs = ''; | ||||
|                     break; | ||||
|                 case "outputs": | ||||
|                 case 'outputs': | ||||
|                     if(JSON.stringify(obj) === JSON.stringify(last_outputs)) | ||||
|                         break; | ||||
|                     $.each(obj.data, function(id, enabled){ | ||||
| @@ -556,7 +619,7 @@ function webSocketConnect() { | ||||
|                     }); | ||||
|                     last_outputs = obj; | ||||
|                     break; | ||||
|                 case "disconnected": | ||||
|                 case 'disconnected': | ||||
|                     if($('.top-right').has('div').length == 0) | ||||
|                         $('.top-right').notify({ | ||||
|                             message:{text:"ympd lost connection to MPD "}, | ||||
| @@ -564,12 +627,12 @@ function webSocketConnect() { | ||||
|                             fadeOut: { enabled: true, delay: 1000 }, | ||||
|                         }).show(); | ||||
|                     break; | ||||
|                 case "update_queue": | ||||
|                 case 'update_queue': | ||||
|                     if(current_app === 'queue') | ||||
|                         socket.send('MPD_API_GET_QUEUE,'+pagination); | ||||
|                     break; | ||||
|                 case "song_change": | ||||
|  | ||||
|                 case 'song_change': | ||||
|                     updatePageTitle(obj.data); | ||||
|                     $('#album').text(""); | ||||
|                     $('#artist').text(""); | ||||
|  | ||||
| @@ -578,14 +641,14 @@ function webSocketConnect() { | ||||
|                     $('#currenttrack').text(" " + obj.data.title); | ||||
|                     var notification = "<strong><h4>" + obj.data.title + "</h4></strong>"; | ||||
|  | ||||
|                     if(obj.data.album) { | ||||
|                         $('#album').text(obj.data.album); | ||||
|                         notification += obj.data.album + "<br />"; | ||||
|                     } | ||||
|                     if(obj.data.artist) { | ||||
|                         $('#artist').text(obj.data.artist); | ||||
|                         notification += obj.data.artist + "<br />"; | ||||
|                     } | ||||
|                     if(obj.data.album) { | ||||
|                         $('#album').text(obj.data.album); | ||||
|                         notification += obj.data.album + "<br />"; | ||||
|                     } | ||||
|  | ||||
|                     if ($.cookie("notification") === "true") | ||||
|                         songNotify(obj.data.title, obj.data.artist, obj.data.album ); | ||||
| @@ -594,16 +657,28 @@ function webSocketConnect() { | ||||
|                             message:{html: notification}, | ||||
|                             type: "info", | ||||
|                         }).show();         | ||||
|                          | ||||
|                     break; | ||||
|                 case "mpdhost": | ||||
|                 case 'mpdhost': | ||||
|                     $('#mpdhost').val(obj.data.host); | ||||
|                     setLocalStream(obj.data.host); | ||||
|                     $('#mpdport').val(obj.data.port); | ||||
|                     if(obj.data.passwort_set) | ||||
|                         $('#mpd_password_set').removeClass('hide'); | ||||
|                     break; | ||||
|                 case "error": | ||||
|                 case 'dirbleapitoken': | ||||
|                     dirble_api_token = obj.data; | ||||
|                      | ||||
| 		    if (dirble_api_token) { | ||||
| 		        $('#dirble').removeClass('hide'); | ||||
|  | ||||
|                         if (dirble_stations) { dirble_load_stations();   } | ||||
|                         else {                 dirble_load_categories(); } | ||||
|  | ||||
|                     } else { | ||||
|                         $('#dirble').addClass('hide'); | ||||
| 		    } | ||||
|                     break; | ||||
|                 case 'error': | ||||
|                     $('.top-right').notify({ | ||||
|                         message:{text: obj.data}, | ||||
|                         type: "danger", | ||||
| @@ -611,9 +686,8 @@ function webSocketConnect() { | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|  | ||||
|         socket.onclose = function(){ | ||||
|             console.log("disconnected"); | ||||
|             $('.top-right').notify({ | ||||
| @@ -699,6 +773,20 @@ var updatePlayIcon = function(state) | ||||
|     } | ||||
| } | ||||
|  | ||||
| var updatePageTitle = function(songInfo) { | ||||
|     if(!songInfo || (!songInfo.artist && !songInfo.title)) { | ||||
|         document.title = 'ympd'; | ||||
|         return; | ||||
|     } | ||||
|     if(songInfo.artist) { | ||||
|         if(songInfo.title) { | ||||
|             document.title = songInfo.artist + ' - ' + songInfo.title; | ||||
|         } | ||||
|     } else { | ||||
|         document.title = songInfo.title; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function updateDB() { | ||||
|     socket.send('MPD_API_UPDATE_DB'); | ||||
|     $('.top-right').notify({ | ||||
| @@ -713,16 +801,6 @@ function clickPlay() { | ||||
|         socket.send('MPD_API_SET_PAUSE'); | ||||
| } | ||||
|  | ||||
| function renumber_table(tableID,item) { | ||||
|     was = item.children("td").first().text();//Check if first item exists! | ||||
|     is = item.index() + 1;//maybe add pagination | ||||
|  | ||||
|     if (was != is) { | ||||
|         socket.send("MPD_API_MOVE_TRACK," + was + "," + is); | ||||
|         socket.send('MPD_API_GET_QUEUE,'+pagination); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function clickLocalPlay() { | ||||
|     var player = document.getElementById('player'); | ||||
|     $("#localplay-icon").removeClass("glyphicon-play").removeClass("glyphicon-pause"); | ||||
| @@ -777,6 +855,16 @@ function trash(tr) { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function renumber_table(tableID,item) { | ||||
|     was = item.children("td").first().text();//Check if first item exists! | ||||
|     is = item.index() + 1;//maybe add pagination | ||||
|  | ||||
|     if (was != is) { | ||||
|         socket.send("MPD_API_MOVE_TRACK," + was + "," + is); | ||||
|         socket.send('MPD_API_GET_QUEUE,'+pagination); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function basename(path) { | ||||
|     return path.split('/').reverse()[0]; | ||||
| } | ||||
| @@ -982,7 +1070,7 @@ function dirble_load_categories() { | ||||
|  | ||||
|     dirble_page = 1; | ||||
|  | ||||
|     $.getJSON( "http://api.dirble.com/v2/categories?token="+TOKEN, function( data ) { | ||||
|     $.getJSON( "https://api.dirble.com/v2/categories?token=" + dirble_api_token, function( data ) { | ||||
|  | ||||
|         $('#dirble_loading').addClass('hide'); | ||||
|  | ||||
| @@ -1030,7 +1118,7 @@ function dirble_load_categories() { | ||||
|  | ||||
| function dirble_load_stations() { | ||||
|  | ||||
|     $.getJSON( "http://api.dirble.com/v2/category/"+dirble_catid+"/stations?page="+dirble_page+"&per_page=20&token="+TOKEN, function( data ) { | ||||
|     $.getJSON( "https://api.dirble.com/v2/category/"+dirble_catid+"/stations?page="+dirble_page+"&per_page=20&token=" + dirble_api_token, function( data ) { | ||||
|  | ||||
|         $('#dirble_loading').addClass('hide'); | ||||
|         if (data.length == 20) $('#next').removeClass('hide'); | ||||
| @@ -1057,7 +1145,7 @@ function dirble_load_stations() { | ||||
|             click: function() { | ||||
|                 var _this = $(this); | ||||
|  | ||||
|                 $.getJSON( "http://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token="+TOKEN, function( data ) { | ||||
|                 $.getJSON( "https://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token=" + dirble_api_token, function( data ) { | ||||
|  | ||||
|                     socket.send("MPD_API_ADD_TRACK," + data.streams[0].stream); | ||||
|                     $('.top-right').notify({ | ||||
| @@ -1075,7 +1163,7 @@ function dirble_load_stations() { | ||||
|                 "<span class=\"glyphicon glyphicon-play\"></span></a>").find('a').click(function(e) { | ||||
|                     e.stopPropagation(); | ||||
|  | ||||
|                     $.getJSON( "http://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token="+TOKEN, function( data ) { | ||||
|                     $.getJSON( "https://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token=" + dirble_api_token, function( data ) { | ||||
|  | ||||
|                         socket.send("MPD_API_ADD_PLAY_TRACK," + data.streams[0].stream); | ||||
|                         $('.top-right').notify({ | ||||
| @@ -1096,7 +1184,7 @@ function dirble_load_stations() { | ||||
|             click: function() { | ||||
|                 var _this = $(this); | ||||
|  | ||||
|                 $.getJSON( "http://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token="+TOKEN, function( data ) { | ||||
|                 $.getJSON( "https://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token=" + dirble_api_token, function( data ) { | ||||
|  | ||||
|                     socket.send("MPD_API_ADD_TRACK," + data.streams[0].stream); | ||||
|                     $('.top-right').notify({ | ||||
| @@ -1114,7 +1202,7 @@ function dirble_load_stations() { | ||||
|                 "<span class=\"glyphicon glyphicon-play\"></span></a>").find('a').click(function(e) { | ||||
|                     e.stopPropagation(); | ||||
|  | ||||
|                     $.getJSON( "http://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token="+TOKEN, function( data ) { | ||||
|                     $.getJSON( "https://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token=" + dirble_api_token, function( data ) { | ||||
|  | ||||
|                         socket.send("MPD_API_ADD_PLAY_TRACK," + data.streams[0].stream); | ||||
|                         $('.top-right').notify({ | ||||
| @@ -1132,3 +1220,53 @@ function dirble_load_stations() { | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function set_filter (c) { | ||||
|     filter = c; | ||||
| 	$('#filter > a').removeClass('active'); | ||||
| 	$('#f' + c).addClass('active'); | ||||
|  | ||||
|     if (filter === "") { | ||||
|     	$('#salamisandwich > tbody > tr').removeClass('hide'); | ||||
| 	} else if (filter === "plist") { | ||||
|     	$('#salamisandwich > tbody > tr.dir').addClass('hide'); | ||||
|     	$('#salamisandwich > tbody > tr.song').addClass('hide'); | ||||
|     	$('#salamisandwich > tbody > tr.plist').removeClass('hide'); | ||||
|     } else { | ||||
| 		$.each($('#salamisandwich > tbody > tr'), function(i, line) { | ||||
| 			var first = basename($(line).attr('uri'))[0]; | ||||
| 			if ( $(line).hasClass('song') ) { | ||||
| 				first = $(line).children().eq(3).text()[0]; | ||||
| 			} | ||||
|  | ||||
| 			if (filter === "num") { | ||||
| 				if (!isNaN(first)) { | ||||
| 					$(line).removeClass('hide'); | ||||
| 				} else { | ||||
| 					$(line).addClass('hide'); | ||||
| 				} | ||||
| 			} else if (filter >= "A" && filter <= "Z") { | ||||
| 				if (first.toUpperCase() === filter) { | ||||
| 					$(line).removeClass('hide'); | ||||
| 				} else { | ||||
| 					$(line).addClass('hide'); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function add_filter () { | ||||
| 	$('#filter').empty(); | ||||
|     $('#filter').append(' <a onclick="set_filter(\'\')" href="#/browse/'+pagination+'/'+browsepath+'">All</a>'); | ||||
|     $('#filter').append(' <a id="fnum" onclick="set_filter(\'num\')" href="#/browse/'+pagination+'/'+browsepath+'">#</a>'); | ||||
|  | ||||
|     for (i = 65; i <= 90; i++) { | ||||
|         var c = String.fromCharCode(i); | ||||
|         $('#filter').append(' <a id="f' + c + '" onclick="set_filter(\'' + c + '\');" href="#/browse/' + pagination + '/' + browsepath + '">' + c + '</a>'); | ||||
|     } | ||||
|  | ||||
|     $('#filter').append(' <a id="fplist" onclick="set_filter(\'plist\')" href="#/browse/'+pagination+'/'+browsepath+'" class="glyphicon glyphicon-list"></a>'); | ||||
| 	$('#f' + filter).addClass('active'); | ||||
|     $('#filter').removeClass('hide'); | ||||
| } | ||||
|   | ||||
| @@ -64,7 +64,8 @@ int callback_mpd(struct mg_connection *c) | ||||
|         return MG_TRUE; | ||||
|  | ||||
|     if(mpd.conn_state != MPD_CONNECTED && cmd_id != MPD_API_SET_MPDHOST && | ||||
|         cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS) | ||||
|         cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS && | ||||
|         cmd_id != MPD_API_GET_DIRBLEAPITOKEN) | ||||
|         return MG_TRUE; | ||||
|  | ||||
|     switch(cmd_id) | ||||
| @@ -290,6 +291,10 @@ out_host_change: | ||||
|                 "{\"host\" : \"%s\", \"port\": \"%d\", \"passwort_set\": %s}" | ||||
|                 "}", mpd.host, mpd.port, mpd.password ? "true" : "false"); | ||||
|             break; | ||||
|         case MPD_API_GET_DIRBLEAPITOKEN: | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"dirbleapitoken\", \"" | ||||
|                 "data\": \"%s\"}", dirble_api_token); | ||||
|             break; | ||||
|         case MPD_API_SET_MPDPASS: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_MPDPASS")) | ||||
| @@ -590,18 +595,10 @@ int mpd_put_current_song(char *buffer) | ||||
|     cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); | ||||
|     cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); | ||||
|  | ||||
|     if(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0) != NULL) | ||||
|     { | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)); | ||||
|     } | ||||
|  | ||||
|     if(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) != NULL) | ||||
|     { | ||||
|     cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)); | ||||
|     } | ||||
|     cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); | ||||
|  | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}}"); | ||||
|     mpd_song_free(song); | ||||
| @@ -615,6 +612,7 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_entity *entity; | ||||
|     unsigned long totalTime = 0; | ||||
|  | ||||
|     if (!mpd_send_list_queue_range_meta(mpd.conn, offset, offset+MAX_ELEMENTS_PER_PAGE)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_queue_meta"); | ||||
| @@ -623,23 +621,31 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|  | ||||
|     while((entity = mpd_recv_entity(mpd.conn)) != NULL) { | ||||
|         const struct mpd_song *song; | ||||
|         unsigned int drtn; | ||||
|  | ||||
|         if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { | ||||
|             song = mpd_entity_get_song(entity); | ||||
|             drtn = mpd_song_get_duration(song); | ||||
|  | ||||
|             cur += json_emit_raw_str(cur, end - cur, "{\"id\":"); | ||||
|             cur += json_emit_int(cur, end - cur, mpd_song_get_id(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"pos\":"); | ||||
|             cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); | ||||
|             cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); | ||||
|             cur += json_emit_int(cur, end - cur, drtn); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|  | ||||
|             totalTime += drtn; | ||||
|         } | ||||
|         mpd_entity_free(entity); | ||||
|     } | ||||
| @@ -647,7 +653,9 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|     /* remove last ',' */ | ||||
|     cur--; | ||||
|  | ||||
|     cur += json_emit_raw_str(cur, end - cur, "]}"); | ||||
|     cur += json_emit_raw_str(cur, end - cur, "],\"totalTime\":"); | ||||
|     cur += json_emit_int(cur, end - cur, totalTime); | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| @@ -761,6 +769,10 @@ int mpd_search(char *buffer, char *searchstr) | ||||
|             cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|             mpd_song_free(song); | ||||
|  | ||||
|   | ||||
| @@ -40,6 +40,7 @@ | ||||
|     X(MPD_API_GET_QUEUE) \ | ||||
|     X(MPD_API_GET_BROWSE) \ | ||||
|     X(MPD_API_GET_MPDHOST) \ | ||||
|     X(MPD_API_GET_DIRBLEAPITOKEN) \ | ||||
|     X(MPD_API_ADD_TRACK) \ | ||||
|     X(MPD_API_ADD_PLAY_TRACK) \ | ||||
|     X(MPD_API_ADD_PLAYLIST) \ | ||||
| @@ -83,8 +84,10 @@ enum mpd_conn_states { | ||||
|  | ||||
| struct t_mpd { | ||||
|     int port; | ||||
| 	int local_port; | ||||
|     char host[128]; | ||||
|     char *password; | ||||
| 	char *gpass; | ||||
|  | ||||
|     struct mpd_connection *conn; | ||||
|     enum mpd_conn_states conn_state; | ||||
| @@ -97,6 +100,8 @@ struct t_mpd { | ||||
|     unsigned queue_version; | ||||
| } mpd; | ||||
|  | ||||
| char dirble_api_token[28]; | ||||
|  | ||||
| struct t_mpd_client_session { | ||||
|     int song_id; | ||||
|     unsigned queue_version; | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/ympd.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/ympd.c
									
									
									
									
									
								
							| @@ -38,8 +38,6 @@ void bye() | ||||
|     force_exit = 1; | ||||
| } | ||||
|  | ||||
| char *gpass = NULL; | ||||
|  | ||||
| static int server_callback(struct mg_connection *c, enum mg_event ev) { | ||||
|     int result = MG_FALSE; | ||||
|     FILE *fp = NULL; | ||||
| @@ -63,10 +61,10 @@ static int server_callback(struct mg_connection *c, enum mg_event ev) { | ||||
| #endif | ||||
|         case MG_AUTH: | ||||
|             // no auth for websockets since mobile safari does not support it | ||||
|             if ( (gpass == NULL) || (c->is_websocket) ) | ||||
|             if ( (mpd.gpass == NULL) || (c->is_websocket) || ((mpd.local_port > 0) && (c->local_port == mpd.local_port)) ) | ||||
|                 return MG_TRUE; | ||||
|             else { | ||||
|                 if ( (fp = fopen(gpass, "r")) != NULL ) { | ||||
|                 if ( (fp = fopen(mpd.gpass, "r")) != NULL ) { | ||||
|                     result = mg_authorize_digest(c, fp); | ||||
|                     fclose(fp); | ||||
|                 } | ||||
| @@ -93,24 +91,29 @@ int main(int argc, char **argv) | ||||
|  | ||||
|     mg_set_option(server, "auth_domain", "ympd"); | ||||
|     mpd.port = 6600; | ||||
|     mpd.local_port = 0; | ||||
| 	mpd.gpass = NULL; | ||||
|     strcpy(mpd.host, "127.0.0.1"); | ||||
|  | ||||
|     static struct option long_options[] = { | ||||
|         {"digest",       required_argument, 0, 'd'}, | ||||
|         {"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'}, | ||||
|         {"dirbletoken",  required_argument, 0, 'd'}, | ||||
|         {"user",         required_argument, 0, 'u'}, | ||||
|         {"version",      no_argument,       0, 'v'}, | ||||
|         {"help",         no_argument,       0,  0 }, | ||||
|         {"mpdpass",      required_argument, 0, 'm'}, | ||||
|         {0,              0,                 0,  0 } | ||||
|     }; | ||||
|  | ||||
|     while((n = getopt_long(argc, argv, "d:h:p:w:u:v", | ||||
|     while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:v:m", | ||||
|                 long_options, &option_index)) != -1) { | ||||
|         switch (n) { | ||||
|             case 'd': | ||||
|                 gpass = strdup(optarg); | ||||
|             case 'D': | ||||
|                 mpd.gpass = strdup(optarg); | ||||
|                 break; | ||||
|             case 'h': | ||||
|                 strncpy(mpd.host, optarg, sizeof(mpd.host)); | ||||
| @@ -118,12 +121,22 @@ 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; | ||||
|             case 'd': | ||||
|                 strncpy(dirble_api_token, optarg, sizeof(dirble_api_token)); | ||||
|                 break; | ||||
|             case 'u': | ||||
|                 run_as_user = strdup(optarg); | ||||
|                 break; | ||||
|             case 'm': | ||||
|                 if (strlen(optarg) > 0) | ||||
|                     mpd.password = strdup(optarg); | ||||
|                 break; | ||||
|             case 'v': | ||||
|                 fprintf(stdout, "ympd  %d.%d.%d\n" | ||||
|                         "Copyright (C) 2014 Andrew Karpow <andy@ndyk.de>\n" | ||||
| @@ -133,13 +146,16 @@ 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" | ||||
|                         " -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" | ||||
|                         " -d, --dirbletoken <apitoken>\tDirble API token\n" | ||||
|                         " -v, --version\t\t\tget version\n" | ||||
|                         " -m, --mpdpass <password>\tspecifies the password to use when connecting to mpd\n" | ||||
|                         " --help\t\t\t\tthis help\n" | ||||
|                         , argv[0]); | ||||
|                 return EXIT_FAILURE; | ||||
|   | ||||
							
								
								
									
										3
									
								
								ympd.1
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								ympd.1
									
									
									
									
									
								
							| @@ -23,6 +23,9 @@ specifies the port for the webserver to listen to, defaults to 8080 | ||||
| \fB\-u\fR, \fB\-\-user username\fR | ||||
| drop privileges to the provided username after socket binding | ||||
| .TP | ||||
| \fB\-m\fR, \fB\-\-mpdpass password\fR | ||||
| specifies the password to use when connecting to mpd | ||||
| .TP | ||||
| \fB\-V\fR, \fB\-\-version\fR | ||||
| print version and exit | ||||
| .TP | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 SuperBFG7
					SuperBFG7