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

artist: getAlbums and getAvailableAlbums #35

Open
wants to merge 2 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
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"sources": [
"src/album.cc",
"src/artist.cc",
"src/artistbrowse.cc",
"src/audio.cc",
"src/binding.cc",
"src/link.cc",
Expand Down
35 changes: 35 additions & 0 deletions lib/Artist.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,40 @@ 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));
}
});
};

module.exports = Artist;
20 changes: 20 additions & 0 deletions src/album.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ static Handle<Value> Album_Is_Loaded(const Arguments& args) {
return scope.Close(Boolean::New(loaded));
}

/**
* JS album_is_available implementation. checks if a given album is available
*/
static Handle<Value> Album_Is_Available(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 1);
assert(args[0]->IsObject());

// gets sp_album pointer from given object
ObjectHandle<sp_album>* album = ObjectHandle<sp_album>::Unwrap(args[0]);

// actually call sp_album_is_available
bool available = sp_album_is_available(album->pointer);

return scope.Close(Boolean::New(available));
}

/**
* JS album_name implementation. checks if a given album is loaded
*/
Expand Down Expand Up @@ -196,6 +215,7 @@ static Handle<Value> Album_Cover(const Arguments& args) {

void nsp::init_album(Handle<Object> target) {
NODE_SET_METHOD(target, "album_is_loaded", Album_Is_Loaded);
NODE_SET_METHOD(target, "album_is_available", Album_Is_Available);
NODE_SET_METHOD(target, "album_name", Album_Name);
NODE_SET_METHOD(target, "album_year", Album_Year);
NODE_SET_METHOD(target, "album_type", Album_Type);
Expand Down
102 changes: 102 additions & 0 deletions src/artistbrowse.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* =====================================================================================
*
* Filename: artistbrowse.cc
*
* Description: bindings for the artist subsystem
*
* Version: 1.0
* Revision: none
* Compiler: gcc
*
* Author: Linus Unnebäck, [email protected]
* Company: LinusU AB
*
* =====================================================================================
*/


#include "common.h"

using namespace v8;
using namespace nsp;

void cb_artistbrowse_complete (sp_artistbrowse *result, void *userdata) {
Persistent<Function> callback = static_cast<Function*>(userdata);

callback->Call(callback, 0, NULL);
callback.Dispose();
}

static Handle<Value> ArtistBrowse_Create(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 3);
assert(args[0]->IsObject()); // sp_session
assert(args[1]->IsObject()); // sp_artist
assert(args[2]->IsFunction()); // callback

ObjectHandle<sp_session> *session = ObjectHandle<sp_session>::Unwrap(args[0]);
ObjectHandle<sp_artist> *artist = ObjectHandle<sp_artist>::Unwrap(args[1]);
Handle<Function> callback = Persistent<Function>::New(Handle<Function>::Cast(args[2]));

ObjectHandle<sp_artistbrowse>* artistbrowse = new ObjectHandle<sp_artistbrowse>("sp_artistbrowse");
artistbrowse->pointer = sp_artistbrowse_create(session->pointer, artist->pointer, SP_ARTISTBROWSE_NO_TRACKS, cb_artistbrowse_complete, *callback);

return scope.Close(artistbrowse->object);
}

static Handle<Value> ArtistBrowse_Num_Albums(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 1);
assert(args[0]->IsObject()); // sp_artistbrowse

ObjectHandle<sp_artistbrowse> *artistbrowse = ObjectHandle<sp_artistbrowse>::Unwrap(args[0]);
const int num = sp_artistbrowse_num_albums(artistbrowse->pointer);

return scope.Close(Number::New(num));
}

static Handle<Value> ArtistBrowse_Album(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 2);
assert(args[0]->IsObject()); // sp_artistbrowse
assert(args[1]->IsNumber()); // index

// input
ObjectHandle<sp_artistbrowse> *artistbrowse = ObjectHandle<sp_artistbrowse>::Unwrap(args[0]);
int index = args[1]->ToNumber()->Int32Value();

// output
sp_album* spalbum = sp_artistbrowse_album(artistbrowse->pointer, index);
ObjectHandle<sp_album>* album = new ObjectHandle<sp_album>("sp_album");
album->pointer = spalbum;

