Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

implemented session.getTrackByLink(link [, callback(err, track)]) -> …

…track
  • Loading branch information...
commit 58391f3df53a288852fd471b3d4fd9604b96703c 1 parent 508df73
@rsms rsms authored
View
64 src/callback_queue.cc
@@ -0,0 +1,64 @@
+#include "callback_queue.h"
+#include "track.h"
+#include "album.h"
+#include "artist.h"
+
+void CallbackQueue::push(const Local<Value> &callback, int type /*= 0*/,
+ void *obj /*= NULL*/) {
+ CallbackQueueEntry *entry = new CallbackQueueEntry;
+ entry->callback = cb_persist(callback);
+ entry->type = type;
+ entry->obj = obj;
+ TAILQ_INSERT_TAIL(&queue_, entry, link);
+}
+
+void CallbackQueue::push(const Local<Value> &callback, sp_track *track) {
+ assert(sp_track_error(track) == SP_ERROR_IS_LOADING);
+ sp_track_add_ref(track);
+ push(callback, kMetadataUpdateTypeTrack, reinterpret_cast<void*>(track));
+}
+
+void CallbackQueue::remove(CallbackQueueEntry *entry) {
+ if (entry->type == kMetadataUpdateTypeTrack && entry->obj)
+ sp_track_release(reinterpret_cast<sp_track*>(entry->obj));
+ if (entry->callback)
+ cb_destroy(entry->callback);
+ TAILQ_REMOVE(&queue_, entry, link);
+ delete entry;
+}
+
+int CallbackQueue::process(const Handle<Object> &context,
+ bool once /*= false*/) {
+ int hits = 0;
+ CallbackQueueEntry *entry;
+ TAILQ_FOREACH(entry, &queue_, link) {
+ sp_error status;
+
+ if (entry->type == kMetadataUpdateTypeTrack) {
+ sp_track *t = reinterpret_cast<sp_track *>(entry->obj);
+ status = sp_track_error(t);
+ if (status != SP_ERROR_IS_LOADING) {
+ // state has changed -- invoke callback
+ Handle<Value> err = Undefined();
+ if (status == SP_ERROR_OK) {
+ // loaded
+ Handle<Value> argv[] = { Undefined(), Track::New(t) };
+ (*entry->callback)->Call(context, 2, argv);
+ } else {
+ // an error occured
+ Handle<Value> argv[] = {
+ Exception::Error(String::New(sp_error_message(status))) };
+ (*entry->callback)->Call(context, 1, argv);
+ }
+ }
+ } // kMetadataUpdateTypeTrack
+
+ // are we done with this entry?
+ if (status != SP_ERROR_IS_LOADING) {
+ remove(entry);
+ hits++;
+ if (once) return hits;
+ }
+ }
+ return hits;
+}
View
31 src/callback_queue.h
@@ -0,0 +1,31 @@
+#ifndef SPOTIFY_CALLBACK_QUEUE_H_
+#define SPOTIFY_CALLBACK_QUEUE_H_
+
+#include "index.h"
+#include "queue.h"
+
+#define CallbackQueueForEach(entry, q) TAILQ_FOREACH(entry, &(q)->queue_, link)
+
+// queue for various callbacks waiting on a particular state
+typedef struct CallbackQueueEntry {
+ // previous and next siblings in the doubly linked-list
+ TAILQ_ENTRY(CallbackQueueEntry) link;
+ // callback to be invoked
+ Persistent<Function> *callback;
+ // optional type (e.g. "it's a track waiting for load event")
+ int type;
+ // optional object/userdata (e.g. a sp_track ref)
+ void *obj;
+} CallbackQueueEntry;
+
+class CallbackQueue {
+ public:
+ TAILQ_HEAD(, CallbackQueueEntry) queue_;
+ CallbackQueue() { TAILQ_INIT(&queue_); }
+ void push(const Local<Value> &callback, int type = 0, void *obj = NULL);
+ void push(const Local<Value> &callback, sp_track *track);
+ void remove(CallbackQueueEntry *entry);
+ int process(const Handle<Object> &context, bool once = false);
+};
+
+#endif // SPOTIFY_CALLBACK_QUEUE_H_
View
37 src/session.cc
@@ -1,6 +1,7 @@
#include "session.h"
#include "user.h"
#include "search.h"
+#include "track.h"
#include <pthread.h>
#include <signal.h>
@@ -126,6 +127,7 @@ static void MetadataUpdated(sp_session *session) {
Session* s = reinterpret_cast<Session*>(sp_session_userdata(session));
assert(s->main_thread_id_ == pthread_self() /* or we will crash */);
s->Emit(String::New("metadataUpdated"), 0, NULL);
+ s->metadata_update_queue_.process(s->handle_);
}
static void ConnectionError(sp_session* session, sp_error error) {
@@ -414,6 +416,40 @@ Handle<Value> Session::Search(const Arguments& args) {
return Undefined();
}
+// .getTrackByLink( link [, callback(err, track)] ) -> Track
+Handle<Value> Session::GetTrackByLink(const Arguments& args) {
+ HandleScope scope;
+
+ if (args.Length() < 1)
+ return JS_THROW(TypeError, "search takes at least one argument");
+ if (args.Length() > 1 && !args[1]->IsFunction())
+ return JS_THROW(TypeError, "last argument must be a function");
+
+ String::Utf8Value linkstr(args[0]);
+ sp_link *link = sp_link_create_from_string(*linkstr);
+ if (!link)
+ return JS_THROW(TypeError, "invalid track link");
+ sp_track *t = sp_link_as_track(link);
+ if (!t)
+ return JS_THROW(Error, "failed to initialize track object from link");
+
+ Handle<Value> track = Track::New(t);
+
+ // "load" callback?
+ if (args.Length() > 1) {
+ Session* s = Unwrap<Session>(args.This());
+ if (!sp_track_is_loaded(t)) {
+ // todo: pass Handle<Value> instead of sp_track*
+ s->metadata_update_queue_.push(args[1], t);
+ } else {
+ Handle<Value> argv[] = { Undefined(), track };
+ Function::Cast(*args[1])->Call(s->handle_, 2, argv);
+ }
+ }
+
+ return scope.Close(track);
+}
+
// ---------
// Properties
@@ -467,6 +503,7 @@ void Session::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "logout", Logout);
NODE_SET_PROTOTYPE_METHOD(t, "login", Login);
NODE_SET_PROTOTYPE_METHOD(t, "search", Search);
+ NODE_SET_PROTOTYPE_METHOD(t, "getTrackByLink", GetTrackByLink);
Local<ObjectTemplate> instance_t = t->InstanceTemplate();
instance_t->SetInternalFieldCount(1);
View
12 src/session.h
@@ -3,6 +3,7 @@
#include "index.h"
#include "atomic_queue.h"
+#include "callback_queue.h"
#include "playlistcontainer.h"
class Session : public node::EventEmitter {
@@ -13,6 +14,7 @@ class Session : public node::EventEmitter {
static v8::Handle<v8::Value> Login(const v8::Arguments& args);
static v8::Handle<v8::Value> Logout(const v8::Arguments& args);
static v8::Handle<v8::Value> Search(const v8::Arguments& args);
+ static v8::Handle<v8::Value> GetTrackByLink(const v8::Arguments& args);
static v8::Handle<v8::Value> ConnectionStateGetter(
v8::Local<v8::String> property,
const v8::AccessorInfo& info);
@@ -40,8 +42,14 @@ class Session : public node::EventEmitter {
ev_async runloop_async_;
// Spotify background thread-to-node-main glue
- ev_async logmsg_async_;
- nt_atomic_queue log_messages_q_;
+ ev_async logmsg_async_;
+
+ // log messages delivered from a background thread
+ nt_atomic_queue log_messages_q_;
+
+ // queued callbacks waiting for metadata_update events for particular objects
+ CallbackQueue metadata_update_queue_;
+
void DequeueLogMessages();
};
Please sign in to comment.
Something went wrong with that request. Please try again.