Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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

…track
  • Loading branch information...
commit 58391f3df53a288852fd471b3d4fd9604b96703c 1 parent 508df73
Rasmus rsms authored
64 src/callback_queue.cc
... ... @@ -0,0 +1,64 @@
  1 +#include "callback_queue.h"
  2 +#include "track.h"
  3 +#include "album.h"
  4 +#include "artist.h"
  5 +
  6 +void CallbackQueue::push(const Local<Value> &callback, int type /*= 0*/,
  7 + void *obj /*= NULL*/) {
  8 + CallbackQueueEntry *entry = new CallbackQueueEntry;
  9 + entry->callback = cb_persist(callback);
  10 + entry->type = type;
  11 + entry->obj = obj;
  12 + TAILQ_INSERT_TAIL(&queue_, entry, link);
  13 +}
  14 +
  15 +void CallbackQueue::push(const Local<Value> &callback, sp_track *track) {
  16 + assert(sp_track_error(track) == SP_ERROR_IS_LOADING);
  17 + sp_track_add_ref(track);
  18 + push(callback, kMetadataUpdateTypeTrack, reinterpret_cast<void*>(track));
  19 +}
  20 +
  21 +void CallbackQueue::remove(CallbackQueueEntry *entry) {
  22 + if (entry->type == kMetadataUpdateTypeTrack && entry->obj)
  23 + sp_track_release(reinterpret_cast<sp_track*>(entry->obj));
  24 + if (entry->callback)
  25 + cb_destroy(entry->callback);
  26 + TAILQ_REMOVE(&queue_, entry, link);
  27 + delete entry;
  28 +}
  29 +
  30 +int CallbackQueue::process(const Handle<Object> &context,
  31 + bool once /*= false*/) {
  32 + int hits = 0;
  33 + CallbackQueueEntry *entry;
  34 + TAILQ_FOREACH(entry, &queue_, link) {
  35 + sp_error status;
  36 +
  37 + if (entry->type == kMetadataUpdateTypeTrack) {
  38 + sp_track *t = reinterpret_cast<sp_track *>(entry->obj);
  39 + status = sp_track_error(t);
  40 + if (status != SP_ERROR_IS_LOADING) {
  41 + // state has changed -- invoke callback
  42 + Handle<Value> err = Undefined();
  43 + if (status == SP_ERROR_OK) {
  44 + // loaded
  45 + Handle<Value> argv[] = { Undefined(), Track::New(t) };
  46 + (*entry->callback)->Call(context, 2, argv);
  47 + } else {
  48 + // an error occured
  49 + Handle<Value> argv[] = {
  50 + Exception::Error(String::New(sp_error_message(status))) };
  51 + (*entry->callback)->Call(context, 1, argv);
  52 + }
  53 + }
  54 + } // kMetadataUpdateTypeTrack
  55 +
  56 + // are we done with this entry?
  57 + if (status != SP_ERROR_IS_LOADING) {
  58 + remove(entry);
  59 + hits++;
  60 + if (once) return hits;
  61 + }
  62 + }
  63 + return hits;
  64 +}
31 src/callback_queue.h
... ... @@ -0,0 +1,31 @@
  1 +#ifndef SPOTIFY_CALLBACK_QUEUE_H_
  2 +#define SPOTIFY_CALLBACK_QUEUE_H_
  3 +
  4 +#include "index.h"
  5 +#include "queue.h"
  6 +
  7 +#define CallbackQueueForEach(entry, q) TAILQ_FOREACH(entry, &(q)->queue_, link)
  8 +
  9 +// queue for various callbacks waiting on a particular state
  10 +typedef struct CallbackQueueEntry {
  11 + // previous and next siblings in the doubly linked-list
  12 + TAILQ_ENTRY(CallbackQueueEntry) link;
  13 + // callback to be invoked
  14 + Persistent<Function> *callback;
  15 + // optional type (e.g. "it's a track waiting for load event")
  16 + int type;
  17 + // optional object/userdata (e.g. a sp_track ref)
  18 + void *obj;
  19 +} CallbackQueueEntry;
  20 +
  21 +class CallbackQueue {
  22 + public:
  23 + TAILQ_HEAD(, CallbackQueueEntry) queue_;
  24 + CallbackQueue() { TAILQ_INIT(&queue_); }
  25 + void push(const Local<Value> &callback, int type = 0, void *obj = NULL);
  26 + void push(const Local<Value> &callback, sp_track *track);
  27 + void remove(CallbackQueueEntry *entry);
  28 + int process(const Handle<Object> &context, bool once = false);
  29 +};
  30 +
  31 +#endif // SPOTIFY_CALLBACK_QUEUE_H_
