Skip to content
This repository has been archived by the owner on Jan 23, 2024. It is now read-only.

Commit

Permalink
fix remote track ordering [closes #9]
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-weber committed Jan 17, 2016
1 parent f566e91 commit 9dab317
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 5 deletions.
27 changes: 22 additions & 5 deletions src/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ function syncPlaylist(playlist, attempt) {
console.warn('attempting to sync over 1000 tracks; only first 1k will sync');
}

Gm.setPlaylistContents(db, userIndex, playlist.remoteId, tracks.slice(0, 1000), response => {
const desiredTracks = tracks.slice(0, 1000);

Gm.setPlaylistContents(db, userIndex, playlist.remoteId, desiredTracks, response => {
if (response !== null) {
// large updates seem to only apply partway sometimes.
// retrying like this seems to make even 1k playlists eventually consistent.
Expand All @@ -138,12 +140,27 @@ function syncPlaylist(playlist, attempt) {
setTimeout(syncPlaylist, 1000 * _attempt + 1000, playlist, _attempt + 1);
} else {
console.warn('giving up on syncPlaylist!', response);
console.log('unlock', playlist.title);
playlistIsUpdating[playlist.remoteId] = false;
// Never has the need for promises been so clear.
Gm.setPlaylistOrder(db, userIndex, playlist.remoteId, desiredTracks, orderResponse => {
console.log('reorder response', orderResponse);
console.log('unlock', playlist.title);
playlistIsUpdating[playlist.remoteId] = false;
}, err => {
console.error('failed to reorder playlist', playlist.title, err);
console.log('unlock', playlist.title);
playlistIsUpdating[playlist.remoteId] = false;
});
}
} else {
console.log('unlock', playlist.title);
playlistIsUpdating[playlist.remoteId] = false;
Gm.setPlaylistOrder(db, userIndex, playlist.remoteId, desiredTracks, orderResponse => {
console.log('reorder response', orderResponse);
console.log('unlock', playlist.title);
playlistIsUpdating[playlist.remoteId] = false;
}, err => {
console.error('failed to reorder playlist', playlist.title, err);
console.log('unlock', playlist.title);
playlistIsUpdating[playlist.remoteId] = false;
});
}
}, err => {
console.error('failed to sync playlist', playlist.title, err);
Expand Down
56 changes: 56 additions & 0 deletions src/js/googlemusic.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,59 @@ exports.setPlaylistContents = function setPlaylistContents(db, userIndex, playli
}
}, onError);
};

exports.setPlaylistOrder = function setPlaylistOrder(db, userIndex, playlistId, tracks, callback, onError) {
// Set the remote ordering of a playlist to match tracks.
// It's assumed that the remote content already matches the tracks.

loadPlaylistContents(db, userIndex, playlistId, contents => {
if (contents.length !== tracks.length) {
// This will always trigger for the don't-delete-the-currently-playing-track case.
console.warn('remote playlist does not contain expected number of tracks:', contents.length, tracks.length);
}

if (contents.length !== 0) {
// Locally we deal in track ids, but remote playlists deal in entry ids.
// So, we need to match our ids to the entry ids, then make the call with those.
const currentOrdering = [];
const desiredOrdering = [];
const idToEntryId = {};

for (let i = 0; i < contents.length; i++) {
idToEntryId[contents[i].track.id] = contents[i].entryId;
currentOrdering.push(contents[i].entryId);
}

for (let i = 0; i < tracks.length; i++) {
const track = tracks[i];
if (track.id in idToEntryId) {
desiredOrdering.push(idToEntryId[track.id]);
} else {
// This isn't the only case, but it's the one we can check without building another mapping.
console.warn('remote playlist is missing', track);
}
}

// It's ridiculous that javascript doesn't have a builtin for this.
// Thankfully we have simple items and can get away with this hack.
if (JSON.stringify(currentOrdering) !== JSON.stringify(desiredOrdering)) {
// The two empty strings are sentinels for "first track" and "last track".
// This lets us send our entire reordering at once without calculating the relative movements.
// I'm not sure if the interface was intended to be used this way, but it seems to work.
const payload = [['', 1], [desiredOrdering, '', '']];
authedGMRequest('changeplaylisttrackorder', payload, userIndex, 'post', response => {
// TODO These should all be checked for errors.
// It looks like responses will have [[0, 1, 1], [call-specific response]] on success.
callback(response);
}, onError);
} else {
// Avoid triggering a ui refresh on noop reorderings.
console.log('no need to reorder playlist', playlistId);
callback(null);
}
} else {
console.log('no need to reorder empty playlist', playlistId);
callback(null);
}
}, onError);
};

0 comments on commit 9dab317

Please sign in to comment.