-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathsession.cc
More file actions
520 lines (423 loc) · 18 KB
/
session.cc
File metadata and controls
520 lines (423 loc) · 18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
/*
* =====================================================================================
*
* Filename: session.cc
*
* Description: Bindings for the sp_session subsystem
*
* Version: 1.0
* Created: 16/12/2012 21:38:05
* Revision: none
* Compiler: gcc
*
* Author: Florent Jaby (FJ), florent.jaby@gmail.com
* Company: Florent Jaby
*
* =====================================================================================
*/
#include "common.h"
#include "playlistcallbacks.cc"
#include <stdlib.h>
using namespace v8;
using namespace nsp;
Handle<Value> nsp::JsNoOp(const Arguments& args) {
return args.This();
}
/*
* The following callback function do nothing more than getting the session object and calling their
* Javascript counterparts
*/
/**
* 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()) {
return;
}
Handle<Function> cb = Local<Function>(Function::Cast(*cbv));
const unsigned int argc = 1;
Handle<Value> err = Null();
if(error != SP_ERROR_OK) {
err = Exception::Error(String::New(sp_error_message(error)));
}
Local<Value> argv[argc] = { Local<Value>::New(err) };
cb->Call(Context::GetCurrent()->Global(), argc, argv);
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()) {
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 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) {
ObjectHandle<sp_session>* s = (ObjectHandle<sp_session>*) sp_session_userdata(session);
Handle<Object> o = s->object;
Handle<Value> cbv = o->Get(String::New("metadata_updated"));
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 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, 1, 0);
}
/**
* spotify callback for the music_delivery event.
* See https://developer.spotify.com/technologies/libspotify/docs/12.1.45/structsp__session__callbacks.html
* implemented in player.cc
*/
extern int call_music_delivery_callback(sp_session* session, const sp_audioformat *format, const void *frames, int num_frames);
/**
* 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) {
ObjectHandle<sp_session>* s = (ObjectHandle<sp_session>*) sp_session_userdata(session);
Handle<Object> o = s->object;
Handle<Value> cbv = o->Get(String::New("play_token_lost"));
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 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
* implemented in player.cc
*/
extern 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) {
}
static sp_session_callbacks spcallbacks = {
&call_logged_in_callback,
&call_logged_out_callback,
&call_metadata_updated_callback,
&call_connection_error_callback,
&call_message_to_user_callback,
&call_notify_main_thread_callback,
&call_music_delivery_callback,
&call_play_token_lost_callback,
&call_log_message_callback,
&call_end_of_track_callback,
&call_streaming_error_callback,
&call_userinfo_updated_callback,
&call_start_playback_callback,
&call_stop_playback_callback,
&call_get_audio_buffer_stats_callback,
&call_offline_status_updated_callback,
&call_offline_error_callback,
&call_credentials_blob_updated_callback,
&call_connectionstate_updated_callback,
&call_scrobble_error_callback,
&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;
assert(args.Length() == 1);
assert(args[0]->IsObject());
// create the handle for this object
ObjectHandle<sp_session_config>* session_config = new ObjectHandle<sp_session_config>("sp_session_config");
// allocate the data structure
sp_session_config* ptr = session_config->pointer = new sp_session_config;
// set 0 in every field so that spotify doesn't complain
memset(ptr, 0, sizeof(sp_session_config));
Handle<Object> obj = args[0]->ToObject();
ptr->api_version = SPOTIFY_API_VERSION;
ptr->cache_location = NSP_STRING_KEY(obj, "cache_location");
ptr->settings_location = NSP_STRING_KEY(obj, "settings_location");
ptr->user_agent = NSP_STRING_KEY(obj, "user_agent");
ptr->compress_playlists = NSP_BOOL_KEY(obj, "compress_playlists");
ptr->dont_save_metadata_for_playlists = NSP_BOOL_KEY(obj, "dont_save_metadata_for_playlists");
ptr->initially_unload_playlists = NSP_BOOL_KEY(obj, "initially_unload_playlists");
ptr->device_id = NSP_STRING_KEY(obj, "device_id");
ptr->proxy = NSP_STRING_KEY(obj, "proxy");
ptr->proxy_username = NSP_STRING_KEY(obj, "proxy_username");
ptr->proxy_password = NSP_STRING_KEY(obj, "proxy_password");
// ptr->ca_certs_filename = NSP_STRING_KEY(obj, "ca_certs_filename");
ptr->tracefile = NSP_STRING_KEY(obj, "tracefile");
ptr->application_key = NSP_BUFFER_KEY(obj, "application_key");
ptr->application_key_size = NSP_BUFFERLENGTH_KEY(obj, "application_key");
ptr->callbacks = &spcallbacks;
// 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(
properties->Get(i),
obj->Get(properties->Get(i))
);
}
return scope.Close(session_config->object);
}
/**
* 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;
assert(args.Length() == 1);
// create a new handle for the session struct
ObjectHandle<sp_session>* session = new ObjectHandle<sp_session>("sp_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;
// actually call session_create
sp_error error = sp_session_create(session_config->pointer, &session->pointer);
NSP_THROW_IF_ERROR(error);
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);
// actually call sp_session_release
sp_error error = sp_session_release(session->pointer);
NSP_THROW_IF_ERROR(error);
// make sure we won't be used this pointer ever again
session->pointer = NULL;
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())),
*(String::Utf8Value(args[2]->ToString())),
false,
NULL
);
NSP_THROW_IF_ERROR(error);
return scope.Close(Undefined());
}
/**
* JS session_logout implementation
*/
static Handle<Value> Session_Logout(const Arguments& args) {
HandleScope scope;
assert(args.Length() == 1);
assert(args[0]->IsObject());
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
sp_error error = sp_session_logout(session->pointer);
NSP_THROW_IF_ERROR(error);
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;
assert(args.Length() == 1);
assert(args[0]->IsObject());
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
int next_timeout = 0;
sp_error error = sp_session_process_events(session->pointer, &next_timeout);
NSP_THROW_IF_ERROR(error);
return scope.Close(Number::New(next_timeout));
}
/**
* JS session_playlistcontainer implementation. This function unwraps the session handle
* and calls sp_session_playlistcontainer. this will return the sp_playlistcontainer for the currently logged in user
*/
static Handle<Value> Session_PlaylistContainer(const Arguments& args) {
HandleScope scope;
assert(args.Length() == 1);
assert(args[0]->IsObject());
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
sp_playlistcontainer* spplaylistcontainer = sp_session_playlistcontainer(session->pointer);
ObjectHandle<sp_playlistcontainer>* playlistcontainer = new ObjectHandle<sp_playlistcontainer>("sp_playlistcontainer");
playlistcontainer->pointer = spplaylistcontainer;
// actually call sp_playlistcontainer_add_callbacks
sp_error error = sp_playlistcontainer_add_callbacks(spplaylistcontainer, &nsp_playlistcontainer_callbacks, playlistcontainer);
NSP_THROW_IF_ERROR(error);
return scope.Close(playlistcontainer->object);
}
static Handle<Value> Session_Starred_Create(const Arguments& args) {
HandleScope scope;
assert(args.Length() == 1);
assert(args[0]->IsObject());
ObjectHandle<sp_session>* session = ObjectHandle<sp_session>::Unwrap(args[0]);
// actually call sp_session_starred_create
sp_playlist* spplaylist = sp_session_starred_create(session->pointer);
// Set the playlist in RAM
sp_playlist_set_in_ram(session->pointer, spplaylist, true);
ObjectHandle<sp_playlist>* playlist = new ObjectHandle<sp_playlist>("sp_playlist");
playlist->pointer = spplaylist;
sp_error error = sp_playlist_add_callbacks(spplaylist, &nsp_playlist_callbacks, playlist);
NSP_THROW_IF_ERROR(error);
return scope.Close(playlist->object);
}
void nsp::init_session(Handle<Object> target) {
NODE_SET_METHOD(target, "session_config", Session_Config);
NODE_SET_METHOD(target, "session_create", Session_Create);
NODE_SET_METHOD(target, "session_release", Session_Release);
NODE_SET_METHOD(target, "session_login", Session_Login);
NODE_SET_METHOD(target, "session_logout", Session_Logout);
NODE_SET_METHOD(target, "session_process_events", Session_Process_Events);
NODE_SET_METHOD(target, "session_playlistcontainer", Session_PlaylistContainer);
NODE_SET_METHOD(target, "session_starred_create", Session_Starred_Create);
}