37 src/session.cc
... ... @@ -1,6 +1,7 @@
1 1 #include "session.h"
2 2 #include "user.h"
3 3 #include "search.h"
  4 +#include "track.h"
4 5
5 6 #include <pthread.h>
6 7 #include <signal.h>
@@ -126,6 +127,7 @@ static void MetadataUpdated(sp_session *session) {
126 127 Session* s = reinterpret_cast<Session*>(sp_session_userdata(session));
127 128 assert(s->main_thread_id_ == pthread_self() /* or we will crash */);
128 129 s->Emit(String::New("metadataUpdated"), 0, NULL);
  130 + s->metadata_update_queue_.process(s->handle_);
129 131 }
130 132
131 133 static void ConnectionError(sp_session* session, sp_error error) {
@@ -414,6 +416,40 @@ Handle<Value> Session::Search(const Arguments& args) {
414 416 return Undefined();
415 417 }
416 418
  419 +// .getTrackByLink( link [, callback(err, track)] ) -> Track
  420 +Handle<Value> Session::GetTrackByLink(const Arguments& args) {
  421 + HandleScope scope;
  422 +
  423 + if (args.Length() < 1)
  424 + return JS_THROW(TypeError, "search takes at least one argument");
  425 + if (args.Length() > 1 && !args[1]->IsFunction())
  426 + return JS_THROW(TypeError, "last argument must be a function");
  427 +
  428 + String::Utf8Value linkstr(args[0]);
  429 + sp_link *link = sp_link_create_from_string(*linkstr);
  430 + if (!link)
  431 + return JS_THROW(TypeError, "invalid track link");
  432 + sp_track *t = sp_link_as_track(link);
  433 + if (!t)
  434 + return JS_THROW(Error, "failed to initialize track object from link");
  435 +
  436 + Handle<Value> track = Track::New(t);
  437 +
  438 + // "load" callback?
  439 + if (args.Length() > 1) {
  440 + Session* s = Unwrap<Session>(args.This());
  441 + if (!sp_track_is_loaded(t)) {
  442 + // todo: pass Handle<Value> instead of sp_track*
  443 + s->metadata_update_queue_.push(args[1], t);
  444 + } else {
  445 + Handle<Value> argv[] = { Undefined(), track };
  446 + Function::Cast(*args[1])->Call(s->handle_, 2, argv);
  447 + }
  448 + }
  449 +
  450 + return scope.Close(track);
  451 +}
  452 +
417 453 // ---------
418 454 // Properties
419 455
@@ -467,6 +503,7 @@ void Session::Initialize(Handle<Object> target) {
467 503 NODE_SET_PROTOTYPE_METHOD(t, "logout", Logout);
468 504 NODE_SET_PROTOTYPE_METHOD(t, "login", Login);
469 505 NODE_SET_PROTOTYPE_METHOD(t, "search", Search);
  506 + NODE_SET_PROTOTYPE_METHOD(t, "getTrackByLink", GetTrackByLink);
470 507
471 508 Local<ObjectTemplate> instance_t = t->InstanceTemplate();
472 509 instance_t->SetInternalFieldCount(1);
12 src/session.h
@@ -3,6 +3,7 @@
3 3
4 4 #include "index.h"
5 5 #include "atomic_queue.h"
  6 +#include "callback_queue.h"
6 7 #include "playlistcontainer.h"
7 8
8 9 class Session : public node::EventEmitter {
@@ -13,6 +14,7 @@ class Session : public node::EventEmitter {
13 14 static v8::Handle<v8::Value> Login(const v8::Arguments& args);
14 15 static v8::Handle<v8::Value> Logout(const v8::Arguments& args);
15 16 static v8::Handle<v8::Value> Search(const v8::Arguments& args);
  17 + static v8::Handle<v8::Value> GetTrackByLink(const v8::Arguments& args);
16 18 static v8::Handle<v8::Value> ConnectionStateGetter(
17 19 v8::Local<v8::String> property,
18 20 const v8::AccessorInfo& info);
@@ -40,8 +42,14 @@ class Session : public node::EventEmitter {
40 42 ev_async runloop_async_;
41 43
42 44 // Spotify background thread-to-node-main glue
43   - ev_async logmsg_async_;
44   - nt_atomic_queue log_messages_q_;
  45 + ev_async logmsg_async_;
  46 +
  47 + // log messages delivered from a background thread
  48 + nt_atomic_queue log_messages_q_;
  49 +
  50 + // queued callbacks waiting for metadata_update events for particular objects
  51 + CallbackQueue metadata_update_queue_;
  52 +
45 53 void DequeueLogMessages();
46 54 };
47 55

0 comments on commit 58391f3

Please sign in to comment.
Something went wrong with that request. Please try again.