mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-31 13:53:00 +00:00 
			
		
		
		
	Merge branch 'browse-view-rewrite'
This commit is contained in:
		| @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 2.6) | ||||
| project (mympd C) | ||||
| set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") | ||||
| set(CPACK_PACKAGE_VERSION_MAJOR "2") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "1") | ||||
| set(CPACK_PACKAGE_VERSION_PATCH "1") | ||||
| set(CPACK_PACKAGE_VERSION_MINOR "2") | ||||
| set(CPACK_PACKAGE_VERSION_PATCH "0") | ||||
| if(CMAKE_BUILD_TYPE MATCHES RELEASE) | ||||
|     set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs") | ||||
| else() | ||||
|   | ||||
| @@ -54,7 +54,7 @@ Usage: ./mympd [OPTION]... | ||||
|  -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] | ||||
|  -V, --version                 get version | ||||
|  -v, --version                 get version | ||||
|  --help                        this help | ||||
| ``` | ||||
|  | ||||
|   | ||||
| Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB | 
							
								
								
									
										
											BIN
										
									
								
								htdocs/assets/coverimage-notavailable.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								htdocs/assets/coverimage-notavailable.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.9 KiB | 
| @@ -21,12 +21,10 @@ body { | ||||
|   width: 104px; | ||||
| } | ||||
|  | ||||
|  | ||||
| button { | ||||
|   overflow: hidden; | ||||
| } | ||||
|  | ||||
|  | ||||
| #browseBreadcrumb { | ||||
|   overflow: auto; | ||||
|   white-space: nowrap; | ||||
| @@ -37,7 +35,7 @@ button { | ||||
| } | ||||
|  | ||||
| #counter { | ||||
|   font-size: 24px; | ||||
|   font-size: 22px; | ||||
|   margin-top: -2px; | ||||
|   margin-left: 10px; | ||||
|   min-width: 50px; | ||||
| @@ -47,20 +45,11 @@ button { | ||||
|   width: 200px; | ||||
| } | ||||
|  | ||||
| h1 { | ||||
|   display: block; | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
| .card { | ||||
|   min-height:350px; | ||||
| } | ||||
|  | ||||
| @media only screen and (max-width: 576px) { | ||||
| /*  .tbllength, .tblnum, .tblalbum { | ||||
|     visibility:collapse; | ||||
|   } | ||||
|   .tbltitle, .tblartist { | ||||
|    min-width:calc(50% - 15px); | ||||
|   } | ||||
| */ | ||||
|   .header-logo { | ||||
|     display:none !important; | ||||
|   } | ||||
| @@ -85,14 +74,6 @@ tbody { | ||||
|   background-color:#eee; | ||||
| } | ||||
|  | ||||
| #filter > a.active { | ||||
|   font-weight: bold; | ||||
|   pointer-events: none; | ||||
|   cursor: default; | ||||
|   text-decoration: none; | ||||
|   color: black; | ||||
| } | ||||
|  | ||||
| .hide { | ||||
|  display: none !important; | ||||
| } | ||||
| @@ -101,7 +82,7 @@ tbody { | ||||
|  float: right !important; | ||||
| } | ||||
|  | ||||
| #queue-buttons { | ||||
| #queue-buttons, #browsePlaylistsButtons, #searchButtons, #browseFilesystemButtons, #browseDatabaseButtons { | ||||
|  margin-bottom:20px; | ||||
| } | ||||
|  | ||||
| @@ -129,7 +110,7 @@ tbody { | ||||
|   word-wrap: normal; | ||||
|   white-space: nowrap; | ||||
|   direction: ltr; | ||||
|  | ||||
|   vertical-align: top; | ||||
|   /* Support for all WebKit browsers. */ | ||||
|   -webkit-font-smoothing: antialiased; | ||||
|   /* Support for Safari and Chrome. */ | ||||
| @@ -197,4 +178,21 @@ main { | ||||
|  font-size:2rem; | ||||
|  float:left; | ||||
|  margin-right:5px; | ||||
| } | ||||
|  | ||||
| #browseFilesystemFilterLetters > button { | ||||
|  min-width:28px; | ||||
| } | ||||
|  | ||||
| .col-md { | ||||
|  min-width:250px; | ||||
|  max-width:350px; | ||||
| } | ||||
|  | ||||
| .card-img-top { | ||||
|  min-height:250px; | ||||
|  background-image:url('/assets/coverimage-notavailable.png'); | ||||
|  background-repeat:no-repeat; | ||||
|  background-color:#d45500; | ||||
|  cursor:pointer; | ||||
| } | ||||
| @@ -30,7 +30,7 @@ | ||||
|             </form> | ||||
|             <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();">Update Database</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-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> | ||||
| @@ -38,13 +38,13 @@ | ||||
|         </div> | ||||
|         <div class="btn-toolbar col-auto" role="toolbar"> | ||||
|           <div class="btn-group mr-2" role="group"> | ||||
|             <button type="button" class="btn btn-secondary" onclick="socket.send('MPD_API_SET_PREV');"> | ||||
|             <button id="btnPrev" type="button" class="btn btn-secondary" onclick="socket.send('MPD_API_SET_PREV');"> | ||||
|               <span class="material-icons">skip_previous</span> | ||||
|             </button> | ||||
|             <button type="button" class="btn btn-secondary" onclick="clickPlay();"> | ||||
|               <span id="play-icon" class="material-icons">pause</span> | ||||
|             <button id="btnPlay" type="button" class="btn btn-secondary" onclick="clickPlay();"> | ||||
|               <span class="material-icons">pause</span> | ||||
|             </button> | ||||
|             <button type="button" class="btn btn-secondary" onclick="socket.send('MPD_API_SET_NEXT');"> | ||||
|             <button id="btnNext" type="button" class="btn btn-secondary" onclick="socket.send('MPD_API_SET_NEXT');"> | ||||
|               <span class="material-icons">skip_next</span> | ||||
|             </button> | ||||
|           </div> | ||||
| @@ -54,7 +54,7 @@ | ||||
|             </button> | ||||
|             <div class="dropdown-menu dropdown-menu-right bg-dark"> | ||||
|               <h1 class="dropdown-header text-light">Volume: <span id="volumePrct"></span></h1> | ||||
|               <form class="px-4 py-3" style="padding-top:0px !important;" id="volumeControl"> | ||||
|               <form class="px-4 py-0 pb-3" id="volumeControl"> | ||||
|                 <div class="btn-group" role="group"> | ||||
|                   <input type="button" class="btn btn-secondary" value="−" onclick="chVolume(-5)"/> | ||||
|                   <div class="btn btn-secondary"> | ||||
| @@ -95,7 +95,7 @@ | ||||
|      | ||||
|     <div class="card hide" id="cardQueue"> | ||||
|       <div class="card-header"> | ||||
|         <a href="#" data-toggle="collapse" data-target="#queue-buttons" style="color:black;">Queue</a> | ||||
|         <a href="#" data-toggle="collapse" data-target="#queue-buttons" class="text-dark">Queue</a> | ||||
|         <span id="panel-heading-queue" class="text pull-right"></span> | ||||
|       </div> | ||||
|       <div class="card-body"> | ||||
| @@ -105,16 +105,16 @@ | ||||
|             <div class="dropdown-menu bg-dark px-2" id="trashmodebtns"> | ||||
|               <h6 class="dropdown-header text-light">Trashmode</h6> | ||||
|               <button id="btntrashmodeup" type="button" class="btn btn-secondary btn-block"> | ||||
|                 <span class="material-icons" style="float:left;">vertical_align_top</span> | ||||
|                 <span style="margin-left:1rem;">Delete upward</span> | ||||
|                 <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"> | ||||
|                 <span class="material-icons" style="float:left;">delete</span> | ||||
|                 <span style="margin-left:1rem;">Delete single</span> | ||||
|                 <span class="material-icons float-left">delete</span> | ||||
|                 <span class="ml-3">Delete single</span> | ||||
|               </button> | ||||
|               <button id="btntrashmodedown" type="button" class="btn btn-secondary btn-block"> | ||||
|                 <span class="material-icons" style="float:left;">vertical_align_bottom</span> | ||||
|                 <span style="margin-left:1rem;">Delete downward</span> | ||||
|                 <span class="material-icons float-left">vertical_align_bottom</span> | ||||
|                 <span class="ml-3">Delete downward</span> | ||||
|               </button> | ||||
|             </div> | ||||
|           </div> | ||||
| @@ -133,7 +133,10 @@ | ||||
|             <div class="input-group mr-2"> | ||||
|               <input type="text" class="form-control" placeholder="Search Queue" id="searchqueuestr"/> | ||||
|               <div class="input-group-append"> | ||||
|                 <button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"><span class="material-icons">search</span></button> | ||||
|                 <button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"> | ||||
|                   <span class="material-icons">search</span> | ||||
|                   <span id="searchqueuetagdesc">Any Tag</span> | ||||
|                 </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> | ||||
| @@ -193,25 +196,154 @@ | ||||
|             <button onclick="gotoPage('next',this,event)" id="queuePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|          | ||||
|       </div> | ||||
|     </div> | ||||
|      | ||||
|     <div class="card hide" id="cardBrowse"> | ||||
|       <div class="card-header" id="panel-heading-browse">Browse</div> | ||||
|       <div class="card-body"> | ||||
|         <ol id="browseBreadcrumb" class="breadcrumb"> | ||||
|         </ol> | ||||
|  | ||||
|         <div class="btn-toolbar" id="filter-toolbar"> | ||||
|           <div class="btn-group mr-2 flex-wrap" data-toggle="radio" id="filter"></div> | ||||
|           <div class="btn-group mr-2 pull-right"> | ||||
|             <button id="add-all-songs" class="btn btn-secondary">Add all</button> | ||||
|       <div class="card-header" id="panel-heading-browse"> | ||||
|         <ul class="nav nav-tabs card-header-tabs"> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link text-dark" href="#/browse/filesystem/0/!/" id="cardBrowseNavFilesystem">Filesystem</a> | ||||
|           </li> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link text-dark" href="#/browse/playlists/0" id="cardBrowseNavPlaylists">Playlists</a> | ||||
|           </li> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link text-dark" href="#/browse/database/0/" id="cardBrowseNavDatabase">Database</a> | ||||
|           </li> | ||||
|         </ul> | ||||
|       </div> | ||||
|        | ||||
|       <div class="card-body hide" id="cardBrowsePlaylists"> | ||||
|         <div class="btn-toolbar collapse show" id="browsePlaylistsButtons" role="toolbar"> | ||||
|           <div id="browsePlaylistsPaginationTop" class="btn-group mr-2"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="browsePlaylistsPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="browsePlaylistsPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="browsePlaylistsPaginationTopPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="browsePlaylistsPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="table-responsive-md"> | ||||
|           <table id="browseList" class="table table-hover table-sm"> | ||||
|           <table id="browsePlaylistsList" class="table table-hover table-sm"> | ||||
|             <col class="tblnum"/> | ||||
|             <col class="tbltitle"/>   | ||||
|             <col class="tbllastmodified"/>           | ||||
|             <col class="tblaction"/> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 <th></th> | ||||
|                 <th>Playlist</th> | ||||
|                 <th>Last modified</th> | ||||
|                 <th></th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
|         <div class="btn-toolbar" id="browsePlaylistsButtonsBottom" role="toolbar"> | ||||
|           <div class="btn-group mr-2"> | ||||
|             <button type="button" class="btn btn-secondary" onclick="scrollToTop()" title="To top"> | ||||
|               <span class="material-icons">keyboard_arrow_up</span> | ||||
|             </button> | ||||
|           </div>         | ||||
|           <div id="browsePlaylistsPaginationBottom" class="btn-group mr-2 dropup"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="browsePlaylistsPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="browsePlaylistsPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="browsePlaylistsPaginationBottomPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="browsePlaylistsPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="card-body hide" id="cardBrowseDatabase"> | ||||
|  | ||||
|         <div class="btn-toolbar collapse show" id="browseDatabaseButtons" role="toolbar"> | ||||
|           <div class="btn-group mr-2"> | ||||
|             <button id="btnBrowseDatabaseArtist" onclick="app.setLocation('#/browse/database/0/');" type="button" class="btn btn-secondary hide">« Artists</button> | ||||
|           </div> | ||||
|           <div id="browseDatabasePaginationTop" class="btn-group mr-2"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="browseDatabasePaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="browseDatabasePaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="browseDatabasePaginationTopPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="browseDatabasePaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="table-responsive-md"> | ||||
|           <table id="browseDatabaseList" class="table table-hover table-sm"> | ||||
|             <col class="tblnum"/> | ||||
|             <col class="tbltitle"/>             | ||||
|             <col class="tblaction"/> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 <th></th> | ||||
|                 <th>Artist</th> | ||||
|                 <th></th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
|          | ||||
|         <div id="browseDatabaseCards" class="row"></div> | ||||
|          | ||||
|         <div class="btn-toolbar" id="browseDatabaseButtonsBottom" role="toolbar"> | ||||
|           <div class="btn-group mr-2"> | ||||
|             <button type="button" class="btn btn-secondary" onclick="scrollToTop()" title="To top"> | ||||
|               <span class="material-icons">keyboard_arrow_up</span> | ||||
|             </button> | ||||
|           </div>         | ||||
|           <div id="browseDatabasePaginationBottom" class="btn-group mr-2 dropup"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="browseDatabasePaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="browseDatabasePaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="browseDatabasePaginationBottomPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="browseDatabasePaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|       </div> | ||||
|  | ||||
|       <div class="card-body hide" id="cardBrowseFilesystem"> | ||||
|         <ol id="browseBreadcrumb" class="breadcrumb"> | ||||
|         </ol> | ||||
|  | ||||
|         <div class="btn-toolbar collapse show" id="browseFilesystemButtons" role="toolbar"> | ||||
|           <div class="btn-group mr-2 pull-right"> | ||||
|             <button id="browseFilesystemAddAllSongs" class="btn btn-secondary">Add all</button> | ||||
|           </div> | ||||
|           <div class="btn-group mr-2"> | ||||
|             <button id="browseFilesystemFilter" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Filter</button> | ||||
|             <div class="dropdown-menu bg-dark px-2" id="browseFilesystemFilterLetters"> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div id="browseFilesystemPaginationTop" class="btn-group mr-2"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="browseFilesystemPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="browseFilesystemPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="browseFilesystemPaginationTopPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="browseFilesystemPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="table-responsive-md"> | ||||
|           <table id="browseFilesystemList" class="table table-hover table-sm"> | ||||
|             <col class="tblnum"/> | ||||
|             <col class="tbltitle"/>             | ||||
|             <col class="tblartist"/> | ||||
| @@ -232,16 +364,62 @@ | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
|         <ul id="browsePagination" class="pagination justify-content-center hide"> | ||||
|           <li id="browsePrev" class="page-item disabled"><a class="page-link text-secondary" href="">Previous</a></li> | ||||
|           <li id="browseNext" class="page-item disabled"><a class="page-link text-secondary" href="">Next</a></li> | ||||
|         </ul> | ||||
|         <div class="btn-toolbar" id="browseFilesystemButtonsBottom" role="toolbar"> | ||||
|           <div class="btn-group mr-2"> | ||||
|             <button type="button" class="btn btn-secondary" onclick="scrollToTop()" title="To top"> | ||||
|               <span class="material-icons">keyboard_arrow_up</span> | ||||
|             </button> | ||||
|           </div>         | ||||
|           <div id="browseFilesystemPaginationBottom" class="btn-group mr-2 dropup"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="browseFilesystemPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="browseFilesystemPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="browseFilesystemPaginationBottomPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="browseFilesystemPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div>         | ||||
|  | ||||
|     <div class="card hide" id="cardSearch"> | ||||
|       <div class="card-header" id="pnael-heading-search">Search</div> | ||||
|       <div class="card-header">Search | ||||
|         <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"> | ||||
|           <form id="search2" role="search"> | ||||
|             <div class="input-group mr-2"> | ||||
|               <input type="text" class="form-control" placeholder="Search" id="searchstr2"/> | ||||
|               <div class="input-group-append"> | ||||
|                 <button title="Select tags to search" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown"> | ||||
|                   <span class="material-icons">search</span> | ||||
|                   <span id="searchtags2desc">Any Tag</span> | ||||
|                 </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 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> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </form> | ||||
|           <div class="btn-group mr-2 pull-right"> | ||||
|             <button id="searchAddAllSongs" class="btn btn-secondary" onclick="addAllFromSearch();">Add all</button> | ||||
|           </div>           | ||||
|           <div id="searchPaginationTop" class="btn-group mr-2"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="searchPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="searchPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="searchPaginationTopPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="searchPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="table-responsive-md"> | ||||
|           <table id="searchList" class="table table-hover table-sm"> | ||||
|             <col class="tblnum"/> | ||||
| @@ -264,6 +442,22 @@ | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
|         <div class="btn-toolbar" id="searchButtonsBottom" role="toolbar"> | ||||
|           <div class="btn-group mr-2"> | ||||
|             <button type="button" class="btn btn-secondary" onclick="scrollToTop()" title="To top"> | ||||
|               <span class="material-icons">keyboard_arrow_up</span> | ||||
|             </button> | ||||
|           </div>         | ||||
|           <div id="searchPaginationBottom" class="btn-group mr-2 dropup"> | ||||
|             <button onclick="gotoPage('prev',this,event)" id="searchPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">«</button> | ||||
|               <div class="input-group-append"> | ||||
|                 <button id="searchPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">Page 1 / 1</button> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="searchPaginationBottomPages"> | ||||
|                 </div> | ||||
|               </div>             | ||||
|             <button onclick="gotoPage('next',this,event)" id="searchPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div>     | ||||
|      | ||||
| @@ -271,8 +465,9 @@ | ||||
|     <nav class="navbar navbar-expand navbar-dark fixed-bottom bg-dark"> | ||||
|       <div class="d-flex flex-fill navbar-nav" id="navbar-bottom"> | ||||
|         <div class="nav-item flex-fill text-center" id="navPlayback"><a class="nav-link" href="#/playing/">Playback</a></div> | ||||
|         <div class="nav-item flex-fill text-center" id="navQueue"><a class="nav-link" href="#/queue/0">Queue</a></div> | ||||
|         <div class="nav-item flex-fill text-center" id="navBrowse"><a class="nav-link" href="#/browse/0/">Browse</a></div> | ||||
|         <div class="nav-item flex-fill text-center" id="navQueue"><a class="nav-link" href="#/queue/0/Any Tag/">Queue</a></div> | ||||
|         <div class="nav-item flex-fill text-center" id="navBrowse"><a class="nav-link" href="#/browse/filesystem/0/!/">Browse</a></div> | ||||
|         <div class="nav-item flex-fill text-center" id="navSearch"><a class="nav-link" href="#/search/0/Any Tag/">Search</a></div>         | ||||
|       </div> | ||||
|     </nav> | ||||
|   </footer> | ||||
| @@ -333,6 +528,16 @@ | ||||
|               </div> | ||||
|               <input id="inputCrossfade" type="text" class="form-control border-secondary" value=""> | ||||
|             </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"> | ||||
|                 <option value="off">Off</option> | ||||
|                 <option value="track">Track</option> | ||||
|                 <option value="album">Album</option> | ||||
|               </select> | ||||
|             </div>             | ||||
|           </div> | ||||
|           <div class="row"> | ||||
|             <div class="form-group input-group col-md-6 border-secondary"> | ||||
| @@ -381,12 +586,12 @@ | ||||
|           </button> | ||||
|         </div> | ||||
|         <div class="modal-body"> | ||||
|           <h4><a style="color:#28a745" href="https://github.com/jcorporation/ympd"><span class="material-icons">play_circle_outline</span> myMPD</a> – <small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4> | ||||
|           <p>myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter. It's tuned for minimal resource usage and requires only very litte dependencies. myMPD is a fork of <a style="color:#28a745" href="http://www.ympd.org">ympd</a>.</p> | ||||
|           <h4><a class="text-success" href="https://github.com/jcorporation/ympd"><span class="material-icons">play_circle_outline</span> myMPD</a> – <small>MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS</small></h4> | ||||
|           <p>myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter. It's tuned for minimal resource usage and requires only very litte dependencies. myMPD is a fork of <a class="text-success" href="http://www.ympd.org">ympd</a>.</p> | ||||
|           <ul> | ||||
|             <li>Version: 2.1.1</li> | ||||
|             <li>Homepage: <a style="color:#28a745" target="_blank" href="https://github.com/jcorporation/mympd">https://github.com/jcorporation/mympd</a></li> | ||||
|             <li>Autor: Juergen Mang <<a style="color:#28a745" href="mailto:mail@jcgames.de">mail@jcgames.de</a>></li> | ||||
|             <li>Version: <span id="mympdVersion"></span></li> | ||||
|             <li>Homepage: <a class="text-success" target="_blank" href="https://github.com/jcorporation/mympd">https://github.com/jcorporation/mympd</a></li> | ||||
|             <li>Autor: Jürgen Mang <<a class="text-success" href="mailto:mail@jcgames.de">mail@jcgames.de</a>></li> | ||||
|           </ul> | ||||
|           <hr/> | ||||
|           <h5>Database Statistics</h5> | ||||
| @@ -403,6 +608,7 @@ | ||||
|           <h5>Play Statistics</h5> | ||||
|           <table class="table table-sm"> | ||||
|             <tbody> | ||||
|               <tr><th>MPD Version</th><td id="mpdVersion"></td></tr> | ||||
|               <tr><th>Uptime</th><td id="mpdstats_uptime"></td></tr> | ||||
|               <tr><th>Play Time</th><td id="mpdstats_playtime"></td></tr> | ||||
|             </tbody> | ||||
|   | ||||
							
								
								
									
										908
									
								
								htdocs/js/mpd.js
									
									
									
									
									
								
							
							
						
						
									
										908
									
								
								htdocs/js/mpd.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								mympd.1
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mympd.1
									
									
									
									
									
								
							| @@ -39,7 +39,7 @@ specifies the password to use when connecting to mpd | ||||
| \fB-i\fR, \fB\-\-coverimage FILENAME\fR | ||||
| filename for coverimage [folder.jpg] | ||||
| .TP | ||||
| \fB\-V\fR, \fB\-\-version\fR | ||||
| \fB\-v\fR, \fB\-\-version\fR | ||||
| print version and exit | ||||
| .TP | ||||
| \fB\-\-help\fR | ||||
|   | ||||
| @@ -25,8 +25,9 @@ | ||||
| #ifndef __CONFIG_H__ | ||||
| #define __CONFIG_H__ | ||||
|  | ||||
| #define YMPD_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} | ||||
| #define YMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} | ||||
| #define YMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} | ||||
| #define MYMPD_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} | ||||
| #define MYMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} | ||||
| #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}" | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										699
									
								
								src/mpd_client.c
									
									
									
									
									
								
							
							
						
						
									
										699
									
								
								src/mpd_client.c
									
									
									
									
									
								
							| @@ -27,6 +27,7 @@ | ||||
| #include <unistd.h> | ||||
| #include <stdlib.h> | ||||
| #include <libgen.h> | ||||
| #include <ctype.h> | ||||
| #include <mpd/client.h> | ||||
| #include <mpd/message.h> | ||||
|  | ||||
| @@ -66,10 +67,10 @@ int callback_mpd(struct mg_connection *c) | ||||
|     double double_buf; | ||||
|     int int_buf; | ||||
|     char *p_charbuf = NULL, *token; | ||||
|     char *mpdtagtype = NULL; | ||||
|     char *p_charbuf2 = NULL; | ||||
|     char *searchstr = NULL; | ||||
|  | ||||
|     fprintf(stdout,"%s\n",c->content); | ||||
|     fprintf(stdout,"Got request: %s:%d\n",c->content,cmd_id); | ||||
|      | ||||
|     if(cmd_id == -1) | ||||
|         return MG_TRUE; | ||||
| @@ -170,20 +171,76 @@ int callback_mpd(struct mg_connection *c) | ||||
|             if(sscanf(c->content, "MPD_API_GET_QUEUE,%u", &uint_buf)) | ||||
|                 n = mpd_put_queue(mpd.buf, uint_buf); | ||||
|             break; | ||||
|         case MPD_API_GET_ARTISTS: | ||||
|             if(sscanf(c->content, "MPD_API_GET_ARTISTS,%u", &uint_buf)) | ||||
|                 n = mympd_put_db_tag(mpd.buf, uint_buf, "AlbumArtist","",""); | ||||
|             break; | ||||
|         case MPD_API_GET_ARTISTALBUMS: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMS")) | ||||
| 		goto out_artistalbum; | ||||
|             uint_buf = strtoul(strtok(NULL, ","), NULL, 10); | ||||
|              | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_artistalbum; | ||||
|             } else { | ||||
|                 searchstr = strdup(token); | ||||
|             } | ||||
|             n = mympd_put_db_tag(mpd.buf, uint_buf, "Album", "AlbumArtist", searchstr); | ||||
|             free(searchstr); | ||||
| out_artistalbum: | ||||
|             free(p_charbuf);         | ||||
|             break; | ||||
|         case MPD_API_GET_ARTISTALBUMTITLES: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_ARTISTALBUMTITLES")) | ||||
| 		goto out_artistalbumtitle; | ||||
|              | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_artistalbumtitle; | ||||
|             } else { | ||||
|                 searchstr = strdup(token); | ||||
|             } | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_artistalbumtitle; | ||||
|             } else { | ||||
|                 p_charbuf2 = strdup(token); | ||||
|             }             | ||||
|             n = mympd_put_songs_in_album(mpd.buf, searchstr, p_charbuf2); | ||||
|             free(searchstr); | ||||
| out_artistalbumtitle: | ||||
|             free(p_charbuf);         | ||||
|             break; | ||||
|         case MPD_API_GET_PLAYLISTS: | ||||
|             if(sscanf(c->content, "MPD_API_GET_PLAYLISTS,%u", &uint_buf)) | ||||
|                 n = mpd_put_playlists(mpd.buf, uint_buf); | ||||
|             break; | ||||
|         case MPD_API_GET_BROWSE: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) | ||||
|                 goto out_browse; | ||||
|  | ||||
|             uint_buf = strtoul(strtok(NULL, ","), NULL, 10); | ||||
|             if((token = strtok(NULL, ",")) == NULL) | ||||
|                 goto out_browse; | ||||
|  | ||||
| 			free(p_charbuf); | ||||
|             p_charbuf = strdup(c->content); | ||||
|             n = mpd_put_browse(mpd.buf, get_arg2(p_charbuf), uint_buf); | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_browse; | ||||
|             } else { | ||||
|                 p_charbuf2 = strdup(token); | ||||
|             } | ||||
|  | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 free(p_charbuf2); | ||||
| 		goto out_browse; | ||||
|             } else { | ||||
|                 searchstr = strdup(token); | ||||
|             } | ||||
|             n = mpd_put_browse(mpd.buf, p_charbuf2, uint_buf, searchstr); | ||||
|             free(searchstr); | ||||
|             free(p_charbuf2); | ||||
| out_browse: | ||||
| 			free(p_charbuf); | ||||
|             free(p_charbuf); | ||||
|             break;             | ||||
|              | ||||
|             break; | ||||
|         case MPD_API_ADD_TRACK: | ||||
|             p_charbuf = strdup(c->content); | ||||
| @@ -193,7 +250,7 @@ out_browse: | ||||
|             if((token = strtok(NULL, ",")) == NULL) | ||||
|                 goto out_add_track; | ||||
|  | ||||
| 			free(p_charbuf); | ||||
|             free(p_charbuf); | ||||
|             p_charbuf = strdup(c->content); | ||||
|             mpd_run_add(mpd.conn, get_arg1(p_charbuf)); | ||||
| out_add_track: | ||||
| @@ -244,43 +301,72 @@ out_save_queue: | ||||
|             free(p_charbuf); | ||||
|             break; | ||||
|         case MPD_API_SEARCH_QUEUE: | ||||
|  | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_QUEUE")) | ||||
| 				goto out_search_queue; | ||||
| 		goto out_search_queue; | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_search_queue; | ||||
|             } else { | ||||
|                 mpdtagtype = strdup(token); | ||||
|                 p_charbuf2 = strdup(token); | ||||
|             } | ||||
|              | ||||
|             uint_buf = strtoul(strtok(NULL, ","), NULL, 10); | ||||
|              | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 free(mpdtagtype); | ||||
|                 free(p_charbuf2); | ||||
|                 goto out_search_queue; | ||||
|             } else { | ||||
|                 searchstr = strdup(token); | ||||
|             } | ||||
|             n = mpd_search_queue(mpd.buf, p_charbuf2, uint_buf, searchstr); | ||||
|             free(searchstr); | ||||
|             free(p_charbuf2); | ||||
| out_search_queue: | ||||
|             free(p_charbuf); | ||||
|             break;             | ||||
|         case MPD_API_SEARCH_ADD: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_ADD")) | ||||
| 		goto out_search_add; | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_search_add; | ||||
|             } else { | ||||
|                 p_charbuf2 = strdup(token); | ||||
|             } | ||||
|              | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 free(p_charbuf2); | ||||
|                 goto out_search_add; | ||||
|             } else { | ||||
|                 searchstr = strdup(token); | ||||
|             } | ||||
|             n = mpd_search_add(mpd.buf, p_charbuf2, searchstr); | ||||
|             free(searchstr); | ||||
|             free(p_charbuf2); | ||||
| out_search_add: | ||||
|             free(p_charbuf); | ||||
|             break; | ||||
|         case MPD_API_SEARCH: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) | ||||
| 		goto out_search; | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 goto out_search; | ||||
|             } else { | ||||
|                 p_charbuf2 = strdup(token); | ||||
|             } | ||||
|              | ||||
|             uint_buf = strtoul(strtok(NULL, ","), NULL, 10); | ||||
|              | ||||
|             if((token = strtok(NULL, ",")) == NULL) { | ||||
|                 free(p_charbuf2); | ||||
|                 goto out_search; | ||||
|             } else { | ||||
|                 searchstr = strdup(token); | ||||
|             } | ||||
|  | ||||
| 	    //free(p_charbuf); | ||||
|             //p_charbuf = strdup(c->content); | ||||
|             n = mpd_search_queue(mpd.buf, mpdtagtype, uint_buf, searchstr); | ||||
|             n = mpd_search(mpd.buf, p_charbuf2, uint_buf, searchstr); | ||||
|             free(searchstr); | ||||
| out_search_queue: | ||||
|             free(p_charbuf); | ||||
|             break;             | ||||
|         case MPD_API_SEARCH: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) | ||||
| 				goto out_search; | ||||
|  | ||||
|             if((token = strtok(NULL, ",")) == NULL) | ||||
|                 goto out_search; | ||||
|  | ||||
|             free(p_charbuf); | ||||
|             p_charbuf = strdup(c->content); | ||||
|             n = mpd_search(mpd.buf, get_arg1(p_charbuf)); | ||||
|             free(p_charbuf2); | ||||
| out_search: | ||||
|             free(p_charbuf); | ||||
|             break; | ||||
| @@ -308,15 +394,44 @@ out_search: | ||||
| out_send_message: | ||||
|             free(p_charbuf); | ||||
|             break; | ||||
|         case MPD_API_GET_OPTIONS: | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdoptions\", \"data\": " | ||||
|                 "{\"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " | ||||
|                 "\"streamport\": \"%d\",\"coverimage\": \"%s\"}" | ||||
|                 "}", mpd.host, mpd.port, mpd.password ? "true" : "false", streamport, coverimage); | ||||
|         case MPD_API_RM_PLAYLIST: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_RM_PLAYLIST")) | ||||
|                 goto out_rm_playlist; | ||||
|  | ||||
|             if((token = strtok(NULL, ",")) == NULL) | ||||
|                 goto out_rm_playlist; | ||||
|  | ||||
|             free(p_charbuf); | ||||
|             p_charbuf = strdup(c->content); | ||||
|             mpd_run_rm(mpd.conn, get_arg1(p_charbuf)); | ||||
| out_rm_playlist: | ||||
|             free(p_charbuf); | ||||
|             break; | ||||
|         case MPD_API_GET_SETTINGS: | ||||
|             n = mympd_put_settings(mpd.buf); | ||||
|             break; | ||||
|         case MPD_API_GET_STATS: | ||||
|             n = mympd_get_stats(mpd.buf); | ||||
|         break; | ||||
|         case MPD_API_SET_REPLAYGAIN: | ||||
|             p_charbuf = strdup(c->content); | ||||
|             if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_REPLAYGAIN")) | ||||
|                 goto out_set_replaygain; | ||||
|  | ||||
|             if((token = strtok(NULL, ",")) == NULL) | ||||
|                 goto out_set_replaygain; | ||||
|  | ||||
|             free(p_charbuf); | ||||
|             p_charbuf = strdup(c->content); | ||||
|             mpd_send_command(mpd.conn, "replay_gain_mode", get_arg1(p_charbuf), NULL); | ||||
|             struct mpd_pair *pair; | ||||
|             while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { | ||||
|         	mpd_return_pair(mpd.conn, pair); | ||||
|             }             | ||||
| out_set_replaygain: | ||||
|             free(p_charbuf);         | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) | ||||
| @@ -329,8 +444,10 @@ out_send_message: | ||||
|             mpd.conn_state = MPD_FAILURE; | ||||
|     } | ||||
|  | ||||
|     if(n > 0) | ||||
|     if(n > 0) { | ||||
|         //fprintf(stdout,"Send response:\n %s\n",mpd.buf); | ||||
|         mg_websocket_write(c, 1, mpd.buf, n); | ||||
|     } | ||||
|  | ||||
|     return MG_TRUE; | ||||
| } | ||||
| @@ -478,6 +595,19 @@ char* mpd_get_title(struct mpd_song const *song) | ||||
|     return str; | ||||
| } | ||||
|  | ||||
| char* mpd_get_track(struct mpd_song const *song) | ||||
| { | ||||
|     char *str; | ||||
|  | ||||
|     str = (char *)mpd_song_get_tag(song, MPD_TAG_TRACK, 0); | ||||
|     if(str == NULL){ | ||||
|         str = "-"; | ||||
|     } | ||||
|  | ||||
|     return str; | ||||
| } | ||||
|  | ||||
|  | ||||
| char* mpd_get_album(struct mpd_song const *song) | ||||
| { | ||||
|     char *str; | ||||
| @@ -529,6 +659,7 @@ char* mpd_get_year(struct mpd_song const *song) | ||||
| int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsigned *queue_version) | ||||
| { | ||||
|     struct mpd_status *status; | ||||
|     const struct mpd_audio_format *audioformat; | ||||
|     int len; | ||||
|  | ||||
|     status = mpd_run_status(mpd.conn); | ||||
| @@ -537,29 +668,27 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsign | ||||
|         mpd.conn_state = MPD_FAILURE; | ||||
|         return 0; | ||||
|     } | ||||
|     if (status) { | ||||
|      audioformat = mpd_status_get_audio_format(status); | ||||
|     } | ||||
|  | ||||
|     len = snprintf(buffer, MAX_SIZE, | ||||
|         "{\"type\":\"state\", \"data\":{" | ||||
|         " \"state\":%d, \"volume\":%d, \"repeat\":%d," | ||||
|         " \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " | ||||
|         " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, " | ||||
|         " \"currentsongid\": %d, \"kbitrate\": %d, \"mixrampdb\": %lf, " | ||||
|         " \"mixrampdelay\": %lf, \"queue_length\": %d, \"nextsongpos\": %d" | ||||
|         "\"state\":%d, \"volume\":%d, \"songpos\": %d, \"elapsedTime\": %d, " | ||||
|         "\"totalTime\":%d, \"currentsongid\": %d, \"kbitrate\": %d, " | ||||
|         "\"audioformat\": { \"sample_rate\": %d, \"bits\": %d, \"channels\": %d}, " | ||||
|         "\"queue_length\": %d, \"nextsongpos\": %d" | ||||
|         "}}",  | ||||
|         mpd_status_get_state(status), | ||||
|         mpd_status_get_volume(status),  | ||||
|         mpd_status_get_repeat(status), | ||||
|         mpd_status_get_single(status), | ||||
|         mpd_status_get_crossfade(status), | ||||
|         mpd_status_get_consume(status), | ||||
|         mpd_status_get_random(status), | ||||
|         mpd_status_get_song_pos(status), | ||||
|         mpd_status_get_elapsed_time(status), | ||||
|         mpd_status_get_total_time(status), | ||||
|         mpd_status_get_song_id(status), | ||||
|         mpd_status_get_kbit_rate(status), | ||||
|         mpd_status_get_mixrampdb(status), | ||||
|         mpd_status_get_mixrampdelay(status), | ||||
|         audioformat ? audioformat->sample_rate : 0,  | ||||
|         audioformat ? audioformat->bits : 0,  | ||||
|         audioformat ? audioformat->channels : 0, | ||||
|         mpd_status_get_queue_length(status), | ||||
|         mpd_status_get_next_song_pos(status) | ||||
|     ); | ||||
| @@ -571,6 +700,50 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id,  unsign | ||||
|     return len; | ||||
| } | ||||
|  | ||||
| int mympd_put_settings(char *buffer) | ||||
| { | ||||
|     struct mpd_status *status; | ||||
|     int len; | ||||
|     char *replaygain; | ||||
|  | ||||
|     status = mpd_run_status(mpd.conn); | ||||
|     if (!status) { | ||||
|         fprintf(stderr, "MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn)); | ||||
|         mpd.conn_state = MPD_FAILURE; | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     mpd_send_command(mpd.conn, "replay_gain_status", NULL); | ||||
|     struct mpd_pair *pair; | ||||
|     while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { | ||||
|         replaygain=strdup(pair->value); | ||||
| 	mpd_return_pair(mpd.conn, pair); | ||||
|     } | ||||
| 			 | ||||
|     len = snprintf(buffer, MAX_SIZE, | ||||
|         "{\"type\":\"settings\", \"data\":{" | ||||
|         "\"repeat\":%d, \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " | ||||
|         "\"mixrampdb\": %lf, \"mixrampdelay\": %lf, \"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " | ||||
|         "\"streamport\": \"%d\",\"coverimage\": \"%s\", \"max_elements_per_page\": %d, \"replaygain\": \"%s\"" | ||||
|         "}}",  | ||||
|         mpd_status_get_repeat(status), | ||||
|         mpd_status_get_single(status), | ||||
|         mpd_status_get_crossfade(status), | ||||
|         mpd_status_get_consume(status), | ||||
|         mpd_status_get_random(status), | ||||
|         mpd_status_get_mixrampdb(status), | ||||
|         mpd_status_get_mixrampdelay(status), | ||||
|         mpd.host, mpd.port,  | ||||
|         mpd.password ? "true" : "false",  | ||||
|         streamport, coverimage, | ||||
|         MAX_ELEMENTS_PER_PAGE, | ||||
|         replaygain | ||||
|     ); | ||||
|     mpd_status_free(status); | ||||
|     return len; | ||||
| } | ||||
|  | ||||
|  | ||||
| int mpd_put_outputs(char *buffer, int names) | ||||
| { | ||||
|     struct mpd_output *out; | ||||
| @@ -618,17 +791,14 @@ 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)); | ||||
| //    cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); | ||||
| //    cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(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, ",\"uri\":"); | ||||
|     cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); | ||||
|  | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}}"); | ||||
|  | ||||
|     mpd_song_free(song); | ||||
|     mpd_response_finish(mpd.conn); | ||||
|  | ||||
| @@ -641,9 +811,9 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_entity *entity; | ||||
|     unsigned long totalTime = 0; | ||||
|     unsigned long totalSongs = 0; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
|  | ||||
|     /*get complete queue for total songs*/ | ||||
|     if (!mpd_send_list_queue_range_meta(mpd.conn, 0, -1)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_queue_meta"); | ||||
|          | ||||
| @@ -657,9 +827,9 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|             song = mpd_entity_get_song(entity); | ||||
|             drtn = mpd_song_get_duration(song); | ||||
|             totalTime += drtn; | ||||
|             totalSongs ++; | ||||
|             if(totalSongs > offset && totalSongs <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|                 /*Pagination*/ | ||||
|             entity_count ++; | ||||
|             if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|                 entities_returned ++; | ||||
|                 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\":"); | ||||
| @@ -668,10 +838,6 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|                 cur += json_emit_int(cur, end - cur, mpd_song_get_duration(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_artist\":"); | ||||
| //                cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(song)); | ||||
| //                cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); | ||||
| //                cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(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\":"); | ||||
| @@ -688,18 +854,25 @@ int mpd_put_queue(char *buffer, unsigned int offset) | ||||
|  | ||||
|     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, ",\"totalSongs\":"); | ||||
|     cur += json_emit_int(cur, end - cur, totalSongs); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); | ||||
|     cur += json_emit_int(cur, end - cur, entity_count); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); | ||||
|     cur += json_emit_int(cur, end - cur, offset); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|     cur += json_emit_int(cur, end - cur, entities_returned); | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mpd_put_browse(char *buffer, char *path, unsigned int offset) | ||||
| int mpd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_entity *entity; | ||||
|     const struct mpd_playlist *pl; | ||||
|     unsigned int entity_count = 0; | ||||
|     unsigned int entities_returned = 0; | ||||
|     const char *entityName; | ||||
|  | ||||
|     if (!mpd_send_list_meta(mpd.conn, path)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); | ||||
| @@ -709,61 +882,79 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) | ||||
|     while((entity = mpd_recv_entity(mpd.conn)) != NULL) { | ||||
|         const struct mpd_song *song; | ||||
|         const struct mpd_directory *dir; | ||||
|         const struct mpd_playlist *pl; | ||||
|         entity_count ++; | ||||
|         if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|             switch (mpd_entity_get_type(entity)) { | ||||
|                 case MPD_ENTITY_TYPE_UNKNOWN: | ||||
|                         entity_count --; | ||||
|                     break; | ||||
|                 case MPD_ENTITY_TYPE_SONG: | ||||
|                     song = mpd_entity_get_song(entity); | ||||
|                     entityName = mpd_get_title(song); | ||||
|                     if (strncmp(filter,"!",1) == 0 || strncasecmp(filter,entityName,1) == 0 || | ||||
|                         ( strncmp(filter,"0",1) == 0 && isalpha(*entityName) == 0 ) | ||||
|                     ) { | ||||
|                         entities_returned ++; | ||||
|                         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); | ||||
|                         cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(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, ",\"artist\":"); | ||||
|                         cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(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_raw_str(cur, end - cur, ",\"title\":"); | ||||
|                         cur += json_emit_quoted_str(cur, end - cur, entityName); | ||||
|                         cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|                     } else { | ||||
|                         entity_count --; | ||||
|                     } | ||||
|                     break; | ||||
|  | ||||
|         if(offset > entity_count) | ||||
|         { | ||||
|             mpd_entity_free(entity); | ||||
|             entity_count++; | ||||
|             continue; | ||||
|         } | ||||
|         else if(offset + MAX_ELEMENTS_PER_PAGE - 1 < entity_count) | ||||
|         { | ||||
|             mpd_entity_free(entity); | ||||
|             cur += json_emit_raw_str(cur, end  - cur, "{\"type\":\"wrap\",\"count\":"); | ||||
|             cur += json_emit_int(cur, end - cur, entity_count); | ||||
|             cur += json_emit_raw_str(cur, end  - cur, "} "); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         switch (mpd_entity_get_type(entity)) { | ||||
|             case MPD_ENTITY_TYPE_UNKNOWN: | ||||
|                 break; | ||||
|  | ||||
|             case MPD_ENTITY_TYPE_SONG: | ||||
|                 song = mpd_entity_get_song(entity); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(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, ",\"artist\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); | ||||
| //                cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); | ||||
| //                cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(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_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, "},"); | ||||
|                 break; | ||||
|  | ||||
|             case MPD_ENTITY_TYPE_DIRECTORY: | ||||
|                 dir = mpd_entity_get_directory(entity); | ||||
|  | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"directory\",\"dir\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_directory_get_path(dir)); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|                 break; | ||||
|  | ||||
|             case MPD_ENTITY_TYPE_PLAYLIST: | ||||
|                 pl = mpd_entity_get_playlist(entity); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_playlist_get_path(pl)); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|                 break; | ||||
|                 case MPD_ENTITY_TYPE_DIRECTORY: | ||||
|                     dir = mpd_entity_get_directory(entity);                 | ||||
|                     entityName = mpd_directory_get_path(dir); | ||||
|                     char *dirName = strrchr(entityName, '/'); | ||||
|                     if (dirName != NULL) { | ||||
|                         dirName ++; | ||||
|                     } else { | ||||
|                      dirName = strdup(entityName); | ||||
|                     } | ||||
|                     if (strncmp(filter,"!",1) == 0 || strncasecmp(filter,dirName,1) == 0 || | ||||
|                         ( strncmp(filter,"0",1) == 0 && isalpha(*dirName) == 0 ) | ||||
|                     ) {                 | ||||
|                         entities_returned ++; | ||||
|                         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"directory\",\"dir\":"); | ||||
|                         cur += json_emit_quoted_str(cur, end - cur, entityName); | ||||
|                         cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|                     } else { | ||||
|                         entity_count --; | ||||
|                     } | ||||
|                     break; | ||||
|                      | ||||
|                 case MPD_ENTITY_TYPE_PLAYLIST: | ||||
|                     pl = mpd_entity_get_playlist(entity); | ||||
|                     entityName = mpd_playlist_get_path(pl); | ||||
|                     char *plName = strrchr(entityName, '/'); | ||||
|                     if (plName != NULL) { | ||||
|                         plName ++; | ||||
|                     } else { | ||||
|                      plName = strdup(entityName); | ||||
|                     } | ||||
|                     if (strncmp(filter,"!",1) == 0 || strncasecmp(filter,plName,1) == 0 || | ||||
|                         ( strncmp(filter,"0",1) == 0 && isalpha(*plName) == 0 ) | ||||
|                     ) { | ||||
|                         entities_returned ++; | ||||
|                         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); | ||||
|                         cur += json_emit_quoted_str(cur, end - cur, entityName ); | ||||
|                         cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|                     } else { | ||||
|                         entity_count --; | ||||
|                     } | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         mpd_entity_free(entity); | ||||
|         entity_count++; | ||||
|     } | ||||
|  | ||||
|     if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) { | ||||
| @@ -774,30 +965,200 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) | ||||
|  | ||||
|     /* remove last ',' */ | ||||
|     cur--; | ||||
|  | ||||
|     cur += json_emit_raw_str(cur, end - cur, "]}"); | ||||
|     cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); | ||||
|     cur += json_emit_int(cur, end - cur, entity_count); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); | ||||
|     cur += json_emit_int(cur, end - cur, offset); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|     cur += json_emit_int(cur, end - cur, entities_returned);     | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mpd_search(char *buffer, char *searchstr) | ||||
| int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_pair *pair; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
|  | ||||
|     if(mpd_search_db_tags(mpd.conn, mpd_tag_name_parse(mpdtagtype)) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_db_tags"); | ||||
|  | ||||
|     if (mpd_tag_name_parse(mpdsearchtagtype) != MPD_TAG_UNKNOWN) { | ||||
|         if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdsearchtagtype), searchstr) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); | ||||
|     } | ||||
|  | ||||
|     if(mpd_search_commit(mpd.conn) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_commit"); | ||||
|     else { | ||||
|         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"listDBtags\",\"data\":[ "); | ||||
|         while((pair = mpd_recv_pair_tag(mpd.conn, mpd_tag_name_parse(mpdtagtype))) != NULL) { | ||||
|             entity_count ++; | ||||
|             if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|                 entities_returned ++; | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "{\"type\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, ",\"value\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, pair->value); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|             } | ||||
|             mpd_return_pair(mpd.conn, pair); | ||||
|         } | ||||
|          | ||||
|         /* remove last ',' */ | ||||
|         cur--; | ||||
|  | ||||
|         cur += json_emit_raw_str(cur, end - cur, "]"); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entity_count); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); | ||||
|         cur += json_emit_int(cur, end - cur, offset); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entities_returned); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"tagtype\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"searchtagtype\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, mpdsearchtagtype); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"searchstr\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, searchstr); | ||||
|         cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     } | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_song *song; | ||||
|     unsigned long totalSongs = 0; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
|  | ||||
|     if(mpd_search_db_songs(mpd.conn, false) == false) | ||||
|     if(mpd_search_db_songs(mpd.conn, true) == false) { | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); | ||||
|     else if(mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint"); | ||||
|     else if(mpd_search_commit(mpd.conn) == false) | ||||
|     } | ||||
|      | ||||
|     if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, albumartist) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); | ||||
|  | ||||
|     if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); | ||||
|          | ||||
|     if(mpd_search_commit(mpd.conn) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_commit"); | ||||
|     else { | ||||
|         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"listTitles\",\"data\":[ "); | ||||
|  | ||||
|         while((song = mpd_recv_song(mpd.conn)) != NULL) { | ||||
|             entity_count ++; | ||||
|             if(entity_count <= MAX_ELEMENTS_PER_PAGE) { | ||||
|                 entities_returned ++; | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(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_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, ",\"track\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_get_track(song)); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|             } | ||||
|             mpd_song_free(song); | ||||
|         } | ||||
|          | ||||
|         /* remove last ',' */ | ||||
|         cur--; | ||||
|  | ||||
|         cur += json_emit_raw_str(cur, end - cur, "]"); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entity_count); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entities_returned); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"albumartist\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, albumartist); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, album); | ||||
|         cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     } | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mpd_put_playlists(char *buffer, unsigned int offset) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_playlist *pl; | ||||
|     unsigned int entity_count = 0; | ||||
|     unsigned int entities_returned = 0; | ||||
|  | ||||
|     if (!mpd_send_list_playlists(mpd.conn)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_lists_playlists"); | ||||
|  | ||||
|     cur += json_emit_raw_str(cur, end  - cur, "{\"type\":\"playlists\",\"data\":[ "); | ||||
|  | ||||
|     while((pl = mpd_recv_playlist(mpd.conn)) != NULL) { | ||||
|         entity_count ++; | ||||
|         if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|             entities_returned ++; | ||||
|             cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_playlist_get_path(pl)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, ",\"last_modified\":"); | ||||
|             cur += json_emit_int(cur, end - cur, mpd_playlist_get_last_modified(pl)); | ||||
|             cur += json_emit_raw_str(cur, end - cur, "},"); | ||||
|         } | ||||
|         mpd_playlist_free(pl); | ||||
|     } | ||||
|  | ||||
|     if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_send_list_playlists"); | ||||
|          | ||||
|     /* remove last ',' */ | ||||
|     cur--; | ||||
|  | ||||
|     cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); | ||||
|     cur += json_emit_int(cur, end - cur, entity_count); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); | ||||
|         cur += json_emit_int(cur, end - cur, offset); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entities_returned);     | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_song *song; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
|  | ||||
|     if(mpd_search_db_songs(mpd.conn, false) == false) { | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); | ||||
|     } | ||||
|      | ||||
|     if (mpd_tag_name_parse(mpdtagtype) != MPD_TAG_UNKNOWN) { | ||||
|        if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); | ||||
|     } | ||||
|     else { | ||||
|         if (mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint");         | ||||
|     }     | ||||
|          | ||||
|     if(mpd_search_commit(mpd.conn) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_commit"); | ||||
|     else { | ||||
|         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"search\",\"data\":[ "); | ||||
|  | ||||
|         while((song = mpd_recv_song(mpd.conn)) != NULL) { | ||||
|             totalSongs ++; | ||||
|             if (totalSongs <=300) { | ||||
|             entity_count ++; | ||||
|             if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|                 entities_returned ++; | ||||
|                 cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); | ||||
|                 cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); | ||||
| @@ -805,8 +1166,6 @@ int mpd_search(char *buffer, char *searchstr) | ||||
|                 cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(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_artist\":"); | ||||
| //            cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(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_raw_str(cur, end - cur, ",\"title\":"); | ||||
| @@ -816,29 +1175,59 @@ int mpd_search(char *buffer, char *searchstr) | ||||
|             mpd_song_free(song); | ||||
|         } | ||||
|          | ||||
|         /* Maximum results */ | ||||
|         if(totalSongs++ > 300) | ||||
|         { | ||||
|            cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"wrap\"},"); | ||||
|         } | ||||
|  | ||||
|         /* remove last ',' */ | ||||
|         cur--; | ||||
|  | ||||
|         cur += json_emit_raw_str(cur, end - cur, "]"); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"totalSongs\":"); | ||||
|         cur += json_emit_int(cur, end - cur, totalSongs); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entity_count); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); | ||||
|         cur += json_emit_int(cur, end - cur, offset); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entities_returned); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, mpdtagtype);         | ||||
|         cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     } | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mpd_search_add(char *buffer,char *mpdtagtype, char *searchstr) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_song *song; | ||||
|  | ||||
|     if(mpd_search_add_db_songs(mpd.conn, false) == false) { | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs"); | ||||
|     } | ||||
|      | ||||
|     if (mpd_tag_name_parse(mpdtagtype) != MPD_TAG_UNKNOWN) { | ||||
|        if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); | ||||
|     } | ||||
|     else { | ||||
|         if (mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint");         | ||||
|     }     | ||||
|          | ||||
|     if(mpd_search_commit(mpd.conn) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_commit"); | ||||
|      | ||||
|     while((song = mpd_recv_song(mpd.conn)) != NULL) { | ||||
|         mpd_song_free(song); | ||||
|     } | ||||
|          | ||||
|     return cur - buffer; | ||||
| } | ||||
|  | ||||
| int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) | ||||
| { | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_song *song; | ||||
|     unsigned long totalSongs = 0; | ||||
|     unsigned long entity_count = 0; | ||||
|     unsigned long entities_returned = 0; | ||||
|  | ||||
|     if(mpd_search_queue_songs(mpd.conn, false) == false) { | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_queue_songs"); | ||||
| @@ -859,11 +1248,10 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * | ||||
|         cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"queuesearch\",\"data\":[ "); | ||||
|  | ||||
|         while((song = mpd_recv_song(mpd.conn)) != NULL) { | ||||
|           totalSongs ++; | ||||
|           if(totalSongs > offset && totalSongs <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|           entity_count ++; | ||||
|           if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { | ||||
|             entities_returned ++; | ||||
|             cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); | ||||
| //            cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); | ||||
| //            cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(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\":"); | ||||
| @@ -872,8 +1260,6 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * | ||||
|             cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(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_artist\":"); | ||||
| //            cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(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_raw_str(cur, end - cur, ",\"title\":"); | ||||
| @@ -883,15 +1269,17 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * | ||||
|           } | ||||
|         } | ||||
|          | ||||
| /*        if (totalSongs > 100) { | ||||
|           cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"wrap\"},"); | ||||
|         } | ||||
| */         | ||||
|         cur--; | ||||
|  | ||||
|         cur += json_emit_raw_str(cur, end - cur, "]"); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"totalSongs\":"); | ||||
|         cur += json_emit_int(cur, end - cur, totalSongs); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entity_count); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); | ||||
|         cur += json_emit_int(cur, end - cur, offset); | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); | ||||
|         cur += json_emit_int(cur, end - cur, entities_returned);         | ||||
|         cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); | ||||
|         cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); | ||||
|         cur += json_emit_raw_str(cur, end - cur, "}"); | ||||
|     } | ||||
|     return cur - buffer; | ||||
| @@ -902,6 +1290,11 @@ int mympd_get_stats(char *buffer) | ||||
|     char *cur = buffer; | ||||
|     const char *end = buffer + MAX_SIZE; | ||||
|     struct mpd_stats *stats = mpd_run_stats(mpd.conn); | ||||
|     const unsigned *version = mpd_connection_get_server_version(mpd.conn); | ||||
|     char mpd_version[20]; | ||||
|      | ||||
|     snprintf(mpd_version,20,"%i.%i.%i", version[0], version[1], version[2]); | ||||
|      | ||||
|     if (stats == NULL) | ||||
|         RETURN_ERROR_AND_RECOVER("mympd_get_stats"); | ||||
|     cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"mpdstats\",\"data\": {"); | ||||
| @@ -918,7 +1311,11 @@ int mympd_get_stats(char *buffer) | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"dbupdated\":"); | ||||
|     cur += json_emit_int(cur, end - cur, mpd_stats_get_db_update_time(stats)); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"dbplaytime\":"); | ||||
|     cur += json_emit_int(cur, end - cur, mpd_stats_get_db_play_time(stats));     | ||||
|     cur += json_emit_int(cur, end - cur, mpd_stats_get_db_play_time(stats)); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"mympd_version\":"); | ||||
|     cur += json_emit_quoted_str(cur, end - cur, MYMPD_VERSION); | ||||
|     cur += json_emit_raw_str(cur, end - cur, ",\"mpd_version\":"); | ||||
|     cur += json_emit_quoted_str(cur, end - cur, mpd_version); | ||||
|     cur += json_emit_raw_str(cur, end - cur, "}}"); | ||||
|      | ||||
|     mpd_stats_free(stats); | ||||
|   | ||||
| @@ -55,6 +55,7 @@ | ||||
|     X(MPD_API_RM_ALL) \ | ||||
|     X(MPD_API_MOVE_TRACK) \ | ||||
|     X(MPD_API_SEARCH_QUEUE) \ | ||||
|     X(MPD_API_SEARCH_ADD) \ | ||||
|     X(MPD_API_SEARCH) \ | ||||
|     X(MPD_API_SEND_MESSAGE) \ | ||||
|     X(MPD_API_SET_VOLUME) \ | ||||
| @@ -72,11 +73,17 @@ | ||||
|     X(MPD_API_TOGGLE_SINGLE) \ | ||||
|     X(MPD_API_SET_CROSSFADE) \ | ||||
|     X(MPD_API_TOGGLE_REPEAT) \ | ||||
|     X(MPD_API_GET_OPTIONS) \ | ||||
|     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_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) | ||||
|  | ||||
| enum mpd_cmd_ids { | ||||
|     MPD_CMDS(GEN_ENUM) | ||||
| @@ -125,10 +132,15 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigne | ||||
| 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); | ||||
| int mpd_search(char *buffer, char *searchstr); | ||||
| int mpd_put_playlists(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); | ||||
| int mympd_get_stats(); | ||||
| 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); | ||||
| int mympd_put_songs_in_album(char *buffer, char *albumartist, char *album); | ||||
| void mpd_disconnect(); | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -112,7 +112,7 @@ int main(int argc, char **argv) | ||||
|         {0,              0,                 0,  0 } | ||||
|     }; | ||||
|  | ||||
|     while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:v:m:s:i:", | ||||
|     while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:vm:s:i:", | ||||
|                 long_options, &option_index)) != -1) { | ||||
|         switch (n) { | ||||
|             case 'D': | ||||
| @@ -146,8 +146,8 @@ int main(int argc, char **argv) | ||||
|             case 'v': | ||||
|                 fprintf(stdout, "myMPD  %d.%d.%d\n" | ||||
|                         "Copyright (C) 2018 Juergen Mang <mail@jcgames.de>\n" | ||||
|                         "built " __DATE__ " "__TIME__ " ("__VERSION__")\n", | ||||
|                         YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH); | ||||
|                         "Built " __DATE__ " "__TIME__ "\n", | ||||
|                         MYMPD_VERSION_MAJOR, MYMPD_VERSION_MINOR, MYMPD_VERSION_PATCH); | ||||
|                 return EXIT_SUCCESS; | ||||
|                 break; | ||||
|             default: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 jcorporation
					jcorporation