return scope.Close(album->object);
}

static Handle<Value> ArtistBrowse_Release(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 1);
assert(args[0]->IsObject()); // sp_artistbrowse

ObjectHandle<sp_artistbrowse> *artistbrowse = ObjectHandle<sp_artistbrowse>::Unwrap(args[0]);
sp_error error = sp_artistbrowse_release(artistbrowse->pointer);
NSP_THROW_IF_ERROR(error);

return scope.Close(Undefined());
}

void nsp::init_artistbrowse(Handle<Object> target) {
NODE_SET_METHOD(target, "artistbrowse_create", ArtistBrowse_Create);
NODE_SET_METHOD(target, "artistbrowse_num_albums", ArtistBrowse_Num_Albums);
NODE_SET_METHOD(target, "artistbrowse_album", ArtistBrowse_Album);
NODE_SET_METHOD(target, "artistbrowse_release", ArtistBrowse_Release);
}
1 change: 1 addition & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern "C" {
// initializing all modules
nsp::init_album(target);
nsp::init_artist(target);
nsp::init_artistbrowse(target);
nsp::init_link(target);
nsp::init_player(target);
nsp::init_search(target);
Expand Down
7 changes: 4 additions & 3 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ namespace nsp {
* init the artist related functions to the target module exports
*/
void init_artist(v8::Handle<v8::Object> target);
void init_artistbrowse(v8::Handle<v8::Object> target);
/**
* init the link related functions to the target module exports
*/
Expand All @@ -156,7 +157,7 @@ namespace nsp {
* init the playlist related functions to the target module exports
*/
void init_playlist(v8::Handle<v8::Object> target);

/**
* This utility class allows to keep track of a C pointer that we attached
* to a JS object. It differs from node's ObjectWrap in the fact that it
Expand Down Expand Up @@ -191,7 +192,7 @@ namespace nsp {
* We do create this one
*/
v8::Persistent<v8::Object> object;

/**
* Get the name of the ObjectHandle that we gave it during instanciation
*/
Expand Down Expand Up @@ -229,7 +230,7 @@ namespace nsp {

object->SetPointerInInternalField(0, this);
}

template <typename T>
ObjectHandle<T>* ObjectHandle<T>::Unwrap(v8::Handle<v8::Value> obj) {
assert(obj->IsObject());
Expand Down
45 changes: 43 additions & 2 deletions src/link.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Filename: link.cc
*
* Description: bindings for links subsystem
* Description: bindings for links subsystem
*
* Version: 1.0
* Created: 07/01/2013 12:37:03
Expand Down Expand Up @@ -42,6 +42,24 @@ static Handle<Value> Link_Create_From_Track(const Arguments& args) {
return scope.Close(String::New(url));
}

static Handle<Value> Link_Create_From_Album(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 1);
assert(args[0]->IsObject());

// gets sp_album pointer from given object
ObjectHandle<sp_album>* album = ObjectHandle<sp_album>::Unwrap(args[0]);

sp_link* link = sp_link_create_from_album(album->pointer);
char url[256];
// TODO handle truncated urls
sp_link_as_string(link, url, 256);

return scope.Close(String::New(url));
}

static Handle<Value> Link_Create_From_Artist(const Arguments& args) {
HandleScope scope;

Expand Down Expand Up @@ -96,6 +114,24 @@ static Handle<Value> Link_As_Track(const Arguments& args) {
return scope.Close(track->object);
}

static Handle<Value> Link_As_Album(const Arguments& args) {
HandleScope scope;

// test arguments sanity
assert(args.Length() == 1);
assert(args[0]->IsString());

String::Utf8Value url(args[0]);

sp_link* link = sp_link_create_from_string(*url);
assert(sp_link_type(link) == SP_LINKTYPE_ALBUM);

ObjectHandle<sp_album>* album = new ObjectHandle<sp_album>("sp_album");
album->pointer = sp_link_as_album(link);

return scope.Close(album->object);
}

static Handle<Value> Link_As_Artist(const Arguments& args) {
HandleScope scope;

Expand Down Expand Up @@ -132,7 +168,7 @@ static Handle<Value> Link_As_Playlist(const Arguments& args) {

ObjectHandle<sp_playlist>* playlist = new ObjectHandle<sp_playlist>("sp_playlist");
playlist->pointer = sp_playlist_create(session->pointer, link);

// Add callbacks
sp_error error = sp_playlist_add_callbacks(playlist->pointer, &nsp_playlist_callbacks, playlist);
NSP_THROW_IF_ERROR(error);
Expand Down Expand Up @@ -162,6 +198,9 @@ static Handle<Value> Link_Type(const Arguments& args) {
case SP_LINKTYPE_ARTIST:
type = "artist";
break;
case SP_LINKTYPE_ALBUM:
type = "album";
break;
case SP_LINKTYPE_TRACK:
type = "track";
break;
Expand All @@ -177,9 +216,11 @@ static Handle<Value> Link_Type(const Arguments& args) {

void nsp::init_link(Handle<Object> target) {
NODE_SET_METHOD(target, "link_create_from_track", Link_Create_From_Track);
NODE_SET_METHOD(target, "link_create_from_album", Link_Create_From_Album);
NODE_SET_METHOD(target, "link_create_from_artist", Link_Create_From_Artist);
NODE_SET_METHOD(target, "link_create_from_playlist", Link_Create_From_Playlist);
NODE_SET_METHOD(target, "link_as_track", Link_As_Track);
NODE_SET_METHOD(target, "link_as_album", Link_As_Album);
NODE_SET_METHOD(target, "link_as_artist", Link_As_Artist);
NODE_SET_METHOD(target, "link_as_playlist", Link_As_Playlist);
NODE_SET_METHOD(target, "link_type", Link_Type);
Expand Down
39 changes: 39 additions & 0 deletions test/test-034-artistbrowse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var sp = require('../lib/libspotify');
var testutil = require('./util');

var getArtist = function(test, cb) {
var search = new sp.Search('artist:"Hurts"');
search.trackCount = 1;
search.execute(function() {
test.ok(search.tracks.length > 0, 'the track was found');
test.ok(search.tracks[0] instanceof sp.Track, 'track is an track');
test.ok(search.tracks[0].album instanceof sp.Album, 'album is an album');
test.ok(search.tracks[0].album.artist instanceof sp.Artist, 'artist is an artist');
cb(search.tracks[0].album.artist);
});
};

var session = null;

exports.artistbrowse = {
setUp: function(cb) {
testutil.getDefaultTestSession(function(s) {
session = s;
cb();
});
},
'get albums from artist': function(test) {
getArtist(test, function(artist) {
artist.getAvailableAlbums(function(err, albums) {
test.ifError(err);
/* FIXME: Should be 22, see comment in lib/Artist.js line 50 */
test.equal(albums.length, 23, 'the artist has 23 available albums');
test.equal(albums.map(function(e) {return e instanceof sp.Album;}).indexOf(false), -1, 'It should only contain albums');
test.equal(albums.reduce(function(prev, current) {
return prev && current.isReady();
}, true), true, 'All albums should be loaded');
test.done();
});
});
}
}
2 changes: 2 additions & 0 deletions test/test-065-link-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ exports.links = {
}, "Getting link type from anything else than string should throw");

var track_link = 'spotify:track:4BdSLkzKO6iMVCgw7A7JBl';
var album_link = 'spotify:album:2UGJa9DjYhXpBDKsCTyhSh';
var artist_link = 'spotify:artist:3zD5liDjbqljSRorrrcEjs';
var playlist_link = 'spotify:user:flobyiv:playlist:5ZMnMnJWGXZ9qm4gacHpQF';
test.doesNotThrow(function() {
test.equal('track', sp.getLinkType(track_link), "Link type should be 'track'");
test.equal('album', sp.getLinkType(album_link), "Link type should be 'album'");
test.equal('artist', sp.getLinkType(artist_link), "Link type should be 'artist'");
test.equal('playlist', sp.getLinkType(playlist_link), "Link type should be 'playlist'");
}, "Getting link types should not throw");
Expand Down
Loading