Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Make playlist diffing track oriented, don't attempt to sync metadaata…

…. Add UTIL.sortByProperty/Method helper methods. Add a PlaylistTrack.toString. Introduce concept of incremental subscriptions, that only add tracks versus those that just replace the track list wholesale. Updating podcasts/XSPFs versus syncing Last.fm playlists. Make fetchTracks only operate if the Playlist has no tracks loaded yet.
  • Loading branch information...
commit 2908e7d180ea1bd24f7125838a1f3cd08d1fee11 1 parent ae3ea05
James Wheare authored
19 src/js/controllers/playlist.controller.js
View
@@ -484,17 +484,24 @@ Playlist.prototype = {
// Add callback and exception handler to the arguments
args.push(function callback (newPlaylist) {
// compare with saved playlist and update/warn for conflicts?
- var diffs = 0;
- for (var field in playlist.diff(newPlaylist)) {
- diffs++;
- console.info(playlist[field], newPlaylist[field]);
+ var diff = playlist.diffTracks(newPlaylist);
+ var added = [];
+ for (var field in diff) {
+ if (diff[field].change === true) {
+ added.push(diff[field].track);
+ }
}
- if (!diffs) {
- console.info('no updates');
+ if (added.length) {
+ console.log(playlist.toString(), playlist.isIncrementalSubscription());
+ UTIL.sortByMethod(added, 'get_position');
+ $.each(added, function (i, playlist_track) {
+ console.info(playlist_track.toString());
+ });
}
});
args.push(function exceptionHandler (exception) {
// show a warning icon and message?
+ console.warn(exception);
exception.diagnose();
});
IMPORTERS[sub.namespace][sub.method].apply(this, args);
6 src/js/importers/lastfm.js
View
@@ -99,7 +99,8 @@ LastFm.getUserPlaylist = function (data, callback, exceptionHandler) {
subscription: {
namespace: 'LastFm',
method: 'getUserPlaylist',
- arguments: [data]
+ arguments: [data],
+ incremental: false
}
};
var image = $.grep(data.image, function (value, i) {
@@ -175,7 +176,8 @@ LastFm.lovedTracks = function (user, callback, exceptionHandler) {
subscription: {
namespace: 'LastFm',
method: 'lovedTracks',
- arguments: [user]
+ arguments: [user],
+ incremental: false
}
});
// Load tracks
3  src/js/importers/url.js
View
@@ -67,7 +67,8 @@ Url.url = function (source, callback, exceptionHandler) {
subscription: {
namespace: 'Url',
method: 'url',
- arguments: [source]
+ arguments: [source],
+ incremental: true
}
};
if (jspf) {
10 src/js/main/playdar.js
View
@@ -55,15 +55,7 @@ var PLAYDAR = {
var resultsTable = $('<table cellspacing="0"></table>');
var foundPerfect = false;
- response.results.sort(function (a, b) {
- if (a.score > b.score) {
- return -1;
- }
- if (a.score < b.score) {
- return 1;
- }
- return 0;
- });
+ UTIL.sortByProperty(response.results, 'score', true);
$.each(response.results, function (i, result) {
// Register sound
Playdar.player.register_stream(result, {
117 src/js/models/playlist.model.js
View
@@ -85,6 +85,9 @@ Playlist.prototype = {
isSubscription: function () {
return this.type == 'subscription' && this.subscription;
},
+ isIncrementalSubscription: function () {
+ return this.isSubscription() && this.subscription.incremental === true;
+ },
isEditable: function () {
return !this.isAlbum() && !this.isSubscription();
},
@@ -104,25 +107,36 @@ Playlist.prototype = {
* Fetch tracks from Couch
**/
fetchTracks: function () {
- try {
- var response = MODELS.couch.view("playlist/all", {
- "key": this._id
- });
- MODELS.couch_up_handler('fetchTracks', response);
- var row = response.rows[0];
- var value = row.value;
- // Load tracks
- var playlist = this;
- var elements = $.map(value.tracks, function (track_data, i) {
- var playlist_track = playlist.add_track(new MODELS.Track(track_data.track));
- // Build DOM element
- return playlist_track.element.get();
- });
- return elements;
- } catch (result) {
- MODELS.couch_down_handler('fetchTracks', result);
+ if (!this.tracks.length) {
+ try {
+ var response = MODELS.couch.view("playlist/all", {
+ "key": this._id
+ });
+ MODELS.couch_up_handler('fetchTracks', response);
+ var row = response.rows[0];
+ var value = row.value;
+ // Load tracks
+ var playlist = this;
+ var elements = $.map(value.tracks, function (track_data, i) {
+ var playlist_track = playlist.add_track(new MODELS.Track(track_data.track));
+ // Build DOM element
+ return playlist_track.element.get();
+ });
+ return elements;
+ } catch (result) {
+ MODELS.couch_down_handler('fetchTracks', result);
+ }
}
},
+ getTrackHash: function () {
+ this.fetchTracks();
+ var hash = {};
+ var track, key;
+ $.each(this.tracks, function (i, playlist_track) {
+ hash[playlist_track.track.getDiffKey()] = playlist_track;
+ });
+ return hash;
+ },
/**
* Load tracks to the DOM, fetching from Couch if needed
**/
@@ -339,42 +353,47 @@ Playlist.prototype = {
return doc;
},
/**
- * Coarse comparison of data with another playlist
- * Returns an object containing differing fields
+ * Coarse comparison of track data with another playlist
+ * Returns an object containing differing tracks
**/
- diff: function (playlist) {
- // Get the data
- var thisDoc = this.get_doc();
- var playlistDoc = playlist.get_doc();
- // Store anything that differs in an object
- var diff = {};
- var field, i, arrayLen;
- for (field in thisDoc) {
- // Only care about single level arrays, if anything differs add the whole array
- if ($.isArray(thisDoc[field])) {
- arrayLen = thisDoc[field].length;
- if (arrayLen !== playlistDoc[field].length) {
- // Lengths differ
- diff[field] = playlistDoc[field];
- } else {
- // Check if any of the elements differ
- for (i = 0; i < arrayLen; i++) {
- if (JSON.stringify(thisDoc[field][i]) !== JSON.stringify(playlistDoc[field][i])) {
- diff[field] = playlistDoc[field];
- break;
- }
- }
+ diffTracks: function (playlist) {
+ var trackDiffs = {};
+ var anyChanges = false;
+ var thisTracks = this.getTrackHash();
+ var playlistTracks = playlist.getTrackHash();
+ var trackKey;
+ for (trackKey in thisTracks) {
+ var change;
+ if (!playlistTracks[trackKey]) {
+ // Added
+ change = false;
+ } else {
+ // Moved
+ change = thisTracks[trackKey].get_position() - playlistTracks[trackKey].get_position();
+ // Don't include non changers
+ if (change === 0) {
+ continue;
}
- } else if (JSON.stringify(thisDoc[field]) !== JSON.stringify(playlistDoc[field])) {
- diff[field] = playlistDoc[field];
}
+ anyChanges = true;
+ trackDiffs[trackKey] = {
+ change: change,
+ track: thisTracks[trackKey].get_doc()
+ };
+ }
+ for (trackKey in playlistTracks) {
+ if (!thisTracks[trackKey]) {
+ // Removed
+ anyChanges = true;
+ trackDiffs[trackKey] = {
+ change: true,
+ track: playlistTracks[trackKey]
+ };
+ }
+ }
+ if (anyChanges) {
+ return trackDiffs;
}
- // Certain keys will always differ
- delete diff._id;
- delete diff._rev;
- delete diff.date;
-
- return diff;
}
};
/**
5 src/js/models/playlisttrack.model.js
View
@@ -53,9 +53,12 @@ PlaylistTrack.prototype = {
},
get_doc: function () {
var doc = {
- position: this.position,
+ position: this.get_position(),
track: this.track.get_doc()
};
return doc;
+ },
+ toString: function () {
+ return this.get_position() + ': ' + this.track.toString();
}
};
9 src/js/models/track.model.js
View
@@ -53,9 +53,14 @@ Track.prototype = {
duration: this.duration,
size: this.size,
mimetype: this.mimetype,
- bitrate: this.bitrate,
- type: this.type
+ bitrate: this.bitrate
};
return doc;
+ },
+ getDiffKey: function () {
+ return this.url || (this.artist + this.album + this.name);
+ },
+ diff: function (track) {
+ return this.getDiffKey() != track.getDiffKey();
}
};
20 src/js/util.js
View
@@ -53,5 +53,25 @@ var UTIL = {
var text = match.replace(/^http:\/\//, '').replace(/\/$/, '');
return '<a href="' + url + '">' + text + '</a>';
});
+ },
+
+ sortBy: function (list, property, callable, descending) {
+ list.sort(function (a, b) {
+ var aSort = callable ? a[property]() : a[property];
+ var bSort = callable ? b[property]() : b[property];
+ if (aSort > bSort) {
+ return descending ? -1 : 1;
+ }
+ if (aSort < bSort) {
+ return descending ? 1 : -1;
+ }
+ return 0;
+ });
+ },
+ sortByProperty: function (list, property, descending) {
+ UTIL.sortBy(list, property, false, descending);
+ },
+ sortByMethod: function (list, method, descending) {
+ UTIL.sortBy(list, method, true, descending);
}
};
Please sign in to comment.
Something went wrong with that request. Please try again.