Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 2 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
Commits on Dec 22, 2012
@Floby Re-use same Javascript object for sessions
Allocating several session pointers from C causes some unstability
(segfaults) so each time we construct a session from javascript, we
actually clean the previous session object, give it a different ID and
reuse that object
a126006
Commits on Dec 23, 2012
@Floby document a lot of stuff. tests are running. life is good c274f27
Showing with 387 additions and 59 deletions.
  1. +95 −15 lib/Session.js
  2. +1 −0 package.json
  3. +81 −1 src/common.h
  4. +178 −33 src/session.cc
  5. +8 −0 test/test-000-a-check.js
  6. +15 −8 test/test-010-session01-instance.js
  7. +9 −2 test/test-010-session02-login.js
View
110 lib/Session.js
@@ -5,11 +5,18 @@ var fs = require('fs');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
+/**
+ * Default settings for the session_config struct
+ * see libspotify documentation for more info on which is what
+ * https://developer.spotify.com/technologies/libspotify/docs/12.1.45/annotated.html
+ */
var defaultSessionConfig = {
cache_location: "/tmp/node-libspotify",
settings_location: "/tmp/node-libspotify",
application_key: new Buffer(0),
user_agent: "node-libspotify",
+ // we could completely remove callbacks here, because
+ // we don't create a callbacks struct from JS at all
callbacks: null,
compress_playlists: false,
dont_save_metadata_for_playlists: true,
@@ -22,88 +29,160 @@ var defaultSessionConfig = {
tracefile: null
};
+/**
+ * a Spotify Session object. Everything in spotify occurs within a given session
+ * currently, the C libspotify library provided by spotify is _really_ buggy when using more
+ * than one session per process (like really really). So in effect, this JS class
+ * makes sure that no C session struct gets created and instead re-uses the same JS object
+ * over and over. Cleaning it each time new is called
+ * @constructor
+ * @param
+ */
function Session (params) {
+ // since we only authorize one session object,
+ // make sure we do
if (Session.currentSession) {
- throw new Error('A spotify session is currently in use. libspotify does not support multiple sessions');
+ // throw if current session is still open
+ if(Session.currentSession.isOpen()) {
+ throw new Error('Only one spotify session can live at once');
+ }
+ Session.currentSession.close();
+ // increment current session id
+ Session.currentSession._id++;
+ // cleaning everything
+ Session.currentSession.removeAllListeners();
+ Session.currentSession._setupNativeCallbacks();
+ Session.currentSession._startProcessingEvents();
+ return Session.currentSession;
}
+ this._id = 0; ///> the id of the current session. this is used to make sure we only have one at a given time
+
params = params || {};
+ // read parameters from the config object, changing them to a C-style name
var cparams = new Object(defaultSessionConfig);
for(var key in params) {
var k = i.underscore(key);
cparams[k] = params[key];
}
+ // try to read application key from the given parameter
var key = null;
+ // if the parameter is a string, then it's a path to the key file
if(typeof cparams.application_key == 'string') {
cparams.application_key = fs.readFileSync(cparams.application_key);
}
+ // if the parameter is not a node buffer, then it's probably not what we want
else if(! (cparams.application_key instanceof Buffer)) {
throw new TypeError('No application key specified for this spotify session');
}
+ // if the parameter is an empty buffer, we've got a problem
else if(cparams.application_key.length == 0) {
throw new TypeError('Empty application key specified for this spotify session');
}
+ // create a config object from these parameters
var session_config = b.session_config(cparams);
- this._sp_session = b.session_create(session_config);
+ // create the underlying sp_session object
+ this._sp_session = b.session_create(session_config);
+ // set this session as new current session
Session.currentSession = this;
+ // setup callbacks from spotify events called from C
this._setupNativeCallbacks();
+
+ // start the loop to process pending events
this._startProcessingEvents();
}
util.inherits(Session, EventEmitter);
+function sessionCallback (session, sid, cb) {
+ return function() {
+ if(sid != session._id) return;
+ return cb.apply(session, args);
+ }
+}
+
+/**
+ * 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
+ */
Session.prototype._setupNativeCallbacks = function _setupNativeCallbacks() {
var self = this;
+ var session_id = this._id;
this._sp_session.logged_in = function(err) {
- console.log('JS LOGIN CALLBACK!!!');
+ if(self._id != session_id) return;
self.emit('login', err);
}
this._sp_session.logged_out = function() {
- console.log('JS LOGOUT CALLBACK')
+ if(self._id != session_id) return;
self.emit('logout');
}
};
+/**
+ * call spotify sp_session_start_processing_events so that pending events
+ * can be processed. Current implementation processes events every 0.8 seconds
+ * regardless of what libspotify returns as next timeout.
+ * TODO ^
+ */
Session.prototype._startProcessingEvents = function _startProcessingEvents() {
+ if(this._process_events_timeout) return;
var self = this;
+ var id = this._id;
this._process_events_timeout = setInterval(function() {
- console.log('processing events', self.name);
+ //console.log('processing events', id);
+ if(id != self._id) return;
var next_timeout = b.session_process_events(self._sp_session);
+ //console.log('processing again in %d', next_timeout);
}, 800);
};
+/**
+ * close this session. In effect, just stopping processing events
+ * TODO maybe make sure we are logged out before considering ourselves closed
+ * then fire a 'close' event
+ */
Session.prototype.close = function close() {
- if(this._sp_session) {
- //console.log('there was a session to release')
- b.session_release(this._sp_session);
- this._sp_session = null;
- }
- if(Session.currentSession === this) {
- Session.currentSession = null;
- }
- console.log('clearing interval');
clearInterval(this._process_events_timeout);
this._process_events_timeout = null;
};
+/**
+ * tests if the current session is closed
+ */
Session.prototype.isClosed = function isClosed() {
- return this._sp_session == null;
+ return this._process_events_timeout === null;
};
+/**
+ * tests if the current sessino is open
+ */
Session.prototype.isOpen = function isOpen() {
return !this.isClosed();
};
+/**
+ * try to login with the given credentials
+ * when this request is processed by libspotify, the 'login' event is
+ * fired with an error or null as first argument
+ * @param {String} login
+ * @param {String} password
+ */
Session.prototype.login = function login(login, password) {
if('string' != typeof login) throw new TypeError('login should be a string')
if('string' != typeof password) throw new TypeError('password should be a string')
b.session_login(this._sp_session, login, password);
};
+
+/**
+ * try to logout.
+ * when this request is processed by libspotify, the 'logout' event is triggered
+ * @param {Function} cb. a callback to automatically attach to the logout event
+ */
Session.prototype.logout = function logout(cb) {
b.session_logout(this._sp_session);
if(typeof cb === 'function') {
@@ -111,4 +190,5 @@ Session.prototype.logout = function logout(cb) {
}
};
+// exports this Class
module.exports = Session;
View
1 package.json
@@ -30,6 +30,7 @@
},
"devDependencies": {
"nodeunit": "~0.7",
+ "trycatch": "*",
"colors": "*"
},
"scripts": {
View
82 src/common.h
@@ -25,10 +25,20 @@
#include <assert.h>
+/**
+ * In a C++ function called from JS, takes a spotify error code, tests if not OK and throws an exception
+ * with the appropriate error message
+ */
#define NSP_THROW_IF_ERROR(error) if(error != SP_ERROR_OK) {return v8::ThrowException( \
v8::Exception::Error(v8::String::New(sp_error_message( error )))\
);}
+/**
+ * Reads a value at the given key and returns a C boolean value
+ * @param o: the JS object on which to read at the given key
+ * @param name: the key to read from
+ * @return the boolean value read from the object
+ */
inline bool NSP_BOOL_KEY(v8::Handle<v8::Object> o, const char* name) {
assert(o->IsObject());
v8::Handle<v8::Value> value = o->Get(v8::String::New(name));
@@ -36,6 +46,12 @@ inline bool NSP_BOOL_KEY(v8::Handle<v8::Object> o, const char* name) {
return value->BooleanValue();
}
+/**
+ * Reads a value at the given key and returns a C int value
+ * @param o: the JS object on which to read at the given key
+ * @param name: the key to read from
+ * @return the int value read from the object
+ */
inline int NSP_INT_KEY(v8::Handle<v8::Object> o, const char* name) {
assert(o->IsObject());
v8::Handle<v8::Value> value = o->Get(v8::String::New(name));
@@ -44,6 +60,14 @@ inline int NSP_INT_KEY(v8::Handle<v8::Object> o, const char* name) {
return value->Int32Value();
}
+/**
+ * Reads a value at the given key and returns a C string value
+ * NOTE: this function allocates the needed space for the string
+ * it is the responsibility of the caller to free this pointer
+ * @param o: the JS object on which to read at the given key
+ * @param name: the key to read from
+ * @return the string value read from the object
+ */
inline char* NSP_STRING_KEY(v8::Handle<v8::Object> o, const char* name) {
assert(o->IsObject());
v8::Handle<v8::Value> value = o->Get(v8::String::New(name));
@@ -57,6 +81,12 @@ inline char* NSP_STRING_KEY(v8::Handle<v8::Object> o, const char* name) {
return v;
}
+/**
+ * Reads a value at the given key and returns a Node buffer object
+ * @param o: the JS object on which to read at the given key
+ * @param name: the key to read from
+ * @return the node buffer object read from the object
+ */
inline char* NSP_BUFFER_KEY(v8::Handle<v8::Object> o, const char* name) {
assert(o->IsObject());
v8::Handle<v8::Value> value = o->Get(v8::String::New(name));
@@ -64,6 +94,13 @@ inline char* NSP_BUFFER_KEY(v8::Handle<v8::Object> o, const char* name) {
return node::Buffer::Data(value->ToObject());
}
+/**
+ * Reads a value at the given key and returns a C int value which is the
+ * length of a Node buffer object
+ * @param o: the JS object on which to read at the given key
+ * @param name: the key to read from
+ * @return the length of the buffer read from the object
+ */
inline int NSP_BUFFERLENGTH_KEY(v8::Handle<v8::Object> o, const char* name) {
assert(o->IsObject());
v8::Handle<v8::Value> value = o->Get(v8::String::New(name));
@@ -71,21 +108,60 @@ inline int NSP_BUFFERLENGTH_KEY(v8::Handle<v8::Object> o, const char* name) {
return node::Buffer::Length(value->ToObject());
}
+/**
+ * Stands for Node+Spotify
+ * namespace in which I declare most of the stuff I need for these bindings
+ */
namespace nsp {
+
+ /**
+ * A function to use as a JS function that does nothing and returns this
+ */
v8::Handle<v8::Value> JsNoOp(const v8::Arguments&);
+
+ /**
+ * init the session related functions to the target module exports
+ */
void init_session(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
+ * does not need a constructor and both attributes are public.
+ * Node's ObjectWrap is useful to wrap C++ classes whereas this class is useful
+ * to wrap C structs. THIS CLASS DOES NOT MANAGE C MEMORY ALLOCATION
+ */
template <typename T>
class ObjectHandle {
public:
+ /**
+ * @constructor
+ * Create a new ObjectHandle object with the given name
+ * the name can be used later to identify the wrapped objects
+ */
ObjectHandle(const char* name);
- //~ObjectHandle();
+ /**
+ * Utility function to retrieve an ObjectHandle from a JS object
+ * @param obj, the JS Object in which the ObjectHandle was wrapped
+ */
static ObjectHandle<T>* Unwrap(v8::Handle<v8::Value> obj);
+ /**
+ * A pointer to the C struct (most often) that we want to wrap
+ * We do not allocate this
+ */
T* pointer;
+
+ /**
+ * The JS Object that we set our pointer in
+ * We do create this one
+ */
v8::Persistent<v8::Object> object;
+ /**
+ * Get the name of the ObjectHandle that we gave it during instanciation
+ */
char* GetName() {
return *(v8::String::Utf8Value(name_));
}
@@ -94,6 +170,10 @@ namespace nsp {
private:
v8::Persistent<v8::String> name_;
+ /**
+ * Empty function to set as constructor for an FunctionTemplate
+ * @deprecated
+ */
v8::Handle<v8::Value> New(const v8::Arguments& args) {
v8::HandleScope scope;
// do nothing;
View
211 src/session.cc
@@ -26,12 +26,15 @@ Handle<Value> nsp::JsNoOp(const Arguments& args) {
return args.This();
}
+/**
+ * spotify callback for the logged_in event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_logged_in_callback(sp_session* session, sp_error error) {
ObjectHandle<sp_session>* s = (ObjectHandle<sp_session>*) sp_session_userdata(session);
Handle<Object> o = s->object;
Handle<Value> cbv = o->Get(String::New("logged_in"));
if(!cbv->IsFunction()) {
- fprintf(stderr, "NOT A FUNCTION\n");
return;
}
@@ -43,16 +46,19 @@ static void call_logged_in_callback(sp_session* session, sp_error error) {
}
Local<Value> argv[argc] = { Local<Value>::New(err) };
cb->Call(Context::GetCurrent()->Global(), argc, argv);
- fprintf(stderr, "KIKOO LOGIN\n");
return;
}
+
+/**
+ * spotify callback for the logged_out event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_logged_out_callback(sp_session* session) {
ObjectHandle<sp_session>* s = (ObjectHandle<sp_session>*) sp_session_userdata(session);
Handle<Object> o = s->object;
Handle<Value> cbv = o->Get(String::New("logged_out"));
if(!cbv->IsFunction()) {
- fprintf(stderr, "NOT A FUNCTION\n");
return;
}
@@ -60,47 +66,171 @@ static void call_logged_out_callback(sp_session* session) {
const unsigned int argc = 0;
Local<Value> argv[argc] = {};
cb->Call(Context::GetCurrent()->Global(), argc, argv);
- fprintf(stderr, "KIKOO LOGOUT\n");
return;
}
+
+/**
+ * spotify callback for the metadata_updated event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_metadata_updated_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the connection_error event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_connection_error_callback(sp_session* session, sp_error error) {
}
+
+/**
+ * spotify callback for the message_to_user event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_message_to_user_callback(sp_session* session, const char* message) {
}
+
+
+uv_timer_t do_notify_handle; ///> uv loop handle for notifying main thread
+
+/**
+ * since the notify_main_thread is not called from the main thread
+ * we have to set a timer in order to execute the JS callback at the right moment
+ */
+static void do_call_notify_main_thread_callback(uv_timer_t* handle, int status) {
+ sp_session* session = (sp_session*) handle->data;
+ ObjectHandle<sp_session>* s = (ObjectHandle<sp_session>*) sp_session_userdata(session);
+ Handle<Object> o = s->object;
+ Handle<Value> cbv = o->Get(String::New("notify_main_thread"));
+ if(!cbv->IsFunction()) {
+ return;
+ }
+
+ Handle<Function> cb = Local<Function>(Function::Cast(*cbv));
+ const unsigned int argc = 0;
+ Local<Value> argv[argc] = {};
+ cb->Call(Context::GetCurrent()->Global(), argc, argv);
+
+ return;
+}
+
+/**
+ * spotify callback for the notify_main_thread event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_notify_main_thread_callback(sp_session* session) {
+ uv_timer_init(uv_default_loop(), &do_notify_handle);
+ do_notify_handle.data = session;
+
+ // set the loop to call our JS callback in 3 ms
+ // TODO how about next tick ?
+ uv_timer_start(&do_notify_handle, &do_call_notify_main_thread_callback, 3, 0);
}
+
+/**
+ * spotify callback for the music_delivery event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static int call_music_delivery_callback(sp_session* session, const sp_audioformat *format, const void *frames, int num_frames) {
return 0;
}
+
+/**
+ * spotify callback for the play_token_lost event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_play_token_lost_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the log_message event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_log_message_callback(sp_session* session, const char* data) {
}
+
+/**
+ * spotify callback for the end_of_track event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_end_of_track_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the streaming_error event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_streaming_error_callback(sp_session* session, sp_error error) {
}
+
+/**
+ * spotify callback for the userinfo_updated event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_userinfo_updated_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the start_playback event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_start_playback_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the stop_playback event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_stop_playback_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the get_audio_buffer_stats event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_get_audio_buffer_stats_callback(sp_session* session, sp_audio_buffer_stats* stats) {
}
+
+/**
+ * spotify callback for the offline_status_updated event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_offline_status_updated_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the offline_error event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_offline_error_callback(sp_session* session, sp_error error) {
}
+
+/**
+ * spotify callback for the credentials_blob_updated event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_credentials_blob_updated_callback(sp_session* session, const char* blob) {
}
+
+/**
+ * spotify callback for the connectionstate_updated event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_connectionstate_updated_callback(sp_session* session) {
}
+
+/**
+ * spotify callback for the scrobble_error event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_scrobble_error_callback(sp_session* session, sp_error error) {
}
+
+/**
+ * spotify callback for the private_session_mode_changed event.
+ * See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
+ */
static void call_private_session_mode_changed_callback(sp_session* session, bool is_private) {
}
@@ -128,6 +258,10 @@ static sp_session_callbacks spcallbacks = {
&call_private_session_mode_changed_callback,
};
+/**
+ * JS session_config implementation. This just creates a sp_session_config struct
+ * from a JS object values and wraps it in a new JS object
+ */
static Handle<Value> Session_Config(const Arguments& args) {
HandleScope scope;
@@ -138,7 +272,6 @@ static Handle<Value> Session_Config(const Arguments& args) {
ObjectHandle<sp_session_config>* session_config = new ObjectHandle<sp_session_config>("sp_session_config");
// allocate the data structure
- //sp_session_config* ptr = &sconfig;
sp_session_config* ptr = session_config->pointer = new sp_session_config;
// set 0 in every field so that spotify doesn't complain
@@ -163,9 +296,10 @@ static Handle<Value> Session_Config(const Arguments& args) {
ptr->application_key = NSP_BUFFER_KEY(obj, "application_key");
ptr->application_key_size = NSP_BUFFERLENGTH_KEY(obj, "application_key");
ptr->callbacks = &spcallbacks;
- // TODO callbacks
+ // copy everything from the original JS object into the new one
+ // so that it can be read later
Handle<Array> properties = obj->GetOwnPropertyNames();
for (unsigned int i = 0; i < properties->Length(); ++i) {
session_config->object->Set(
@@ -177,71 +311,74 @@ static Handle<Value> Session_Config(const Arguments& args) {
return scope.Close(session_config->object);
}
-static sp_session_config spconfig = {
- .api_version = SPOTIFY_API_VERSION,
- .cache_location = "tmp",
- .settings_location = "tmp",
- .application_key = NULL,
- .application_key_size = 0,
- .user_agent = "spot",
- .callbacks = &spcallbacks,
-};
-
+/**
+ * JS session_create implementation. This unwraps the config argument and calls
+ * sp_session_create. The session is then wrapped in a JS object for use in JS land
+ */
static Handle<Value> Session_Create(const Arguments& args) {
HandleScope scope;
- fprintf(stderr, "SIZEOF config %d\n", sizeof(sp_session_config));
- fprintf(stderr, "SIZEOF callbacks %d\n", sizeof(sp_session_callbacks));
- fprintf(stderr, "SIZEOF config wrapper %d\n", sizeof(ObjectHandle<sp_session_config>));
-
assert(args.Length() == 1);
+ // create a new handle for the session struct
ObjectHandle<sp_session>* session = new ObjectHandle<sp_session>("sp_session");
- fprintf(stderr, "CREATED HANDLE at %x\n", session);
- ObjectHandle<sp_session_config>* session_config = ObjectHandle<sp_session_config>::Unwrap(args[0]);
-
- spconfig.application_key = session_config->pointer->application_key;
- spconfig.application_key_size = session_config->pointer->application_key_size;
- spconfig.userdata = session;
+ // unwraps config struct from the first arguments
+ ObjectHandle<sp_session_config>* session_config = ObjectHandle<sp_session_config>::Unwrap(args[0]);
+ // set the current session ObjectHandle as session userdata for later retrieval
session_config->pointer->userdata = session;
- //sp_error error = sp_session_create(session_config->pointer, &session->pointer);
- sp_error error = sp_session_create(&spconfig, &(session->pointer));
+ // actually call session_create
+ sp_error error = sp_session_create(session_config->pointer, &session->pointer);
NSP_THROW_IF_ERROR(error);
- fprintf(stderr, "CREATED SESSION at %x IN HANDLE %x (%x)\n", session->pointer, session, &session->pointer);
return scope.Close(session->object);
}
+/**
+ * JS session_release implementation. This function unwraps the session for the given object
+ * and calls sp_session_release on it
+ */
static Handle<Value> Session_Release(const Arguments& args) {
HandleScope scope;
assert(args.Length() == 1);
+ // unwrap the ObjectHandle for the session
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
+
+ // that would be unfortunate...
assert(NULL != session->pointer);
- fprintf(stderr, "RELEASING %x FROM HANDLE %x (%x)\n", session->pointer, session, &session->pointer);
+
+ // actually call sp_session_release
sp_error error = sp_session_release(session->pointer);
- fprintf(stderr, "RELEAED %x\n", session->pointer);
NSP_THROW_IF_ERROR(error);
+ // make sure we won't be used this pointer ever again
session->pointer = NULL;
- fprintf(stderr, "NEW VALUE %x\n", session->pointer);
return scope.Close(Undefined());
}
+/*
+ * JS session_login implementation. This function unwraps the session from the given object
+ * and calls sp_session_login with the given credentials
+ * TODO support for remember_me and credential blobs
+ */
static Handle<Value> Session_Login(const Arguments& args) {
HandleScope scope;
+ // check parameters sanity
assert(args.Length() == 3);
assert(args[0]->IsObject());
assert(args[1]->IsString());
assert(args[2]->IsString());
+ // unwrap the session from the given object
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
+
+ // actually call sp_session_login
sp_error error = sp_session_login(
session->pointer,
*(String::Utf8Value(args[1]->ToString())),
@@ -254,6 +391,9 @@ static Handle<Value> Session_Login(const Arguments& args) {
return scope.Close(Undefined());
}
+/**
+ * JS session_logout implementation
+ */
static Handle<Value> Session_Logout(const Arguments& args) {
HandleScope scope;
@@ -267,6 +407,12 @@ static Handle<Value> Session_Logout(const Arguments& args) {
return scope.Close(Undefined());
}
+/**
+ * JS session_process_events implementation. This function unwraps the session handle
+ * and calls sp_session_process_events. libspotify uses this to process all pending events
+ * from the main thread
+ * @return next_timeout when in milliseconds to call this function again
+ */
static Handle<Value> Session_Process_Events(const Arguments& args) {
HandleScope scope;
@@ -275,7 +421,6 @@ static Handle<Value> Session_Process_Events(const Arguments& args) {
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
int next_timeout = 0;
- fprintf(stderr, "processing events from C...\n");
sp_error error = sp_session_process_events(session->pointer, &next_timeout);
NSP_THROW_IF_ERROR(error);
View
8 test/test-000-a-check.js
@@ -1,4 +1,12 @@
var fs = require('fs');
+require('trycatch');
+
+/**
+ * We don't actually export any tests. We only check that all the data needed for the
+ * tests to run are here
+ * TODO test connectivity
+ */
+
var exists = false;
try {
View
23 test/test-010-session01-instance.js
@@ -9,12 +9,19 @@ exports.testInstanciatingSessionWithAppKeyPath = function(test) {
}, "Opening a session with a valid spotify_key_app should not throw");
test.done();
}
- exports.testStopSession = function(test) {
- test.doesNotThrow(function() {
- session.close();
- }, 'Closing a session should not throw');
- test.ok(session.isClosed(), 'Session isClosed should return true');
- test.ok(!session.isOpen(), 'Session isOpen should return false');
-
+exports.testOnlyOneSession = function(test) {
+ test.throws(function() {
+ var s = new sp.Session({});
+ });
test.done();
- }
+}
+exports.testStopSession = function(test) {
+ test.doesNotThrow(function() {
+ session.close();
+ }, 'Closing a session should not throw');
+ test.ok(session.isClosed(), 'Session isClosed should return true');
+ test.ok(!session.isOpen(), 'Session isOpen should return false');
+
+ test.done();
+}
+
View
11 test/test-010-session02-login.js
@@ -18,9 +18,16 @@ exports.testLoginIsSucessful = function(test) {
});
session.name = 'chose';
session.login(cred.login, cred.password);
+ console.log('trying to login, this may take a while');
+ var logged = false;
+ setTimeout(function() {
+ if(!logged) {
+ test.fail("Waited too long to login");
+ }
+ }, 8000);
session.on('login', function(err) {
- console.log('ON TEST LOGIN CALLBACK');
- test.equal(err, null, 'There should be no error');
+ test.equal(null, err, err + ' occured on login');
+ logged = true;
session.logout(function() {
session.close();
test.done();

No commit comments for this range

Something went wrong with that request. Please try again.