Implement shuffle from main menu
This commit is contained in:
1 parent
f8ef8b5667
commit
0bf16cda63
2 files changed
+120
-4
No files matched your search
+25
-2
@@ -142,6 +142,29 @@ static void to_pkjs_play_from_track(uint16_t seqId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void to_pkjs_shuffle_in_scope(NavScope scope, uint16_t *subscope_index, bool use_groups) {
|
||||||
|
if (!s_pkjs_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DictionaryIterator *out;
|
||||||
|
AppMessageResult result = app_message_outbox_begin(&out);
|
||||||
|
if (result != APP_MSG_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *subscope = subscope_index ? s_current_menu_vanity[*subscope_index] : "";
|
||||||
|
if (subscope_index) {
|
||||||
|
dict_write_cstring(out, MESSAGE_KEY_OUT_MENU_NAV_SUBSCOPE, subscope);
|
||||||
|
}
|
||||||
|
dict_write_uint8(out, MESSAGE_KEY_BIDIR_MENU_NAV_SCOPE, scope);
|
||||||
|
dict_write_uint8(out, MESSAGE_KEY_OUT_SHUFFLE_MODE, use_groups ? 2 : 1);
|
||||||
|
|
||||||
|
result = app_message_outbox_send();
|
||||||
|
if (result != APP_MSG_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void to_pkjs_move_deeper(NavScope scope, uint16_t *subscope_index) {
|
static void to_pkjs_move_deeper(NavScope scope, uint16_t *subscope_index) {
|
||||||
if (!s_pkjs_ready) {
|
if (!s_pkjs_ready) {
|
||||||
return;
|
return;
|
||||||
@@ -236,10 +259,10 @@ static void menu_select_click(struct MenuLayer *menu_layer, MenuIndex *clicked_i
|
|||||||
case NAV_SCOPE_MAIN:
|
case NAV_SCOPE_MAIN:
|
||||||
switch (clicked_index->row) {
|
switch (clicked_index->row) {
|
||||||
case 0:
|
case 0:
|
||||||
// TODO tell PKJS to shuffle all songs
|
to_pkjs_shuffle_in_scope(s_current_menu_scope, NULL, false);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// TODO tell PKJS to shuffle artists
|
to_pkjs_shuffle_in_scope(s_current_menu_scope, NULL, true);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
to_pkjs_move_deeper(NAV_SCOPE_ARTISTS, NULL);
|
to_pkjs_move_deeper(NAV_SCOPE_ARTISTS, NULL);
|
||||||
|
|||||||
+95
-2
@@ -14,7 +14,8 @@ var NavScope = Object.freeze({
|
|||||||
var cfg = JSON.parse(localStorage.getItem('clay-settings'));
|
var cfg = JSON.parse(localStorage.getItem('clay-settings'));
|
||||||
var currentArtist = null;
|
var currentArtist = null;
|
||||||
|
|
||||||
////////////////////// GENERAL API VARS //////////////////////
|
////////////////////// GENERAL VARS //////////////////////
|
||||||
|
var QUEUE_SIZE = 100;
|
||||||
var USER_ID_CACHE_KEY = 'user-id';
|
var USER_ID_CACHE_KEY = 'user-id';
|
||||||
var jfSessionId;
|
var jfSessionId;
|
||||||
|
|
||||||
@@ -229,6 +230,41 @@ function ringSlice(arr, start, end) {
|
|||||||
return Array.from({ length: count }, (_, k) => arr[(((start + k) % n) + n) % n]);
|
return Array.from({ length: count }, (_, k) => arr[(((start + k) % n) + n) % n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fisher-Yates shuffles a copy of an array
|
||||||
|
function shuffledCopy(arr) {
|
||||||
|
const result = arr.slice();
|
||||||
|
for (let i = result.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
const tmp = result[i];
|
||||||
|
result[i] = result[j];
|
||||||
|
result[j] = tmp;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// flattens an album to tracks map into jfIds
|
||||||
|
function albumMapJfIds(albumToTracksMap) {
|
||||||
|
const jfIds = [];
|
||||||
|
for (const tracks of albumToTracksMap.values()) {
|
||||||
|
for (const track of tracks) {
|
||||||
|
jfIds.push(track.JfId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jfIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shuffle the order of whole groups while keeping each group's tracks together
|
||||||
|
function groupedShuffle(groups, limit) {
|
||||||
|
const result = [];
|
||||||
|
for (const group of shuffledCopy(groups)) {
|
||||||
|
for (const jfId of group) {
|
||||||
|
result.push(jfId);
|
||||||
|
if (result.length >= limit) { return result; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Pebble.addEventListener("appmessage", function (dict) {
|
Pebble.addEventListener("appmessage", function (dict) {
|
||||||
//console.log("payload", JSON.stringify(dict.payload));
|
//console.log("payload", JSON.stringify(dict.payload));
|
||||||
//console.log("expected NAV_SCOPE key", keys.BIDIR_MENU_NAV_SCOPE);
|
//console.log("expected NAV_SCOPE key", keys.BIDIR_MENU_NAV_SCOPE);
|
||||||
@@ -236,12 +272,69 @@ Pebble.addEventListener("appmessage", function (dict) {
|
|||||||
const scope = payloadGet(dict.payload, "BIDIR_MENU_NAV_SCOPE");
|
const scope = payloadGet(dict.payload, "BIDIR_MENU_NAV_SCOPE");
|
||||||
const subscope = payloadGet(dict.payload, "OUT_MENU_NAV_SUBSCOPE");
|
const subscope = payloadGet(dict.payload, "OUT_MENU_NAV_SUBSCOPE");
|
||||||
const seqId = payloadGet(dict.payload, "OUT_PLAY_SEQ_ID");
|
const seqId = payloadGet(dict.payload, "OUT_PLAY_SEQ_ID");
|
||||||
|
const shuffleMode = payloadGet(dict.payload, "OUT_SHUFFLE_MODE");
|
||||||
|
|
||||||
if (seqId !== undefined) {
|
if (seqId !== undefined) {
|
||||||
callAPI("POST", cfg.PRIV_BASE_URL + "/Sessions/" + jfSessionId + "/Playing?playCommand=PlayNow&itemIds=" + ringSlice(allJfIds, seqId, seqId+99), null);
|
callAPI("POST", cfg.PRIV_BASE_URL + "/Sessions/" + jfSessionId + "/Playing?playCommand=PlayNow&itemIds=" + ringSlice(allJfIds, seqId, seqId+QUEUE_SIZE), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scope !== undefined) {
|
if (scope !== undefined) {
|
||||||
|
// scope shuffle
|
||||||
|
if (shuffleMode) {
|
||||||
|
// build a shuffled queue of jfIds for the current scope
|
||||||
|
let queuedJfIds = [];
|
||||||
|
switch (scope) {
|
||||||
|
case NavScope.NAV_SCOPE_MAIN:
|
||||||
|
if (shuffleMode == 1) {
|
||||||
|
// choose 100 random jfIds from the entire library (truly random order)
|
||||||
|
queuedJfIds = shuffledCopy(allJfIds).slice(0, QUEUE_SIZE);
|
||||||
|
} else {
|
||||||
|
// choose 100 random jfIds from the entire library (grouped by artist)
|
||||||
|
const artistGroups = [];
|
||||||
|
for (const artistToAlbumsMap of lookupMap.values()) {
|
||||||
|
for (const albumToTracksMap of artistToAlbumsMap.values()) {
|
||||||
|
artistGroups.push(albumMapJfIds(albumToTracksMap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queuedJfIds = groupedShuffle(artistGroups, QUEUE_SIZE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NavScope.NAV_SCOPE_ALBUMS: {
|
||||||
|
const artistToAlbumsMap = lookupMap.get(Array.from(subscope)[0].toUpperCase());
|
||||||
|
const albumToTracksMap = artistToAlbumsMap ? artistToAlbumsMap.get(subscope) : null;
|
||||||
|
if (albumToTracksMap) {
|
||||||
|
if (shuffleMode == 1) {
|
||||||
|
// choose 100 random jfIds from the current artist (truly random order)
|
||||||
|
queuedJfIds = shuffledCopy(albumMapJfIds(albumToTracksMap)).slice(0, QUEUE_SIZE);
|
||||||
|
} else {
|
||||||
|
// choose 100 random jfIds from the current artist (grouped by album)
|
||||||
|
const albumGroups = [];
|
||||||
|
for (const tracks of albumToTracksMap.values()) {
|
||||||
|
albumGroups.push(tracks.map((track) => track.JfId));
|
||||||
|
}
|
||||||
|
queuedJfIds = groupedShuffle(albumGroups, QUEUE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NavScope.NAV_SCOPE_ALBUM_TRACKS: {
|
||||||
|
const artistToAlbumsMap = lookupMap.get(Array.from(currentArtist)[0].toUpperCase());
|
||||||
|
const albumToTracksMap = artistToAlbumsMap ? artistToAlbumsMap.get(currentArtist) : null;
|
||||||
|
const tracks = albumToTracksMap ? albumToTracksMap.get(subscope) : null;
|
||||||
|
if (tracks) {
|
||||||
|
// randomize the order of jfIds from the current album
|
||||||
|
queuedJfIds = shuffledCopy(tracks.map((track) => track.JfId));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queuedJfIds.length > 0) {
|
||||||
|
callAPI("POST", cfg.PRIV_BASE_URL + "/Sessions/" + jfSessionId + "/Playing?playCommand=PlayNow&itemIds=" + queuedJfIds.join(','), null);
|
||||||
|
}
|
||||||
|
return; // no menu navigation
|
||||||
|
}
|
||||||
|
|
||||||
|
// navigation
|
||||||
let vanityList;
|
let vanityList;
|
||||||
let seqIdList = [];
|
let seqIdList = [];
|
||||||
switch (scope) {
|
switch (scope) {
|
||||||
|
|||||||
Reference in new issue
Block a user