mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-30 21:33:00 +00:00 
			
		
		
		
	Feat: interface for advanced search
This commit is contained in:
		| @@ -321,3 +321,7 @@ div.key { | ||||
|   heigth: 20px; | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| ol#searchCrumb { | ||||
|  padding: .5rem; | ||||
| } | ||||
| @@ -142,7 +142,7 @@ | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <form id="searchqueue"> | ||||
|           <form id="searchqueue" class="flex-grow-1"> | ||||
|             <div class="input-group mr-2"> | ||||
|               <input type="text" class="form-control" placeholder="Search Queue" id="searchqueuestr"/> | ||||
|               <div class="input-group-append"> | ||||
| @@ -164,9 +164,9 @@ | ||||
|               </div>             | ||||
|             <button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="QueueCurrentPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|           <div class="btn-group mr-2 featTags"> | ||||
|           <div class="btn-group featTags"> | ||||
|             <button id="QueueCurrentColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button> | ||||
|             <div class="dropdown-menu bg-dark px-2" id="QueueCurrentColsDropdown"><form></form> | ||||
|             <div class="dropdown-menu bg-dark px-2 dropdown-menu-right" id="QueueCurrentColsDropdown"><form></form> | ||||
|               <button data-href='{"cmd": "saveCols", "options": ["QueueCurrent"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button> | ||||
|             </div>           | ||||
|           </div> | ||||
| @@ -517,17 +517,26 @@ | ||||
|       </div> | ||||
|       <div class="card-body"> | ||||
|         <div class="btn-toolbar card-toolbar" id="SearchButtons"> | ||||
|           <form id="search"> | ||||
|           <form id="search" class="flex-grow-1"> | ||||
|             <div class="input-group mr-2"> | ||||
|               <input type="text" class="form-control" placeholder="Search" id="searchstr"/> | ||||
|               <div class="input-group-append"> | ||||
|               <div class="input-group-prepend"> | ||||
|                 <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="searchtagsdesc">Any Tag</span> | ||||
|                 </button> | ||||
|                 <div class="dropdown-menu bg-dark dropdown-menu-right px-2" id="searchtags"> | ||||
|                 <div class="dropdown-menu bg-dark px-2" id="searchtags"> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="input-group-prepend featAdvsearch"> | ||||
|                 <select class="form-control" id="searchMatch"> | ||||
|                   <option value="contains">contains</option> | ||||
|                   <option value="==">==</option> | ||||
|                   <option value="=~">=~</option> | ||||
|                   <option value="!=">!=</option> | ||||
|                   <option value="!~">!~</option> | ||||
|                 </select> | ||||
|               </div> | ||||
|               <input type="text" class="form-control" placeholder="Search" id="searchstr"/> | ||||
|             </div> | ||||
|           </form> | ||||
|           <div class="input-group mr-2"> | ||||
| @@ -550,15 +559,17 @@ | ||||
|               </div>             | ||||
|             <button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="SearchPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">»</button> | ||||
|           </div> | ||||
|           <div class="btn-group mr-2 featTags"> | ||||
|           <div class="btn-group featTags"> | ||||
|             <button id="SearchColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button> | ||||
|             <div class="dropdown-menu bg-dark px-2" id="SearchColsDropdown"><form></form> | ||||
|             <div class="dropdown-menu bg-dark px-2 dropdown-menu-right" id="SearchColsDropdown"><form></form> | ||||
|               <button data-href='{"cmd": "saveCols", "options": ["Search"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button> | ||||
|             </div>           | ||||
|           </div> | ||||
|         </div> | ||||
|         <ol class="FeatAdvsearch breadcrumb" id="searchCrumb"></ol> | ||||
|  | ||||
|         <div class="table-responsive-md"> | ||||
|           <table id="SearchList" class="table table-hover table-sm"> | ||||
|           <table id="SearchList" class="table table-hover table-sm" data-sort=""> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 <th></th> | ||||
|   | ||||
| @@ -276,8 +276,30 @@ function appRoute() { | ||||
|     else if (app.current.app == 'Search') { | ||||
|         var searchstrEl = document.getElementById('searchstr'); | ||||
|         searchstrEl.focus(); | ||||
|         if (searchstrEl.value == '' && app.current.search != '') | ||||
|             searchstrEl.value = app.current.search; | ||||
|         if (settings.featAdvsearch) { | ||||
|             var crumbs = ''; | ||||
|             var elements = app.current.search.substring(1, app.current.search.length - 1).split(' AND '); | ||||
|             for (var i = 0; i < elements.length - 1 ; i++) { | ||||
|                 var value = elements[i].substring(1, elements[i].length - 1); | ||||
|                 crumbs += '<button data-filter="' + encodeURI(value) + '" class="btn btn-light mr-2">' + value + '<span class="ml-2 badge badge-secondary">×</span></button>'; | ||||
|             } | ||||
|             document.getElementById('searchCrumb').innerHTML = crumbs; | ||||
|             if (searchstrEl.value == '' && elements.length >= 1) { | ||||
|                 var lastEl = elements[elements.length - 1].substring(1,  elements[elements.length - 1].length - 1); | ||||
|                 var lastElValue = lastEl.substring(lastEl.indexOf('\'') + 1, lastEl.length - 1); | ||||
|                 if (searchstrEl.value != lastElValue) | ||||
|                     document.getElementById('searchCrumb').innerHTML += '<button data-filter="' + encodeURI(lastEl) +'" class="btn btn-light mr-2">' + lastEl + '<span href="#" class="ml-2 badge badge-secondary">×</span></button>'; | ||||
|                 var match = lastEl.substring(lastEl.indexOf(' ') + 1); | ||||
|                 match = match.substring(0, match.indexOf(' ')); | ||||
|                 if (match == '') | ||||
|                     match = 'contains'; | ||||
|                 document.getElementById('searchMatch').value = match; | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             if (searchstrEl.value == '' && app.current.search != '') | ||||
|                 searchstrEl.value = app.current.search; | ||||
|         } | ||||
|         if (app.last.app != app.current.app) { | ||||
|             if (app.current.search != '') { | ||||
|                 var colspan = settings['cols' + app.current.app].length; | ||||
| @@ -289,12 +311,33 @@ function appRoute() { | ||||
|         } | ||||
|  | ||||
|         if (app.current.search.length >= 2) { | ||||
|             sendAPI({"cmd": "MPD_API_DATABASE_SEARCH", "data": { "plist": "", "offset": app.current.page, "filter": app.current.filter, "searchstr": app.current.search}}, parseSearch); | ||||
|             if (settings.featAdvsearch) { | ||||
|                 var sort = document.getElementById('SearchList').getAttribute('data-sort'); | ||||
|                 var sortdesc = false; | ||||
|                 if (sort == '') { | ||||
|                     if (settings.tags.includes('Title')) | ||||
|                         sort = 'Title'; | ||||
|                     else | ||||
|                         sort = 'Filename'; | ||||
|                     document.getElementById('SearchList').setAttribute('data-sort', sort); | ||||
|                 } | ||||
|                 else { | ||||
|                     if (sort.indexOf('-') == 0) { | ||||
|                         sortdesc = true; | ||||
|                         sort = sort.substring(1); | ||||
|                     } | ||||
|                 } | ||||
|                 sendAPI({"cmd": "MPD_API_DATABASE_SEARCH_ADV", "data": { "plist": "", "offset": app.current.page, "sort": sort, "sortdesc": sortdesc, "expression": app.current.search}}, parseSearch); | ||||
|             } | ||||
|             else { | ||||
|                 sendAPI({"cmd": "MPD_API_DATABASE_SEARCH", "data": { "plist": "", "offset": app.current.page, "filter": app.current.filter, "searchstr": app.current.search}}, parseSearch); | ||||
|             } | ||||
|         } else { | ||||
|             document.getElementById('SearchList').getElementsByTagName('tbody')[0].innerHTML = ''; | ||||
|             document.getElementById('searchAddAllSongs').setAttribute('disabled', 'disabled'); | ||||
|             document.getElementById('searchAddAllSongsBtn').setAttribute('disabled', 'disabled'); | ||||
|             document.getElementById('panel-heading-search').innerText = ''; | ||||
|             document.getElementById('cardFooterSearch').innerText = ''; | ||||
|             document.getElementById('SearchList').classList.remove('opacity05'); | ||||
|             setPagination(0); | ||||
|         } | ||||
| @@ -608,8 +651,81 @@ function appInit() { | ||||
|     document.getElementById('searchstr').addEventListener('keyup', function(event) { | ||||
|         if (event.key == 'Escape') | ||||
|             this.blur(); | ||||
|         else if (event.key == 'Enter' && settings.featAdvsearch) { | ||||
|             if (this.value != '') { | ||||
|                 var match = document.getElementById('searchMatch'); | ||||
|                 var li = document.createElement('button'); | ||||
|                 li.classList.add('btn', 'btn-light', 'mr-2'); | ||||
|                 li.setAttribute('data-filter', encodeURI(app.current.filter + ' ' + match.options[match.selectedIndex].value +' \'' + this.value + '\'')); | ||||
|                 li.innerHTML = app.current.filter + ' ' + match.options[match.selectedIndex].value + ' \'' + this.value + '\'<span class="ml-2 badge badge-secondary">×</span>'; | ||||
|                 this.value = ''; | ||||
|                 document.getElementById('searchCrumb').appendChild(li); | ||||
|             } | ||||
|             else | ||||
|                 search(this.value); | ||||
|         } | ||||
|         else | ||||
|             appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + this.value); | ||||
|             search(this.value); | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('searchCrumb').addEventListener('click', function(event) { | ||||
|         event.preventDefault(); | ||||
|         event.stopPropagation(); | ||||
|         if (event.target.nodeName == 'SPAN') { | ||||
|             event.target.parentNode.remove(); | ||||
|             search(''); | ||||
|         } | ||||
|         else if (event.target.nodeName == 'BUTTON') { | ||||
|             var value = decodeURI(event.target.getAttribute('data-filter')); | ||||
|             document.getElementById('searchstr').value = value.substring(value.indexOf('\'') + 1, value.length - 1); | ||||
|             var filter = value.substring(0, value.indexOf(' ')); | ||||
|             selectTag('searchtags', 'searchtagsdesc', filter); | ||||
|             var match = value.substring(value.indexOf(' ') + 1); | ||||
|             match = match.substring(0, match.indexOf(' ')); | ||||
|             document.getElementById('searchMatch').value = match; | ||||
|             event.target.remove(); | ||||
|             search(document.getElementById('searchstr').value); | ||||
|         } | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('searchMatch').addEventListener('change', function(event) { | ||||
|         search(document.getElementById('searchstr').value); | ||||
|     }, false); | ||||
|      | ||||
|     document.getElementById('SearchList').getElementsByTagName('tr')[0].addEventListener('click', function(event) { | ||||
|         if (settings.featAdvsearch) { | ||||
|             if (event.target.nodeName == 'TH') { | ||||
|                 var col = event.target.getAttribute('data-col'); | ||||
|                 if (col == 'Duration') | ||||
|                     return; | ||||
|                 var sortcol = document.getElementById('SearchList').getAttribute('data-sort'); | ||||
|                 var sortdesc = true; | ||||
|                  | ||||
|                 if (sortcol == col || sortcol == '-' + col) { | ||||
|                     if (sortcol.indexOf('-') == 0) { | ||||
|                         sortdesc = true; | ||||
|                         sortcol = sortcol.substring(1); | ||||
|                     } | ||||
|                     else | ||||
|                         sortdesc = false; | ||||
|                 } | ||||
|                 if (sortdesc == false) { | ||||
|                     sortcol = '-' + col; | ||||
|                     sortdesc = true; | ||||
|                 } | ||||
|                 else { | ||||
|                     sortdesc = false; | ||||
|                     sortcol = col; | ||||
|                 } | ||||
|                  | ||||
|                 var s = document.getElementById('SearchList').getElementsByClassName('sort-dir'); | ||||
|                 for (var i = 0; i < s.length; i++) | ||||
|                     s[i].remove(); | ||||
|                 document.getElementById('SearchList').setAttribute('data-sort', sortcol); | ||||
|                 event.target.innerHTML = col + '<span class="sort-dir material-icons pull-right">' + (sortdesc == true ? 'arrow_drop_up' : 'arrow_drop_down') + '</span>'; | ||||
|                 appRoute(); | ||||
|             } | ||||
|         } | ||||
|     }, false); | ||||
|  | ||||
|     document.getElementById('BrowseDatabaseByTagDropdown').addEventListener('click', function(event) { | ||||
| @@ -712,6 +828,28 @@ function parseCmd(event, href) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function search(x) { | ||||
|     if (settings.featAdvsearch) { | ||||
|         var expression = '('; | ||||
|         var crumbs = document.getElementById('searchCrumb').children; | ||||
|         for (var i = 0; i < crumbs.length; i++) { | ||||
|             expression += '(' + decodeURI(crumbs[i].getAttribute('data-filter')) + ')'; | ||||
|             if (x != '') expression += ' AND '; | ||||
|         } | ||||
|         if (x != '') { | ||||
|             var match = document.getElementById('searchMatch'); | ||||
|             expression += '(' + app.current.filter + ' ' + match.options[match.selectedIndex].value + ' \'' + x +'\'))'; | ||||
|         } | ||||
|         else | ||||
|             expression += ')'; | ||||
|         if (expression.length <= 2) | ||||
|             expression = ''; | ||||
|         appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + encodeURI(expression)); | ||||
|     } | ||||
|     else | ||||
|         appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + x); | ||||
| } | ||||
|  | ||||
| function dragAndDropTable(table) { | ||||
|     var tableBody=document.getElementById(table).getElementsByTagName('tbody')[0]; | ||||
|     tableBody.addEventListener('dragstart', function(event) { | ||||
| @@ -1059,7 +1197,7 @@ function parseSettings(obj) { | ||||
|      | ||||
|     toggleBtn('btnnotifyPage', settings.notificationPage); | ||||
|  | ||||
|     var features = ["featStickers", "featSmartpls", "featPlaylists", "featTags", "featLocalplayer", "featSyscmds", "featCoverimage"]; | ||||
|     var features = ["featStickers", "featSmartpls", "featPlaylists", "featTags", "featLocalplayer", "featSyscmds", "featCoverimage", "featAdvsearch"]; | ||||
|      | ||||
|     for (var j = 0; j < features.length; j++) { | ||||
|         var Els = document.getElementsByClassName(features[j]); | ||||
| @@ -1221,6 +1359,14 @@ function setCols(table, className) { | ||||
|     } | ||||
|     document.getElementById(table + 'ColsDropdown').firstChild.innerHTML = tagChks; | ||||
|      | ||||
|     var sort = document.getElementById('SearchList').getAttribute('data-sort'); | ||||
|     if (sort == '') { | ||||
|         if (settings.featTags) | ||||
|             sort = 'Title'; | ||||
|         else | ||||
|             sort = 'Filename'; | ||||
|     } | ||||
|      | ||||
|     if (table != 'Playback') { | ||||
|         var heading = ''; | ||||
|         for (var i = 0; i < settings['cols' + table].length; i++) { | ||||
| @@ -1228,9 +1374,20 @@ function setCols(table, className) { | ||||
|             heading += '<th draggable="true" data-col="' + h  + '">'; | ||||
|             if (h == 'Track' || h == 'Pos') | ||||
|                 h = '#'; | ||||
|             heading += h + '</th>'; | ||||
|             heading += h; | ||||
|  | ||||
|             if (table == 'Search' && h == sort ) { | ||||
|                 var sortdesc = false; | ||||
|                 if (sort.indexOf('-') == 0) { | ||||
|                     sortdesc = true; | ||||
|                     sort = sort.substring(1); | ||||
|                 } | ||||
|                 heading += '<span class="sort-dir material-icons pull-right">' + (sortdesc == true ? 'arrow_drop_up' : 'arrow_drop_down') + '</span>'; | ||||
|             } | ||||
|             heading += '</th>'; | ||||
|         } | ||||
|         heading += '<th></th>'; | ||||
|          | ||||
|         if (className == undefined)  | ||||
|             document.getElementById(table + 'List').getElementsByTagName('tr')[0].innerHTML = heading; | ||||
|         else { | ||||
|   | ||||
| @@ -56,6 +56,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { | ||||
|     unsigned int uint_buf1, uint_buf2, uint_rc; | ||||
|     int je, int_buf1, int_rc;  | ||||
|     float float_buf; | ||||
|     bool bool_buf; | ||||
|     char *p_charbuf1, *p_charbuf2, *p_charbuf3, *p_charbuf4; | ||||
|     char p_char[4]; | ||||
|     enum mpd_cmd_ids cmd_id; | ||||
| @@ -594,6 +595,16 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { | ||||
|                 free(p_charbuf2); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_DATABASE_SEARCH_ADV: | ||||
|             je = json_scanf(msg.p, msg.len, "{data: {expression:%Q, sort:%Q, sortdesc:%B, plist:%Q, offset:%u}}",  | ||||
|                 &p_charbuf1, &p_charbuf2, &bool_buf, &p_charbuf3, &uint_buf1); | ||||
|             if (je == 5) { | ||||
|                 n = mympd_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); | ||||
|                 free(p_charbuf1); | ||||
|                 free(p_charbuf2); | ||||
|                 free(p_charbuf3); | ||||
|             } | ||||
|             break; | ||||
|         case MPD_API_QUEUE_SHUFFLE: | ||||
|             mpd_run_shuffle(mpd.conn); | ||||
|             n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); | ||||
| @@ -2202,12 +2213,12 @@ int mympd_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, | ||||
|     if (mpd_search_add_expression(mpd.conn, expression) == false) | ||||
|         RETURN_ERROR_AND_RECOVER("mpd_search_add_expression"); | ||||
|      | ||||
|     if (sort != NULL && strcmp(plist, "") == 0) { | ||||
|     if (sort != NULL && strcmp(sort, "") != 0 && strcmp(plist, "") == 0) { | ||||
|         if (mpd_search_add_sort_name(mpd.conn, sort, sortdesc) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_sort_name"); | ||||
|     } | ||||
|      | ||||
|     if (grouptag != NULL && strcmp(plist, "") == 0) { | ||||
|     if (grouptag != NULL && strcmp(grouptag, "") != 0 && strcmp(plist, "") == 0) { | ||||
|         if (mpd_search_add_group_tag(mpd.conn, mpd_tag_name_parse(grouptag)) == false) | ||||
|             RETURN_ERROR_AND_RECOVER("mpd_search_add_group_tag"); | ||||
|     } | ||||
|   | ||||
| @@ -98,6 +98,7 @@ | ||||
|     X(MPD_API_SMARTPLS_UPDATE_ALL) \ | ||||
|     X(MPD_API_SMARTPLS_SAVE) \ | ||||
|     X(MPD_API_SMARTPLS_GET) \ | ||||
|     X(MPD_API_DATABASE_SEARCH_ADV) \ | ||||
|     X(MPD_API_DATABASE_SEARCH) \ | ||||
|     X(MPD_API_DATABASE_UPDATE) \ | ||||
|     X(MPD_API_DATABASE_RESCAN) \ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 jcorporation
					jcorporation