From 991b2f0a8280c31b93ad91d6a215b74183417352 Mon Sep 17 00:00:00 2001 From: Florrie Date: Sat, 11 Jul 2020 16:22:01 -0300 Subject: support queue controls over socket clients --- playlist-utils.js | 133 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 60 deletions(-) (limited to 'playlist-utils.js') diff --git a/playlist-utils.js b/playlist-utils.js index 5279177..b813181 100644 --- a/playlist-utils.js +++ b/playlist-utils.js @@ -166,15 +166,30 @@ function flattenGrouplike(grouplike) { // levels in the group tree and returns them as a new group containing those // tracks. - return { - items: grouplike.items.map(item => { - if (isGroup(item)) { - return flattenGrouplike(item).items - } else { - return [item] - } - }).reduce((a, b) => a.concat(b), []) - } + return {items: getFlatTrackList(grouplike)} +} + +function getFlatTrackList(grouplike) { + // Underlying function for flattenGrouplike. Can be used if you just want to + // get an array and not a grouplike, too. + + return grouplike.items.map(item => { + if (isGroup(item)) { + return getFlatTrackList(item) + } else { + return [item] + } + }).reduce((a, b) => a.concat(b), []) +} + +function getFlatGroupList(grouplike) { + // Analogue of getFlatTrackList for groups instead of tracks. Returns a flat + // array of all the groups in each level of the provided grouplike. + + return grouplike.items + .filter(isGroup) + .map(item => [item, ...getFlatGroupList(item)]) + .reduce((a, b) => a.concat(b), []) } function countTotalTracks(item) { @@ -782,20 +797,17 @@ function getPathScore(path1, path2) { return scores.reduce((a, b) => a < b ? a : b) } -function findTrackObject(referenceData, sourcePlaylist, flattenedSourcePlaylist = null) { - // Finds the track object in the source playlist which most closely resembles +function findItemObject(referenceData, possibleChoices) { + // Finds the item object in the provided choices which most closely resembles // the provided reference data. This is used for maintaining the identity of - // track objects when reloading a playlist (see serialized-backend.js). It's - // also usable in synchronizing the identity of tracks across linked clients + // item objects when reloading a playlist (see serialized-backend.js). It's + // also usable in synchronizing the identity of items across linked clients // (see socket.js). - // - // NB: This function is many times more efficient if you pass a preemptively - // flattened version of the source playlist in as well! - // Reference data includes track NAME, track SOURCE (downloaderArg), and - // track PATH (names of parent groups). Specifics of how existing track - // objects are determined to resemble this data are laid out next to the - // relevant implementation code. + // Reference data includes item NAME, item SOURCE (downloaderArg), and item + // PATH (names of parent groups). Specifics of how existing item objects are + // determined to resemble this data are laid out next to the relevant + // implementation code. // // TODO: Should track number be considered here? // TODO: Should track "metadata" (duration, md5?) be considered too? @@ -803,41 +815,37 @@ function findTrackObject(referenceData, sourcePlaylist, flattenedSourcePlaylist // tracks *is*, and in considering those I lean towards "no" here, but // it's probably worth looking at more in the future. (TM.) - function getTrackPathScore(track) { + function getItemPathScore(item) { if (!referenceData.path) { return null } const path1 = referenceData.path.slice() - const path2 = getItemPath(track).slice(0, -1).map(group => group.name) + const path2 = getItemPath(item).slice(0, -1).map(group => group.name) return getPathScore(path1, path2) } - if (!flattenedSourcePlaylist) { - flattenedSourcePlaylist = flattenGrouplike(sourcePlaylist) - } - - // The only tracks which will be considered at all are those which match at + // The only items which will be considered at all are those which match at // least one of the reference name/source. - const baselineResemble = flattenedSourcePlaylist.items.filter(track => - track.name === referenceData.name || - track.downloaderArg === referenceData.downloaderArg) + const baselineResemble = possibleChoices.filter(item => + item.name === referenceData.name || + item.downloaderArg && item.downloaderArg === referenceData.downloaderArg) - // If no track matches the baseline conditions for resemblance at all, + // If no item matches the baseline conditions for resemblance at all, // return null. It's up to the caller to decide what to do in this case, - // e.g. reporting that no track was found, or creating a new track object + // e.g. reporting that no item was found, or creating a new item object // from the reference data altogether. if (!baselineResemble.length) { return null } - // Find the "reasons" these tracks resemble the reference data; these will - // be used as the factors in calculating which track resembles closest. - const reasons = baselineResemble.map(track => ({ - track, - nameMatches: track.name === referenceData.name, - sourceMatches: track.downloaderArg === referenceData.downloaderArg, - pathScore: getTrackPathScore(track) + // Find the "reasons" these items resemble the reference data; these will + // be used as the factors in calculating which item resembles closest. + const reasons = baselineResemble.map(item => ({ + item, + nameMatches: item.name === referenceData.name, + sourceMatches: item.downloaderArg && item.downloaderArg === referenceData.downloaderArg, + pathScore: getItemPathScore(item) })) // TODO: The algorithm for determining which track matches closest is @@ -868,7 +876,7 @@ function findTrackObject(referenceData, sourcePlaylist, flattenedSourcePlaylist mostResembles = reasons[0] } - return mostResembles.track + return mostResembles.item } module.exports = { @@ -876,7 +884,10 @@ module.exports = { updatePlaylistFormat, updateGroupFormat, updateTrackFormat, cloneGrouplike, filterTracks, - flattenGrouplike, countTotalTracks, + flattenGrouplike, + getFlatTrackList, + getFlatGroupList, + countTotalTracks, shuffleOrderOfGroups, reverseOrderOfGroups, partiallyFlattenGrouplike, collapseGrouplike, @@ -892,7 +903,7 @@ module.exports = { getCorrespondingFileForItem, getCorrespondingPlayableForFile, getPathScore, - findTrackObject, + findItemObject, isGroup, isTrack, isOpenable, isPlayable } @@ -902,25 +913,27 @@ if (require.main === module) { console.log(getPathScore(['A', 'B', 'C'], ['A', 'B', 'C', 'D'])) console.log(getPathScore(['A', 'B', 'C', 'E'], ['A', 'B', 'C'])) console.log(getPathScore(['W', 'X'], ['Y', 'Z'])) - console.log(findTrackObject( - {name: 'T', downloaderArg: 'foo', path: ['A', 'B', 'C']}, - updateGroupFormat({items: [ - {id: 1, name: 'T'}, - {id: 2, name: 'T'}, - {id: 3, name: 'T'}, - // {id: 4, name: 'T', downloaderArg: 'foo'}, - {id: 5, name: 'T'}, - {id: 6, name: 'Y', downloaderArg: 'foo'}, - /* - {name: 'A', items: [ - {name: 'B', items: [ - {name: 'C', items: [ + console.log(findItemObject( + // {name: 'T', downloaderArg: 'foo', path: ['A', 'B', 'C']}, + {name: 'B'}, + // getFlatTrackList( + getFlatGroupList( + updateGroupFormat({items: [ + {id: 1, name: 'T'}, + {id: 2, name: 'T'}, + {id: 3, name: 'T'}, + // {id: 4, name: 'T', downloaderArg: 'foo'}, + {id: 5, name: 'T'}, + {id: 6, name: 'Y', downloaderArg: 'foo'}, + {name: 'A', items: [ + {name: 'B', items: [ + {name: 'C', items: [ + {name: 'T'} + ]}, {name: 'T'} - ]}, - {name: 'T'} + ]} ]} - ]} - */ - ]}) + ]}) + ) )) } -- cgit 1.3.0-6-gf8a5