From 3dd75ba3b5ec3e1770525953114f5fdb436cf045 Mon Sep 17 00:00:00 2001 From: regorxxx Date: Fri, 21 Jul 2023 13:31:05 +0200 Subject: [PATCH] - Selection manipulation\Shuffle: new sorting bias settings. Key (sorted from 12A to 1B), Key 6A centered (starting from 6A). Using these is like merging Harmonic Mix (consecutive tracks should have similar keys) and Smart Shuffle. - Selection manipulation\Group: new tool to group tracks by TF without respecting the original sorting. It may be used to listen to all tracks of a random album played in a shuffled order, then all of another album (chosen randomly), ... - Selection manipulation\Shuffle: sorting bias setting not being applied on selection manipulation tool (thus always using 'random'). --- .../playlist_tools_menu_pools.js | 4 +- .../playlist_tools_menu_search_by_distance.js | 4 +- .../playlist_tools_menu_sel_manipulation.js | 106 +++++++++++++++++- main/sort/scatter_by_tags.js | 8 +- 4 files changed, 114 insertions(+), 8 deletions(-) diff --git a/main/playlist_tools/playlist_tools_menu_pools.js b/main/playlist_tools/playlist_tools_menu_pools.js index 981283fe..1e06f3cb 100644 --- a/main/playlist_tools/playlist_tools_menu_pools.js +++ b/main/playlist_tools/playlist_tools_menu_pools.js @@ -1,5 +1,5 @@ 'use strict'; -//09/06/23 +//04/07/23 // Pools { @@ -654,6 +654,8 @@ {key: 'Rating', flags: MF_STRING}, {key: 'Popularity', flags: utils.GetPackageInfo('{F5E9D9EB-42AD-4A47-B8EE-C9877A8E7851}') ? MF_STRING : MF_GRAYED, req: 'Find & Play'}, {key: 'Last played', flags: isPlayCount ? MF_STRING : MF_GRAYED, req: 'foo_playcount'}, + {key: 'Key', flags: MF_STRING}, + {key: 'Key 6A centered', flags: MF_STRING}, ]; menu.newEntry({menuName: subMenuNameSecond, entryText: 'Prioritize tracks by:', flags: MF_GRAYED}); menu.newEntry({menuName: subMenuNameSecond, entryText: 'sep'}); diff --git a/main/playlist_tools/playlist_tools_menu_search_by_distance.js b/main/playlist_tools/playlist_tools_menu_search_by_distance.js index bec044f5..a12225ac 100644 --- a/main/playlist_tools/playlist_tools_menu_search_by_distance.js +++ b/main/playlist_tools/playlist_tools_menu_search_by_distance.js @@ -1,5 +1,5 @@ 'use strict'; -//27/03/23 +//04/07/23 // Similar by...Graph\Dyngenre\Weight { @@ -371,6 +371,8 @@ {key: 'Rating', flags: MF_STRING}, {key: 'Popularity', flags: utils.GetPackageInfo('{F5E9D9EB-42AD-4A47-B8EE-C9877A8E7851}') ? MF_STRING : MF_GRAYED, req: 'Find & Play'}, {key: 'Last played', flags: isPlayCount ? MF_STRING : MF_GRAYED, req: 'foo_playcount'}, + {key: 'Key', flags: MF_STRING}, + {key: 'Key 6A centered', flags: MF_STRING}, ]; menu.newEntry({menuName: subMenuNameSecond, entryText: 'Prioritize tracks by:', flags: MF_GRAYED}); menu.newEntry({menuName: subMenuNameSecond, entryText: 'sep'}); diff --git a/main/playlist_tools/playlist_tools_menu_sel_manipulation.js b/main/playlist_tools/playlist_tools_menu_sel_manipulation.js index 7ccd4005..d15bb6f4 100644 --- a/main/playlist_tools/playlist_tools_menu_sel_manipulation.js +++ b/main/playlist_tools/playlist_tools_menu_sel_manipulation.js @@ -1,5 +1,5 @@ 'use strict'; -//13/06/23 +//21/07/23 // Selection manipulation... { @@ -467,7 +467,12 @@ } else {entryNames.add(shuffleName);} // Entries menu.newEntry({menuName: subMenuName, entryText: shuffleName, func: () => { - shuffleByTags({...shuffleObj.args, bDebug: defaultArgs.bDebug}); + shuffleByTags({ + ...shuffleObj.args, + bAdvancedShuffle: menu_properties.bSmartShuffleAdvc[1], + sortBias: menu_properties.smartShuffleSortBias[1], + bDebug: defaultArgs.bDebug + }); }, flags: multipleSelectedFlagsReorder}); } }); @@ -501,7 +506,6 @@ }); } }}); - menu.newEntry({menuName, entryText: 'sep'}); if (!menusEnabled.hasOwnProperty(configMenu) || menusEnabled[configMenu] === true) { const subMenuName = 'Smart shuffle'; if (!menu.hasMenu(subMenuName, configMenu)) { @@ -533,6 +537,8 @@ {key: 'Rating', flags: MF_STRING}, {key: 'Popularity', flags: utils.GetPackageInfo('{F5E9D9EB-42AD-4A47-B8EE-C9877A8E7851}') ? MF_STRING : MF_GRAYED, req: 'Find & Play'}, {key: 'Last played', flags: isPlayCount ? MF_STRING : MF_GRAYED, req: 'foo_playcount'}, + {key: 'Key', flags: MF_STRING}, + {key: 'Key 6A centered', flags: MF_STRING}, ]; menu.newEntry({menuName: subMenuNameSecond, entryText: 'Prioritize tracks by:', flags: MF_GRAYED}); menu.newEntry({menuName: subMenuNameSecond, entryText: 'sep'}); @@ -562,6 +568,100 @@ } else {menuDisabled.push({menuName: name, subMenuFrom: menuName, index: menu.getMenus().filter((entry) => {return menuAltAllowed.has(entry.subMenuFrom);}).length + disabledCount++, bIsMenu: true});} } } + { // Group + const scriptPath = folders.xxx + 'main\\sort\\group_by_tags.js'; + if (_isFile(scriptPath)){ + const name = 'Group by tags'; + if (!menusEnabled.hasOwnProperty(name) || menusEnabled[name] === true) { + include(scriptPath.replace(folders.xxx + 'main\\', '..\\')); + readmes[menuName + '\\' + name] = folders.xxx + 'helpers\\readme\\group_by_tags.txt'; + const subMenuName = menu.newMenu(name, menuName); + let group = [ + {name: 'Group by artist' , args: {tagName: [globTags.artist]}}, + {name: 'Group by genre' , args: {tagName: [globTags.genre]}}, + {name: 'Group by style' , args: {tagName: [globTags.style]}}, + {name: 'Group by album' , args: {tagName: ['ALBUM']}} + ]; + let selArg = {name: 'Custom', args: group[0].args}; + const groupDefaults = [...group]; + // Create new properties with previous args + menu_properties['group'] = [menuName + '\\' + name + ' entries', JSON.stringify(group)]; + menu_properties['groupCustomArg'] = [menuName + '\\' + name + ' Dynamic menu custom args', JSON.stringify(selArg)]; + // Check + menu_properties['group'].push({func: isJSON}, menu_properties['group'][1]); + menu_properties['groupCustomArg'].push({func: isJSON}, menu_properties['groupCustomArg'][1]); + // Helpers + const inputGroup = () => { + let tagName = ''; + try {tagName = utils.InputBox(window.ID, 'Enter tag(s) or TF expression(s):\n(multiple values may be separated by \';\')', scriptName + ': ' + name, selArg.args.tagName, true);} + catch (e) {return;} + if (!tagName.length) {return;} + tagName = tagName.split(/;|,/g); + return {args: {tagName}}; + }; + // Menus + menu.newEntry({menuName: subMenuName, entryText: 'Group by TF (without sorting):', func: null, flags: MF_GRAYED}); + menu.newEntry({menuName: subMenuName, entryText: 'sep'}); + menu.newCondEntry({entryText: 'Group... (cond)', condFunc: () => { + // Entry list + group = JSON.parse(menu_properties['group'][1]); + const entryNames = new Set(); + group.forEach((groupObj) => { + // Add separators + if (groupObj.hasOwnProperty('name') && groupObj.name === 'sep') { + menu.newEntry({menuName: subMenuName, entryText: 'sep'}); + } else { + // Create names for all entries + let groupName = groupObj.name; + groupName = groupName.length > 40 ? groupName.substring(0,40) + ' ...' : groupName; + if (entryNames.has(groupName)) { + fb.ShowPopupMessage('There is an entry with duplicated name:\t' + groupName + '\nEdit the custom entries and either remove or rename it.\n\nEntry:\n' + JSON.stringify(groupObj, null, '\t'), scriptName + ': ' + name); + return; + } else {entryNames.add(groupName);} + // Entries + menu.newEntry({menuName: subMenuName, entryText: groupName, func: () => { + groupByTags({ + ...groupObj.args, + bDebug: defaultArgs.bDebug + }); + }, flags: multipleSelectedFlagsReorder}); + } + }); + menu.newEntry({menuName: subMenuName, entryText: 'sep'}); + { // Static menu: user configurable + menu.newEntry({menuName: subMenuName, entryText: 'By... (tag)', func: () => { + const ap = plman.ActivePlaylist; + if (ap === -1) {return;} + // On first execution, must update from property + selArg.args.tagName = JSON.parse(menu_properties['groupCustomArg'][1]).args.tagName; + // Input + const input = inputGroup(); + if (!input) {return;} + // Execute + groupByTags({...input.args, bDebug: defaultArgs.bDebug}); + // For internal use original object + selArg.args = input.args; + menu_properties['groupCustomArg'][1] = JSON.stringify(selArg); // And update property with new value + overwriteMenuProperties(); // Updates panel + }, flags: multipleSelectedFlagsReorder}); + menu.newEntry({menuName: subMenuName, entryText: 'sep'}); + } + { // Add / Remove + createSubMenuEditEntries(subMenuName, { + name, + list: group, + propName: 'group', + defaults: groupDefaults, + defaultPreset: folders.xxx + 'presets\\Playlist Tools\\group\\default.json', + input : inputGroup + }); + } + }}); + } else {menuDisabled.push({menuName: name, subMenuFrom: menuName, index: menu.getMenus().filter((entry) => {return menuAltAllowed.has(entry.subMenuFrom);}).length + disabledCount++, bIsMenu: true});} + } + } + ['Sort...', 'Advanced sort...', 'Scatter by tags', 'Intercalate by tags', 'Shuffle by tags', 'Group by tags'] + .some((n) => !menusEnabled.hasOwnProperty(n) || menusEnabled[n] === true) && menu.newEntry({menuName, entryText: 'sep'}); { // Remove and find in playlists const scriptPath = folders.xxx + 'main\\playlists\\find_remove_from_playlists.js'; if (_isFile(scriptPath)){ diff --git a/main/sort/scatter_by_tags.js b/main/sort/scatter_by_tags.js index 5ed14e16..af7520ed 100644 --- a/main/sort/scatter_by_tags.js +++ b/main/sort/scatter_by_tags.js @@ -1,5 +1,5 @@ 'use strict'; -//27/03/23 +//21/07/23 include('..\\..\\helpers\\helpers_xxx_basic_js.js'); include('..\\..\\helpers\\helpers_xxx_prototypes.js'); @@ -91,7 +91,7 @@ function scatterByTags({ plman.SetPlaylistSelection(plman.ActivePlaylist, idx, true); plman.SetPlaylistFocusItem(plman.ActivePlaylist, focusIdx); } - console.log('Selection scattered by tag(s) \'' + tagValue.join(',') + '\' (' + tagName.join(', ') + ') on playlist: ' + plman.GetPlaylistName(plman.ActivePlaylist)); + console.log('Selection scattered by tag(s) \'' + tagValue.join(', ') + '\' (' + tagName.join(', ') + ') on playlist: ' + plman.GetPlaylistName(plman.ActivePlaylist)); } return selItemsArray; } @@ -181,7 +181,7 @@ function shuffleByTags({ bSendToActivePls = true, data = {handleArray: [], dataArray: [], tagsArray: []}, // Shallow copies are made bAdvancedShuffle = false, // Tries to scatter instrumental, live tracks, ... - sortBias = 'random', // random | playcount | rating | popularity | lastplayed | TitleFormat expression || '' (none) + sortBias = 'random', // random | playcount | rating | popularity | lastplayed | key | TitleFormat expression || '' (none) sortDir = 1, bDebug = false } = {}) { @@ -209,6 +209,8 @@ function shuffleByTags({ case 'rating': sortTF = '$max(%RATING%,$meta(RATING),0)'; break; case 'popularity': sortTF = '$max($meta(Track Statistics Last.fm,5[score]),0)'; break; case 'lastplayed': sortTF = bEnhPlayCount ? '%LAST_PLAYED_ENHANCED%' : '%LAST_PLAYED%'; break; + case 'key': sortTF = '$if($stricmp(%KEY%,G#m),$puts(kTrans,1B))$if($stricmp(%KEY%,Abm),$puts(kTrans,1B))$if($stricmp(%KEY%,D#m),$puts(kTrans,2B))$if($stricmp(%KEY%,Ebm),$puts(kTrans,2B))$if($stricmp(%KEY%,A#m),$puts(kTrans,3B))$if($stricmp(%KEY%,Bbm),$puts(kTrans,3B))$if($stricmp(%KEY%,Fm),$puts(kTrans,4B))$if($stricmp(%KEY%,Cm),$puts(kTrans,5B))$if($stricmp(%KEY%,Gm),$puts(kTrans,6B))$if($stricmp(%KEY%,Dm),$puts(kTrans,7B))$if($stricmp(%KEY%,Am),$puts(kTrans,8B))$if($stricmp(%KEY%,Em),$puts(kTrans,9B))$if($stricmp(%KEY%,Bm),$puts(kTrans,10B))$if($stricmp(%KEY%,F#m),$puts(kTrans,11B))$if($stricmp(%KEY%,Gbm),$puts(kTrans,11B))$if($stricmp(%KEY%,C#m),$puts(kTrans,12B))$if($stricmp(%KEY%,Dbm),$puts(kTrans,12B))$if($stricmp(%KEY%,6m),$puts(kTrans,1B))$if($stricmp(%KEY%,7m),$puts(kTrans,2B))$if($stricmp(%KEY%,8m),$puts(kTrans,3B))$if($stricmp(%KEY%,9m),$puts(kTrans,4B))$if($stricmp(%KEY%,10m),$puts(kTrans,5B))$if($stricmp(%KEY%,11m),$puts(kTrans,6B))$if($stricmp(%KEY%,12m),$puts(kTrans,7B))$if($stricmp(%KEY%,1m),$puts(kTrans,8B))$if($stricmp(%KEY%,2m),$puts(kTrans,9B))$if($stricmp(%KEY%,3m),$puts(kTrans,10B))$if($stricmp(%KEY%,4m),$puts(kTrans,11B))$if($stricmp(%KEY%,5m),$puts(kTrans,12B))$if($stricmp(%KEY%,B),$puts(kTrans,1A))$if($stricmp(%KEY%,F#),$puts(kTrans,2A))$if($stricmp(%KEY%,Gb),$puts(kTrans,2A))$if($stricmp(%KEY%,C#),$puts(kTrans,3A))$if($stricmp(%KEY%,Db),$puts(kTrans,3A))$if($stricmp(%KEY%,G#),$puts(kTrans,4A))$if($stricmp(%KEY%,Ab),$puts(kTrans,4A))$if($stricmp(%KEY%,D#),$puts(kTrans,5A))$if($stricmp(%KEY%,Eb),$puts(kTrans,5A))$if($stricmp(%KEY%,A#),$puts(kTrans,6A))$if($stricmp(%KEY%,Bb),$puts(kTrans,6A))$if($stricmp(%KEY%,F),$puts(kTrans,7A))$if($stricmp(%KEY%,C),$puts(kTrans,8A))$if($stricmp(%KEY%,G),$puts(kTrans,9A))$if($stricmp(%KEY%,D),$puts(kTrans,10A))$if($stricmp(%KEY%,A),$puts(kTrans,11A))$if($stricmp(%KEY%,E),$puts(kTrans,12A))$if($stricmp(%KEY%,6d),$puts(kTrans,1A))$if($stricmp(%KEY%,7d),$puts(kTrans,2A))$if($stricmp(%KEY%,8d),$puts(kTrans,3A))$if($stricmp(%KEY%,9d),$puts(kTrans,4A))$if($stricmp(%KEY%,10d),$puts(kTrans,5A))$if($stricmp(%KEY%,11d),$puts(kTrans,6A))$if($stricmp(%KEY%,12d),$puts(kTrans,7A))$if($stricmp(%KEY%,1d),$puts(kTrans,8A))$if($stricmp(%KEY%,2d),$puts(kTrans,9A))$if($stricmp(%KEY%,3d),$puts(kTrans,10A))$if($stricmp(%KEY%,4d),$puts(kTrans,11A))$if($stricmp(%KEY%,5d),$puts(kTrans,12A))$if($get(kTrans),,$puts(kTrans,%key%))$get(kTrans)';break; + case 'key6acentered': sortTF = '$if($stricmp(%KEY%,G#m),$puts(kTrans,7B))$if($stricmp(%KEY%,Abm),$puts(kTrans,7B))$if($stricmp(%KEY%,D#m),$puts(kTrans,8B))$if($stricmp(%KEY%,Ebm),$puts(kTrans,8B))$if($stricmp(%KEY%,A#m),$puts(kTrans,9B))$if($stricmp(%KEY%,Bbm),$puts(kTrans,9B))$if($stricmp(%KEY%,Fm),$puts(kTrans,10B))$if($stricmp(%KEY%,Cm),$puts(kTrans,11B))$if($stricmp(%KEY%,Gm),$puts(kTrans,12B))$if($stricmp(%KEY%,Dm),$puts(kTrans,1B))$if($stricmp(%KEY%,Am),$puts(kTrans,2B))$if($stricmp(%KEY%,Em),$puts(kTrans,3B))$if($stricmp(%KEY%,Bm),$puts(kTrans,4B))$if($stricmp(%KEY%,F#m),$puts(kTrans,5B))$if($stricmp(%KEY%,Gbm),$puts(kTrans,5B))$if($stricmp(%KEY%,C#m),$puts(kTrans,6B))$if($stricmp(%KEY%,Dbm),$puts(kTrans,6B))$if($stricmp(%KEY%,6m),$puts(kTrans,7B))$if($stricmp(%KEY%,7m),$puts(kTrans,8B))$if($stricmp(%KEY%,8m),$puts(kTrans,9B))$if($stricmp(%KEY%,9m),$puts(kTrans,10B))$if($stricmp(%KEY%,10m),$puts(kTrans,11B))$if($stricmp(%KEY%,11m),$puts(kTrans,12B))$if($stricmp(%KEY%,12m),$puts(kTrans,1B))$if($stricmp(%KEY%,1m),$puts(kTrans,2B))$if($stricmp(%KEY%,2m),$puts(kTrans,3B))$if($stricmp(%KEY%,3m),$puts(kTrans,4B))$if($stricmp(%KEY%,4m),$puts(kTrans,5B))$if($stricmp(%KEY%,5m),$puts(kTrans,6B))$if($stricmp(%KEY%,B),$puts(kTrans,7A))$if($stricmp(%KEY%,F#),$puts(kTrans,8A))$if($stricmp(%KEY%,Gb),$puts(kTrans,8A))$if($stricmp(%KEY%,C#),$puts(kTrans,9A))$if($stricmp(%KEY%,Db),$puts(kTrans,9A))$if($stricmp(%KEY%,G#),$puts(kTrans,10A))$if($stricmp(%KEY%,Ab),$puts(kTrans,10A))$if($stricmp(%KEY%,D#),$puts(kTrans,11A))$if($stricmp(%KEY%,Eb),$puts(kTrans,11A))$if($stricmp(%KEY%,A#),$puts(kTrans,12A))$if($stricmp(%KEY%,Bb),$puts(kTrans,12A))$if($stricmp(%KEY%,F),$puts(kTrans,1A))$if($stricmp(%KEY%,C),$puts(kTrans,2A))$if($stricmp(%KEY%,G),$puts(kTrans,3A))$if($stricmp(%KEY%,D),$puts(kTrans,4A))$if($stricmp(%KEY%,A),$puts(kTrans,5A))$if($stricmp(%KEY%,E),$puts(kTrans,6A))$if($stricmp(%KEY%,6d),$puts(kTrans,7A))$if($stricmp(%KEY%,7d),$puts(kTrans,8A))$if($stricmp(%KEY%,8d),$puts(kTrans,9A))$if($stricmp(%KEY%,9d),$puts(kTrans,10A))$if($stricmp(%KEY%,10d),$puts(kTrans,11A))$if($stricmp(%KEY%,11d),$puts(kTrans,12A))$if($stricmp(%KEY%,12d),$puts(kTrans,1A))$if($stricmp(%KEY%,1d),$puts(kTrans,2A))$if($stricmp(%KEY%,2d),$puts(kTrans,3A))$if($stricmp(%KEY%,3d),$puts(kTrans,4A))$if($stricmp(%KEY%,4d),$puts(kTrans,5A))$if($stricmp(%KEY%,5d),$puts(kTrans,6A))$if($get(kTrans),,$puts(kTrans,%key%))$get(kTrans)';break; case 'random': sortTF = null; break; default: sortTF = sortBias; // Pass a TF expression or empty (don't sort) }