static void playlistcontainer_loaded(sp_playlistcontainer *pc, void *userdata) { syslog(LOG_DEBUG, "playlistcontainer_loaded\n"); sp_session *session = userdata; struct state *state = sp_session_userdata(session); sp_playlistcontainer_remove_callbacks(pc, &playlistcontainer_callbacks, session); state->http = evhttp_new(state->event_base); evhttp_set_timeout(state->http, 60); evhttp_set_gencb(state->http, &handle_request, state); // Bind HTTP server int bind = evhttp_bind_socket(state->http, state->http_host, state->http_port); if (bind == -1) { syslog(LOG_WARNING, "Could not bind HTTP server socket to %s:%d", state->http_host, state->http_port); sp_session_logout(session); return; } syslog(LOG_DEBUG, "HTTP server listening on %s:%d", state->http_host, state->http_port); }
static void connection_error(sp_session * session, sp_error error) { PyGILState_STATE gstate; PyObject *res, *err, *method; #ifdef DEBUG fprintf(stderr, "[DEBUG]-session- >> connection_error called\n"); #endif gstate = PyGILState_Ensure(); Session *psession = (Session *) PyObject_CallObject((PyObject *)&SessionType, NULL); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); err = error_message(error); method = PyObject_GetAttrString(client, "connection_error"); res = PyObject_CallFunctionObjArgs(method, psession, err, NULL); if (!res) PyErr_WriteUnraisable(method); Py_DECREF(psession); Py_DECREF(err); Py_XDECREF(res); Py_DECREF(method); PyGILState_Release(gstate); }
static void notify_main_thread(sp_session * session) { PyGILState_STATE gstate; PyObject *res, *method; #ifdef DEBUG fprintf(stderr, "[DEBUG]-session- >> notify_main_thread called\n"); #endif if (!session_constructed) return; gstate = PyGILState_Ensure(); Session *psession = (Session *) PyObject_CallObject((PyObject *)&SessionType, NULL); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); if (client != NULL) { method = PyObject_GetAttrString(client, "wake"); res = PyObject_CallFunctionObjArgs(method, psession, NULL); if (!res) PyErr_WriteUnraisable(method); Py_XDECREF(res); Py_DECREF(method); } Py_DECREF(psession); PyGILState_Release(gstate); }
static void log_message(sp_session * session, const char *data) { PyGILState_STATE gstate; PyObject *res, *method, *msg; #ifdef DEBUG fprintf(stderr, "[DEBUG]-session- >> log message: %s\n", data); #endif gstate = PyGILState_Ensure(); Session *psession = (Session *) PyObject_CallObject((PyObject *)&SessionType, NULL); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); msg = PyUnicode_FromString(data); method = PyObject_GetAttrString(client, "log_message"); res = PyObject_CallFunctionObjArgs(method, psession, msg, NULL); if (!res) PyErr_WriteUnraisable(method); Py_DECREF(psession); Py_XDECREF(res); Py_DECREF(msg); Py_DECREF(method); PyGILState_Release(gstate); }
int SpotifyPlayback::musicDelivery(sp_session *session, const sp_audioformat *format, const void *frames, int numFrames_) { SpotifySession* _session = reinterpret_cast<SpotifySession*>(sp_session_userdata(session)); Q_ASSERT (_session); QMutex &m = _session->Playback()->dataMutex(); _session->Playback()->m_currChannels = format->channels; _session->Playback()->m_currFrames = numFrames_; _session->Playback()->m_currSamples = format->sample_rate; if (numFrames_ == 0) // flush caches { QMutexLocker l(&m); _session->Playback()->clearData(); return 0; } m.lock(); // libspotify v11 bug, seems to retry to push the last batch of audio no matter what. short-circuit to ignore if ( _session->Playback()->trackIsOver() ) { m.unlock(); return numFrames_; } const QByteArray data( (const char*)frames, numFrames_ * 4 ); // 4 == channels * ( bits per sample / 8 ) == 2 * ( 16 / 8 ) == 2 * 2 _session->Playback()->queueData( data ); m.unlock(); return numFrames_; // num frames read, not bytes read. we always read all the frames }
void SpotifyClient::LoggedInCallback(sp_session* session, sp_error error) { SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session)); const bool success = error == SP_ERROR_OK; spotify_pb::LoginResponse_Error error_code = spotify_pb::LoginResponse_Error_Other; if (!success) { qLog(Warning) << "Failed to login" << sp_error_message(error); } switch (error) { case SP_ERROR_BAD_USERNAME_OR_PASSWORD: error_code = spotify_pb::LoginResponse_Error_BadUsernameOrPassword; break; case SP_ERROR_USER_BANNED: error_code = spotify_pb::LoginResponse_Error_UserBanned; break; case SP_ERROR_USER_NEEDS_PREMIUM : error_code = spotify_pb::LoginResponse_Error_UserNeedsPremium; break; } me->SendLoginCompleted(success, sp_error_message(error), error_code); if (success) { sp_playlistcontainer_add_callbacks( sp_session_playlistcontainer(session), &me->playlistcontainer_callbacks_, me); } }
static void logged_out(sp_session *session) { fprintf(stderr, "logged_out\n"); struct state *state = sp_session_userdata(session); event_del(state->async); event_del(state->timer); event_del(state->sigint); event_base_loopbreak(state->event_base); apr_pool_destroy(state->pool); }
void SpotifyPlayback::endOfTrack(sp_session *session) { SpotifySession* _session = reinterpret_cast<SpotifySession*>( sp_session_userdata( session ) ); // qDebug() << "Got spotify end of track callback!"; if ( !_session->Playback()->trackIsOver() ) _session->Playback()->endTrack(); }
static void logged_out_callback(sp_session *session) { TRACE("logged_out_callback\n"); struct owl_state* state = sp_session_userdata(session); state->state = OWL_STATE_INITIALIZED; INFO("Logged out from Spotify\n"); respond_success(state->http_request); }
void logged_out(sp_session *session) { syslog(LOG_DEBUG, "logged_out\n"); struct state *state = sp_session_userdata(session); event_del(state->async); event_del(state->timer); event_del(state->sigint); event_base_loopbreak(state->event_base); apr_pool_destroy(state->pool); closelog(); }
static void connection_error(sp_session *session, sp_error error) { // fprintf(stderr, "----------> connection_error called\n"); PyGILState_STATE gstate; gstate = PyGILState_Ensure(); Session *psession = (Session *)PyObject_CallObject((PyObject *)&SessionType, NULL); Py_INCREF(psession); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); PyObject_CallMethod(client, "connection_error", "Oi", psession, error); Py_DECREF(psession); PyGILState_Release(gstate); }
static void log_message(sp_session *session, const char *data) { // fprintf(stderr, "----------> log_message called: %s\n", data); PyGILState_STATE gstate; gstate = PyGILState_Ensure(); Session *psession = (Session *)PyObject_CallObject((PyObject *)&SessionType, NULL); Py_INCREF(psession); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); PyObject_CallMethod(client, "log_message", "Os", psession, data); Py_DECREF(psession); PyGILState_Release(gstate); }
static void end_of_track(sp_session *session) { // fprintf(stderr, "----------> end_of_track called\n"); PyGILState_STATE gstate; gstate = PyGILState_Ensure(); Session *psession = (Session *)PyObject_CallObject((PyObject *)&SessionType, NULL); Py_INCREF(psession); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); PyObject_CallMethod(client, "end_of_track", "O", psession); Py_DECREF(psession); PyGILState_Release(gstate); }
static void message_to_user(sp_session *session, const char *message) { // fprintf(stderr, "----------> message to user: %s\n", message); PyGILState_STATE gstate; gstate = PyGILState_Ensure(); Session *psession = (Session *)PyObject_CallObject((PyObject *)&SessionType, NULL); Py_INCREF(psession); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); PyObject_CallMethod(client, "message_to_user", "Os", psession, message); Py_DECREF(psession); PyGILState_Release(gstate); }
/** credentialsBlobUpdated callback from login when we get the blob, used instead of plain password will send blob to application to storage @note its up the the application to store it @note2: it will be fired more than once, always store latest blob @note3: if user have no cache, it will fire once, if cache it will (maybe) update current blob **/ void SpotifySession::credentialsBlobUpdated(sp_session *session, const char *blob) { SpotifySession* _session = reinterpret_cast<SpotifySession*>(sp_session_userdata(session)); #if SPOTIFY_API_VERSION >= 12 const char* username = sp_session_user_name( session ); #else const char* username = sp_user_canonical_name( sp_session_user( session ) ); #endif _session->m_blob = QByteArray(blob); qDebug() << " ==== Got blob update for " << QString::fromUtf8(username, strlen(username) ) << " ==== "; emit _session->blobUpdated( username, QByteArray(blob).constData() ); }
static void logged_in(sp_session *session, sp_error error) { spotify_object *p = sp_session_userdata(session); p->is_logged_in = 1; if (SP_ERROR_OK != error) { p->is_logged_out = 1; sp_session_release(session); char *errMsg; spprintf(&errMsg, 0, "login failed: %s", sp_error_message(error)); zend_throw_exception((zend_class_entry*)zend_exception_get_default(), errMsg, 0 TSRMLS_CC); } }
static void notify_main_thread(sp_session *session) { // fprintf(stderr, "----------> notify_main_thread\n"); if(!session_constructed) return; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); Session *psession = (Session *)PyObject_CallObject((PyObject *)&SessionType, NULL); Py_INCREF(psession); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); if(client != NULL) { PyObject_CallMethod(client, "wake", "O", psession); } Py_DECREF(psession); PyGILState_Release(gstate); }
static int music_delivery(sp_session * session, const sp_audioformat * format, const void *frames, int num_frames) { /* TODO: This is called _all_ the time, make it faster? */ // Note that we do not try to shoe horn this into session_callback as it is // quite different in that this needs to handle return values and much more // complicated arguments. debug_printf(">> music_delivery called: frames %d", num_frames); int consumed = num_frames; // assume all consumed int size = frame_size(format); PyObject *callback, *client, *py_frames, *py_session, *result; PyGILState_STATE gstate = PyGILState_Ensure(); /* TODO: check if session creations succeeds. */ py_frames = PyBuffer_FromMemory((void *)frames, num_frames * size); py_session = Session_FromSpotify(session); /* TODO: check if callback get succeeds. */ client = (PyObject *)sp_session_userdata(session); callback = PyObject_GetAttrString(client, "music_delivery"); result = PyObject_CallFunction(callback, "NNiiiii", py_session, py_frames, size, num_frames, format->sample_type, format->sample_rate, format->channels); if (result == NULL) PyErr_WriteUnraisable(callback); else { if (PyInt_Check(result)) consumed = (int)PyInt_AsLong(result); else if (PyLong_Check(result)) consumed = (int)PyLong_AsLong(result); else { PyErr_SetString(PyExc_TypeError, "music_delivery must return an integer"); PyErr_WriteUnraisable(callback); } Py_DECREF(result); } Py_XDECREF(callback); PyGILState_Release(gstate); return consumed; }
// // -- Spotify callbacks --------------------------------------------------------------------------- // static void logged_in_callback(sp_session *session, sp_error error) { TRACE("logged_in_callback\n"); struct owl_state* state = sp_session_userdata(session); if(error == SP_ERROR_OK) { state->state = OWL_STATE_IDLE; INFO("Logged in to Spotify!\n"); respond_success(state->http_request); } else { state->state = OWL_STATE_INITIALIZED; ERROR("Failed to login to Spotify: %s\n", sp_error_message(error)); respond_error(state->http_request, OWL_HTTP_ERROR_LOGIN, sp_error_message(error)); } }
static void logged_in(sp_session *session, sp_error error) { if (error != SP_ERROR_OK) { fprintf(stderr, "%s\n", sp_error_message(error)); exit_status = EXIT_FAILURE; logged_out(session); return; } struct state *state = sp_session_userdata(session); state->session = session; evsignal_add(state->sigint, NULL); sp_playlistcontainer *pc = sp_session_playlistcontainer(session); sp_playlistcontainer_add_callbacks(pc, &playlistcontainer_callbacks, session); }
/** loggedout callback will relogin if true **/ void SpotifySession::loggedOut(sp_session *session) { SpotifySession* _session = reinterpret_cast<SpotifySession*>(sp_session_userdata(session)); _session->setLoggedIn( false ); qDebug() << "Logging out"; /// @note: This will login the user after previous user /// was properly logged out. if(_session->m_relogin) { _session->m_relogin = false; _session->login( _session->m_username, _session->m_password, _session->m_blob.constData() ); } }
static void playlistcontainer_loaded(sp_playlistcontainer *pc, void *userdata) { syslog(LOG_DEBUG, "playlistcontainer_loaded\n"); sp_session *session = userdata; struct state *state = sp_session_userdata(session); sp_playlistcontainer_remove_callbacks(pc, &playlistcontainer_callbacks, session); state->http = evhttp_new(state->event_base); evhttp_set_timeout(state->http, 60); evhttp_set_gencb(state->http, &handle_request, state); // TODO(liesen): Make address and port configurable if (evhttp_bind_socket(state->http, "0.0.0.0", 1337) == -1) { syslog(LOG_WARNING, "Could not bind HTTP server socket"); sp_session_logout(session); } }
void logged_in(sp_session *session, sp_error error) { struct state *state = sp_session_userdata(session); if (error != SP_ERROR_OK) { syslog(LOG_CRIT, "Error logging in to Spotify: %s", sp_error_message(error)); state->exit_status = EXIT_FAILURE; logged_out(session); return; } state->session = session; evsignal_add(state->sigint, NULL); sp_playlistcontainer *pc = sp_session_playlistcontainer(session); sp_playlistcontainer_add_callbacks(pc, &playlistcontainer_callbacks, session); }
/** loggedin callback from spotify also initilizes the playlistcontainer and callbacks **/ void SpotifySession::loggedIn(sp_session *session, sp_error error) { SpotifySession* _session = reinterpret_cast<SpotifySession*>(sp_session_userdata(session)); if (error == SP_ERROR_OK) { qDebug() << "Logged in successfully!!"; _session->setSession(session); _session->setLoggedIn(true); _session->setPlaylistContainer( sp_session_playlistcontainer(session) ); sp_playlistcontainer_add_ref( _session->PlaylistContainer() ); sp_playlistcontainer_add_callbacks(_session->PlaylistContainer(), &SpotifyCallbacks::containerCallbacks, _session); } qDebug() << Q_FUNC_INFO << "==== " << sp_error_message( error ) << " ===="; const QString msg = QString::fromUtf8( sp_error_message( error ) ); emit _session->loginResponse( error == SP_ERROR_OK, msg ); }
static int music_delivery(sp_session *session, const sp_audioformat *format, const void *frames, int num_frames) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); int siz = frame_size(format); PyObject *pyframes = PyBuffer_FromMemory((void *)frames, num_frames * siz); Py_INCREF(pyframes); Session *psession = (Session *)PyObject_CallObject((PyObject *)&SessionType, NULL); Py_INCREF(psession); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); PyObject *c= PyObject_CallMethod(client, "music_delivery", "OOiiiii", psession, pyframes, siz, num_frames, format->sample_type, format->sample_rate, format->channels); int consumed = num_frames; // assume all consumed if(PyObject_TypeCheck(c, &PyInt_Type)) { consumed = (int)PyInt_AsLong(c); } Py_DECREF(pyframes); Py_DECREF(psession); PyGILState_Release(gstate); return consumed; }
static int music_delivery(sp_session * session, const sp_audioformat * format, const void *frames, int num_frames) { PyGILState_STATE gstate; PyObject *res, *method; #ifdef DEBUG fprintf(stderr, "[DEBUG]-session- >> music_delivery called\n"); #endif gstate = PyGILState_Ensure(); int siz = frame_size(format); PyObject *pyframes = PyBuffer_FromMemory((void *)frames, num_frames * siz); Session *psession = (Session *) PyObject_CallObject((PyObject *)&SessionType, NULL); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); method = PyObject_GetAttrString(client, "music_delivery"); res = PyObject_CallFunction(method, "OOiiiii", psession, pyframes, siz, num_frames, format->sample_type, format->sample_rate, format->channels); int consumed = num_frames; // assume all consumed if (!res) PyErr_WriteUnraisable(method); if (PyInt_Check(res)) consumed = (int)PyInt_AsLong(res); else if (PyLong_Check(res)) consumed = (int)PyLong_AsLong(res); else { PyErr_SetString(PyExc_TypeError, "music_delivery must return an integer"); PyErr_WriteUnraisable(method); } Py_DECREF(pyframes); Py_DECREF(psession); Py_XDECREF(res); Py_DECREF(method); PyGILState_Release(gstate); return consumed; }
void credentials_blob_updated(sp_session *session, const char *blob) { syslog(LOG_DEBUG, "credentials_blob_updated"); struct state *state = sp_session_userdata(session); if (state->credentials_blob_filename == NULL) { syslog(LOG_DEBUG, "Not configured to store credentials"); return; } FILE *fp = fopen(state->credentials_blob_filename, "w+"); if (!fp) { syslog(LOG_DEBUG, "Could not open credentials file for writing"); return; } size_t blob_size = strlen(blob); fwrite(blob, 1, blob_size, fp); fclose(fp); syslog(LOG_DEBUG, "Wrote new credentials to %s", state->credentials_blob_filename); }
/* TODO: convert to Py_VaBuildValue based solution so we can support * music_delivery? * TODO: could we avoid having to pass session into the python callbacks, or * could we at least store a g_py_session to save us reconstructing it all * the time? Or would that break in unexpected ways if the session gets * modified? Measuring the affect of changing say music_delivery to not * waste time contructing the session would be a good place to start. * TODO: could we avoid having to lookup the attr for the callback on every * single callback? Would that break cases where people change the config * later, does that matter? */ static void session_callback(sp_session * session, const char *attr, PyObject *extra) { PyObject *callback, *client, *py_session, *result; py_session = Session_FromSpotify(session); if (py_session != NULL) { client = (PyObject *)sp_session_userdata(session); callback = PyObject_GetAttrString(client, attr); if (callback != NULL) { result = PyObject_CallFunctionObjArgs(callback, py_session, extra, NULL); if (result == NULL) PyErr_WriteUnraisable(callback); else Py_DECREF(result); Py_DECREF(callback); } Py_DECREF(py_session); } }
static void play_token_lost(sp_session * session) { PyGILState_STATE gstate; PyObject *res, *method; #ifdef DEBUG fprintf(stderr, "[DEBUG]-session- >> play_token_lost called\n"); #endif gstate = PyGILState_Ensure(); Session *psession = (Session *) PyObject_CallObject((PyObject *)&SessionType, NULL); psession->_session = session; PyObject *client = (PyObject *)sp_session_userdata(session); method = PyObject_GetAttrString(client, "play_token_lost"); res = PyObject_CallFunctionObjArgs(method, psession, NULL); if (!res) PyErr_WriteUnraisable(method); Py_DECREF(psession); Py_XDECREF(res); Py_DECREF(method); PyGILState_Release(gstate); }
/** sp_session_userdata is assumed to be thread safe. */ SpotifySession *from(sp_session *sess) { return static_cast<SpotifySession *>(sp_session_userdata(sess)); }