Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Images and more link types supported #38

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
"target_name": "libspotify",
"sources": [
"src/album.cc",
"src/albumbrowse.cc",
"src/artist.cc",
"src/artistbrowse.cc",
"src/audio.cc",
"src/binding.cc",
"src/image.cc",
"src/link.cc",
"src/player.cc",
"src/search.cc",
"src/session.cc",
"src/track.cc",
"src/playlist.cc"
"src/playlist.cc"
],
"cflags": ["-Wall"],
"conditions" : [
Expand Down
65 changes: 59 additions & 6 deletions lib/Album.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Album.prototype.toString = function toString() {
* libspotify finished, onImageLoaded callback gets executed.
*/
Album.prototype.coverImage = function coverImage(imageSize, cb) {
var sp_image, image;
var deprecated = function () {
console.log('`cb` parameter to `coverImage` is deprecated, please use the `Image` class instead');
};

if (typeof(imageSize) == 'function') {
cb = imageSize;
Expand All @@ -66,21 +70,70 @@ Album.prototype.coverImage = function coverImage(imageSize, cb) {
}
}

var wrap = function (buffer) {
if (typeof(imageSize) == 'undefined') {
imageSize = this.IMAGE_SIZE_NORMAL;
}

var wrap = function () {
var buffer = image.getData();
if (buffer.length == 0) {
cb(new Error('Cover image is empty'));
} else {
cb(null, buffer);
}
};

if (b.album_cover(this.getSession()._sp_session, this._sp_object, imageSize, wrap) === false) {
process.nextTick(function () { cb(new Error('Album has no cover image')); });
sp_image = b.album_cover(this.getSession()._sp_session, this._sp_object, imageSize);

if(sp_image === null) {
if(typeof(cb) == 'function') {
deprecated();
process.nextTick(function () { cb(new Error('Album has no cover image')); });
}
return null;
} else {
image = new sp.Image(sp_image);
if(typeof(cb) == 'function') {
deprecated();
image.whenReady(wrap);
}
return image;
}
};

Album.prototype.smallCoverImage = function (cb) { this.coverImage(this.IMAGE_SIZE_SMALL, cb); }
Album.prototype.normalCoverImage = function (cb) { this.coverImage(this.IMAGE_SIZE_NORMAL, cb); }
Album.prototype.largeCoverImage = function (cb) { this.coverImage(this.IMAGE_SIZE_LARGE, cb); }
Album.prototype.smallCoverImage = function (cb) { return this.coverImage(this.IMAGE_SIZE_SMALL, cb); }
Album.prototype.normalCoverImage = function (cb) { return this.coverImage(this.IMAGE_SIZE_NORMAL, cb); }
Album.prototype.largeCoverImage = function (cb) { return this.coverImage(this.IMAGE_SIZE_LARGE, cb); }

Album.prototype.coverImageUrl = function coverImageUrl(imageSize) {

if (typeof(imageSize) == 'string') {
switch(imageSize) {
case 'small': imageSize = this.IMAGE_SIZE_SMALL; break;
case 'normal': imageSize = this.IMAGE_SIZE_NORMAL; break;
case 'large': imageSize = this.IMAGE_SIZE_LARGE; break;
default: throw new Error('Unknown image size');
}
}

if (typeof(imageSize) == 'undefined') {
imageSize = this.IMAGE_SIZE_NORMAL;
}

return b.link_create_from_album_cover(this._sp_object, imageSize);
}

Album.prototype.getTracks = function (cb) {
var browser = b.albumbrowse_create(this.getSession()._sp_session, this._sp_object, function () {
tracks = new Array(b.albumbrowse_num_tracks(browser));

for(var i = 0; i<tracks.length; i++) {
tracks[i] = new sp.Track(b.albumbrowse_track(browser, i));
}

b.albumbrowse_release(browser);
cb(null, tracks);
});
}

module.exports = Album;
58 changes: 56 additions & 2 deletions lib/Artist.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ function artist_object_type() { return 'artist'; };
Artist.__defineGetter__('_object_type', artist_object_type);
Artist.prototype.__defineGetter__('_object_type', artist_object_type);


Artist.prototype._object_type = 'artist';
Artist.prototype.IMAGE_SIZE_NORMAL = b.SP_IMAGE_SIZE_NORMAL;
Artist.prototype.IMAGE_SIZE_SMALL = b.SP_IMAGE_SIZE_SMALL;
Artist.prototype.IMAGE_SIZE_LARGE = b.SP_IMAGE_SIZE_LARGE;

Artist.prototype._populateAttributes = function _populateAttributes() {
this.name = b.artist_name(this._sp_object);
Expand All @@ -27,5 +28,58 @@ Artist.prototype.toString = function toString() {
return this.name;
};

Artist.prototype.getAlbums = function getAlbums(cb) {
var browser = b.artistbrowse_create(this.getSession()._sp_session, this._sp_object, function () {
albums = new Array(b.artistbrowse_num_albums(browser));

for(var i = 0; i<albums.length; i++) {
albums[i] = new sp.Album(b.artistbrowse_album(browser, i));
}

b.artistbrowse_release(browser);
cb(null, albums);
});
};

Artist.prototype.getAvailableAlbums = function getAvailableAlbums(cb) {
this.getAlbums(function (err, albums) {
if(err) {
cb(err);
} else {
var set = {};
var filter = function (e) {
/**
FIXME
When the album has multiple artists, loop all songs
and check if it belongs to current artist and is
available, if none the filter should return false.
**/
if(!e.isAvailable()) { return false; }
if(set[e.name]) { return false; }
return (set[e.name] = true);
};

cb(null, albums.filter(filter));
}
});
};

Artist.prototype.portraitImageUrl = function portraitImageUrl(imageSize) {

if (typeof(imageSize) == 'string') {
switch(imageSize) {
case 'small': imageSize = this.IMAGE_SIZE_SMALL; break;
case 'normal': imageSize = this.IMAGE_SIZE_NORMAL; break;
case 'large': imageSize = this.IMAGE_SIZE_LARGE; break;
default: throw new Error('Unknown image size');
}
}

if (typeof(imageSize) == 'undefined') {
imageSize = this.IMAGE_SIZE_NORMAL;
}

return b.link_create_from_artist_portrait(this._sp_object, imageSize);
}

module.exports = Artist;
34 changes: 34 additions & 0 deletions lib/Image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var b = require('bindings')('spotify.node');
var sp = require('./libspotify');
var util = require('util');
var SpObject = require('./SpObject');

function Image (sp_image) {
this._sp_object = sp_image;
this._setupNativeCallbacks();
SpObject.apply(this);
}
util.inherits(Image, SpObject);
Image.__proto__ = SpObject;

function image_object_type() { return 'image'; };
Image.__defineGetter__('_object_type', image_object_type);
Image.prototype.__defineGetter__('_object_type', image_object_type);

Image.prototype.getData = function getData() {
this._readyOrThrow();
return b.image_data(this._sp_object);
};

/**
* C callbacks for spotify events do nothing more than calling their JS equivalent
* here we setup the functions that must be called from C upon these events
*/
Image.prototype._setupNativeCallbacks = function _setupNativeCallbacks() {
var self = this;
this._sp_object.image_loaded = function(data) {
self.emit('ready', data);
};
};

module.exports = Image;
14 changes: 7 additions & 7 deletions lib/Player.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ function Player (session) {
assert(session instanceof sp.Session, "parameter is not a session");
this._session = session;

var self = this;
var self = this;

// Tell spotify that we're ready for more data
this._read = function() {
b.session_player_stream_resume();
};
// Tell spotify that we're ready for more data
this._read = function() {
b.session_player_stream_resume();
};

this._session._sp_session.music_delivery = function(buffer) {
// Readable.push returns whether or not we should push more data,
// Readable.push returns whether or not we should push more data,
// so we're returning that value so the underlying code knows what to do
return self.push(buffer);
};

this._session._sp_session.end_of_track = function() {
self.emit('track-end');
self.emit('track-end');
};

stream.Readable.call(this);
Expand Down
60 changes: 32 additions & 28 deletions lib/Playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ function playlist_object_type() { return 'playlist'; };
Playlist.__defineGetter__('_object_type', playlist_object_type);
Playlist.prototype.__defineGetter__('_object_type', playlist_object_type);


Playlist.getFromUrl = function getFromUrl(url) {
if(sp.getLinkType(url) !== 'playlist') {
throw new URIError("Not a playlist URI");
}
return new this(b.link_as_playlist(url, sp.Session.currentSession._sp_session));
};

Playlist.prototype._populateAttributes = function _populateAttributes() {
this.name = b.playlist_name(this._sp_object);
this.numSubscribers = b.playlist_num_subscribers(this._sp_object);
Expand Down Expand Up @@ -70,7 +62,22 @@ Playlist.prototype.getTracks = function getTracks(callback) {

var checkTracksLoaded = function(numReady, array, callback) {
if (numReady >= array.length) {
callback(array);
callback(array);
}
};

/**
* Get the image for the playlist
*/
Playlist.prototype.getImage = function getImage() {
this._readyOrThrow();

var sp_image = b.playlist_get_image(this.getSession()._sp_session, this._sp_object);

if(sp_image === null) {
return null;
} else {
return new sp.Image(sp_image);
}
};

Expand All @@ -82,32 +89,29 @@ Playlist.prototype._setupNativeCallbacks = function _setupNativeCallbacks() {
var self = this;

var checkReady = function() {
if (self.isReady() && !self.__readyEventFired === true) {
self.__readyEventFired = true;
self._populateAttributes();
self.emit('ready');
// We want the number of subscribers for the
// playlist so tell spotify to get it for us (this
// is an async call so is dealt with in the
// callbacks)
b.playlist_update_subscribers(sp.Session.currentSession._sp_session, self._sp_object);
}
if (self.isReady() && !self.__readyEventFired === true) {
self.__readyEventFired = true;
self._populateAttributes();
self.emit('ready');
// We want the number of subscribers for the
// playlist so tell spotify to get it for us (this
// is an async call so is dealt with in the
// callbacks)
b.playlist_update_subscribers(sp.Session.currentSession._sp_session, self._sp_object);
}
};

this._sp_object.state_changed = function() {
// Check the possible reasons that state changed has been triggered

// 1. Collaboration turned on / off

// 2. Pending Changes started / complete

// 3. Playlist started loading / finished loading
checkReady();
// Check the possible reasons that state changed has been triggered
// 1. Collaboration turned on / off
// 2. Pending Changes started / complete
// 3. Playlist started loading / finished loading
checkReady();
};

var t = 0;
this._sp_object.subscribers_changed = function() {
self.numSubscribers = b.playlist_num_subscribers(self._sp_object);
self.numSubscribers = b.playlist_num_subscribers(self._sp_object);
};

checkReady();
Expand Down
Loading