summaryrefslogtreecommitdiff
path: root/src/libpiano/piano.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpiano/piano.c')
-rw-r--r--src/libpiano/piano.c1146
1 files changed, 642 insertions, 504 deletions
diff --git a/src/libpiano/piano.c b/src/libpiano/piano.c
index 342e4ec..0ac96b6 100644
--- a/src/libpiano/piano.c
+++ b/src/libpiano/piano.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2008-2011
+Copyright (c) 2008-2012
Lars-Dominik Braun <lars@6xq.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -32,20 +32,17 @@ THE SOFTWARE.
#include <time.h>
#include <assert.h>
#include <stdint.h>
+#include <json.h>
/* needed for urlencode */
#include <waitress.h>
#include "piano_private.h"
#include "piano.h"
-#include "xml.h"
#include "crypt.h"
#include "config.h"
-#define PIANO_PROTOCOL_VERSION "33"
-#define PIANO_RPC_HOST "www.pandora.com"
-#define PIANO_RPC_PORT "80"
-#define PIANO_RPC_PATH "/radio/xmlrpc/v" PIANO_PROTOCOL_VERSION "?"
+#define PIANO_RPC_PATH "/services/json/?"
#define PIANO_SEND_BUFFER_SIZE 10000
/* initialize piano handle
@@ -54,10 +51,6 @@ THE SOFTWARE.
*/
void PianoInit (PianoHandle_t *ph) {
memset (ph, 0, sizeof (*ph));
-
- /* route-id seems to be random. we're using time anyway... */
- snprintf (ph->routeId, sizeof (ph->routeId), "%07luP",
- (unsigned long) time (NULL) % 10000000);
}
/* destroy artist linked list
@@ -125,10 +118,8 @@ void PianoDestroyPlaylist (PianoSong_t *playlist) {
free (curSong->artist);
free (curSong->musicId);
free (curSong->title);
- free (curSong->userSeed);
free (curSong->stationId);
free (curSong->album);
- free (curSong->artistMusicId);
free (curSong->feedbackId);
free (curSong->seedId);
free (curSong->detailUrl);
@@ -164,7 +155,6 @@ static void PianoDestroyGenres (PianoGenre_t *genres) {
/* destroy user information
*/
static void PianoDestroyUserInfo (PianoUserInfo_t *user) {
- free (user->webAuthToken);
free (user->authToken);
free (user->listenerId);
}
@@ -185,6 +175,7 @@ void PianoDestroy (PianoHandle_t *ph) {
curGenreCat = curGenreCat->next;
free (lastGenreCat);
}
+ free (ph->partnerAuthToken);
memset (ph, 0, sizeof (*ph));
}
@@ -228,9 +219,12 @@ static const char *PianoAudioFormatToString (PianoAudioFormat_t format) {
*/
PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
PianoRequestType_t type) {
- char xmlSendBuf[PIANO_SEND_BUFFER_SIZE];
+ const char *jsonSendBuf;
+ const char *method = NULL;
+ json_object *j = json_object_new_object ();
/* corrected timestamp */
time_t timestamp = time (NULL) - ph->timeOffset;
+ bool encrypted = true;
assert (ph != NULL);
assert (req != NULL);
@@ -248,66 +242,59 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
switch (logindata->step) {
case 0:
- snprintf (xmlSendBuf, sizeof (xmlSendBuf),
- "<?xml version=\"1.0\"?><methodCall>"
- "<methodName>misc.sync</methodName>"
- "<params></params></methodCall>");
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&method=sync", ph->routeId);
+ encrypted = false;
+ req->secure = true;
+
+ json_object_object_add (j, "username",
+ json_object_new_string ("android"));
+ json_object_object_add (j, "password",
+ json_object_new_string ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7"));
+ json_object_object_add (j, "deviceModel",
+ json_object_new_string ("android-generic"));
+ json_object_object_add (j, "version",
+ json_object_new_string ("5"));
+ json_object_object_add (j, "includeUrls",
+ json_object_new_boolean (true));
+ snprintf (req->urlPath, sizeof (req->urlPath),
+ PIANO_RPC_PATH "method=auth.partnerLogin");
break;
case 1: {
- char *xmlencodedPassword = NULL;
+ char *urlencAuthToken;
req->secure = true;
- /* username == email address does not contain &,<,>," */
- if ((xmlencodedPassword =
- PianoXmlEncodeString (logindata->password)) ==
- NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
+ json_object_object_add (j, "loginType",
+ json_object_new_string ("user"));
+ json_object_object_add (j, "username",
+ json_object_new_string (logindata->user));
+ json_object_object_add (j, "password",
+ json_object_new_string (logindata->password));
+ json_object_object_add (j, "partnerAuthToken",
+ json_object_new_string (ph->partnerAuthToken));
+ json_object_object_add (j, "syncTime",
+ json_object_new_int (timestamp));
+
+ urlencAuthToken = WaitressUrlEncode (ph->partnerAuthToken);
+ assert (urlencAuthToken != NULL);
+ snprintf (req->urlPath, sizeof (req->urlPath),
+ PIANO_RPC_PATH "method=auth.userLogin&"
+ "auth_token=%s&partner_id=%i", urlencAuthToken,
+ ph->partnerId);
+ free (urlencAuthToken);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf),
- "<?xml version=\"1.0\"?><methodCall>"
- "<methodName>listener.authenticateListener</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* user */
- "<param><value><string>%s</string></value></param>"
- /* password */
- "<param><value><string>%s</string></value></param>"
- /* vendor */
- "<param><value><string>html5tuner</string></value></param>"
- "<param><value><string/></value></param>"
- "<param><value><string/></value></param>"
- "<param><value><string>HTML5</string></value></param>"
- "<param><value><boolean>1</boolean></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- logindata->user, xmlencodedPassword);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&method=authenticateListener", ph->routeId);
-
- free (xmlencodedPassword);
break;
}
}
break;
}
- case PIANO_REQUEST_GET_STATIONS:
+ case PIANO_REQUEST_GET_STATIONS: {
/* get stations, user must be authenticated */
assert (ph->user.listenerId != NULL);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.getStations</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getStations", ph->routeId,
- ph->user.listenerId);
+ method = "user.getStationList";
break;
+ }
case PIANO_REQUEST_GET_PLAYLIST: {
/* get playlist for specified station */
@@ -318,33 +305,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData->station->id != NULL);
assert (reqData->format != PIANO_AF_UNKNOWN);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>playlist.getFragment</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* total listening time */
- "<param><value><string>0</string></value></param>"
- /* time since last session */
- "<param><value><string></string></value></param>"
- /* tracking code */
- "<param><value><string></string></value></param>"
- /* audio format */
- "<param><value><string>%s</string></value></param>"
- /* delta listening time */
- "<param><value><string>0</string></value></param>"
- /* listening timestamp */
- "<param><value><string>0</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id,
- PianoAudioFormatToString (reqData->format));
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getFragment&arg1=%s&arg2=0"
- "&arg3=&arg4=&arg5=%s&arg6=0&arg7=0", ph->routeId,
- ph->user.listenerId, reqData->station->id,
- PianoAudioFormatToString (reqData->format));
+ req->secure = true;
+
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+
+ method = "station.getPlaylist";
break;
}
@@ -353,65 +319,31 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
PianoRequestDataAddFeedback_t *reqData = req->data;
assert (reqData != NULL);
- assert (reqData->stationId != NULL);
+ assert (reqData->trackToken != NULL);
assert (reqData->rating != PIANO_RATE_NONE);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.addFeedback</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* track token */
- "<param><value><string>%s</string></value></param>"
- /* positive */
- "<param><value><boolean>%i</boolean></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->stationId, reqData->trackToken,
- (reqData->rating == PIANO_RATE_LOVE) ? 1 : 0);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addFeedback&arg1=%s&arg2=%s"
- "&arg3=%s",
- ph->routeId, ph->user.listenerId, reqData->stationId,
- reqData->trackToken,
- (reqData->rating == PIANO_RATE_LOVE) ? "true" : "false");
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (reqData->trackToken));
+ json_object_object_add (j, "isPositive",
+ json_object_new_boolean (reqData->rating == PIANO_RATE_LOVE));
+
+ method = "station.addFeedback";
break;
}
case PIANO_REQUEST_RENAME_STATION: {
- /* rename stations */
PianoRequestDataRenameStation_t *reqData = req->data;
- char *urlencodedNewName, *xmlencodedNewName;
assert (reqData != NULL);
assert (reqData->station != NULL);
assert (reqData->newName != NULL);
- if ((xmlencodedNewName = PianoXmlEncodeString (reqData->newName)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
- urlencodedNewName = WaitressUrlEncode (reqData->newName);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.setStationName</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* new name */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id,
- xmlencodedNewName);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=setStationName&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, reqData->station->id,
- urlencodedNewName);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+ json_object_object_add (j, "stationName",
+ json_object_new_string (reqData->newName));
- free (urlencodedNewName);
- free (xmlencodedNewName);
+ method = "station.renameStation";
break;
}
@@ -420,50 +352,26 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
PianoStation_t *station = req->data;
assert (station != NULL);
+ assert (station->id != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.removeStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=removeStation&arg1=%s", ph->routeId,
- ph->user.listenerId, station->id);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (station->id));
+
+ method = "station.deleteStation";
break;
}
case PIANO_REQUEST_SEARCH: {
/* search for artist/song title */
PianoRequestDataSearch_t *reqData = req->data;
- char *xmlencodedSearchStr, *urlencodedSearchStr;
assert (reqData != NULL);
assert (reqData->searchStr != NULL);
- if ((xmlencodedSearchStr = PianoXmlEncodeString (reqData->searchStr)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
- }
- urlencodedSearchStr = WaitressUrlEncode (reqData->searchStr);
-
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>music.search</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* search string */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, xmlencodedSearchStr);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=search&arg1=%s", ph->routeId,
- ph->user.listenerId, urlencodedSearchStr);
+ json_object_object_add (j, "searchText",
+ json_object_new_string (reqData->searchStr));
- free (urlencodedSearchStr);
- free (xmlencodedSearchStr);
+ method = "music.search";
break;
}
@@ -474,23 +382,11 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData != NULL);
assert (reqData->id != NULL);
- assert (reqData->type != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s%s</string></value></param>"
- /* empty */
- "<param><value><string></string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->type, reqData->id);
+ json_object_object_add (j, "musicToken",
+ json_object_new_string (reqData->id));
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createStation&arg1=%s%s&arg2=", ph->routeId,
- ph->user.listenerId, reqData->type, reqData->id);
+ method = "station.createStation";
break;
}
@@ -502,20 +398,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData->station != NULL);
assert (reqData->musicId != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.addSeed</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id, reqData->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addSeed&arg1=%s&arg2=%s", ph->routeId,
- ph->user.listenerId, reqData->station->id, reqData->musicId);
+ json_object_object_add (j, "musicToken",
+ json_object_new_string (reqData->musicId));
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+
+ method = "station.addMusic";
break;
}
@@ -525,87 +413,40 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>listener.addTiredSong</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- /* key */
- "<param><value><string>%s</string></value></param>"
- /* user seed */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken,
- (song->musicId == NULL) ? "" : song->musicId,
- (song->userSeed == NULL) ? "" : song->userSeed,
- song->stationId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=addTiredSong&arg1=%s&arg2=%s&arg3=%s",
- ph->routeId, ph->user.listenerId,
- (song->musicId == NULL) ? "" : song->musicId,
- (song->userSeed == NULL) ? "" : song->userSeed,
- song->stationId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (song->trackToken));
+
+ method = "user.sleepSong";
break;
}
case PIANO_REQUEST_SET_QUICKMIX: {
/* select stations included in quickmix (see useQuickMix flag of
* PianoStation_t) */
- char valueBuf[1000], urlArgBuf[1000];
PianoStation_t *curStation = ph->stations;
+ json_object *a = json_object_new_array ();
- memset (urlArgBuf, 0, sizeof (urlArgBuf));
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.setQuickMix</methodName><params>"
- "<param><value><int>%lu</int></value></param>"
- "<param><value><string>%s</string></value></param>"
- /* quick mix type */
- "<param><value><string>RANDOM</string></value></param>"
- "<param><value><array><data>", (unsigned long) timestamp,
- ph->user.authToken);
while (curStation != NULL) {
/* quick mix can't contain itself */
- if (!curStation->useQuickMix || curStation->isQuickMix) {
- curStation = curStation->next;
- continue;
+ if (curStation->useQuickMix && !curStation->isQuickMix) {
+ json_object_array_add (a,
+ json_object_new_string (curStation->id));
}
- /* append to xml doc */
- snprintf (valueBuf, sizeof (valueBuf),
- "<value><string>%s</string></value>", curStation->id);
- strncat (xmlSendBuf, valueBuf, sizeof (xmlSendBuf) -
- strlen (xmlSendBuf) - 1);
- /* append to url arg */
- strncat (urlArgBuf, curStation->id, sizeof (urlArgBuf) -
- strlen (urlArgBuf) - 1);
+
curStation = curStation->next;
- /* if not last item: append "," */
- if (curStation != NULL) {
- strncat (urlArgBuf, "%2C", sizeof (urlArgBuf) -
- strlen (urlArgBuf) - 1);
- }
}
- strncat (xmlSendBuf,
- "</data></array></value></param>"
- /* empty */
- "<param><value><string></string></value></param>"
- /* empty */
- "<param><value><string></string></value></param>"
- "</params></methodCall>",
- sizeof (xmlSendBuf) - strlen (xmlSendBuf) - 1);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=setQuickMix&arg1=RANDOM&arg2=%s&arg3=&arg4=",
- ph->routeId, ph->user.listenerId, urlArgBuf);
+ json_object_object_add (j, "quickMixStationIds", a);
+
+ method = "user.setQuickMix";
break;
}
- case PIANO_REQUEST_GET_GENRE_STATIONS:
+ case PIANO_REQUEST_GET_GENRE_STATIONS: {
/* receive list of pandora's genre stations */
- xmlSendBuf[0] = '\0';
- snprintf (req->urlPath, sizeof (req->urlPath), "/xml/genre?r=%lu",
- (unsigned long) timestamp);
+ method = "station.getGenreStations";
break;
+ }
case PIANO_REQUEST_TRANSFORM_STATION: {
/* transform shared station into private */
@@ -613,18 +454,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (station != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.transformShared</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=transformShared&arg1=%s", ph->routeId,
- ph->user.listenerId, station->id);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (station->id));
+
+ method = "station.transformSharedStation";
break;
}
@@ -635,26 +468,15 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData != NULL);
assert (reqData->song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>playlist.narrative</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->song->stationId,
- reqData->song->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=narrative&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, reqData->song->stationId,
- reqData->song->musicId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (reqData->song->trackToken));
+
+ method = "track.explainTrack";
break;
}
case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
+#if 0
/* find similar artists */
PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
@@ -679,6 +501,7 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
"rid=%s&lid=%s&method=getSeedSuggestions&arg1=%s&arg2=%u",
ph->routeId, ph->user.listenerId, reqData->musicId, reqData->max);
+#endif
break;
}
@@ -688,21 +511,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createBookmark</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->stationId, song->musicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createBookmark&arg1=%s&arg2=%s",
- ph->routeId, ph->user.listenerId, song->stationId,
- song->musicId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (song->trackToken));
+
+ method = "bookmark.addSongBookmark";
break;
}
@@ -712,18 +524,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.createArtistBookmark</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* music id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->artistMusicId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=createArtistBookmark&arg1=%s",
- ph->routeId, ph->user.listenerId, song->artistMusicId);
+ json_object_object_add (j, "trackToken",
+ json_object_new_string (song->trackToken));
+
+ method = "bookmark.addArtistBookmark";
break;
}
@@ -734,18 +538,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (reqData != NULL);
assert (reqData->station != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.getStation</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* station id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, reqData->station->id);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=getStation&arg1=%s",
- ph->routeId, ph->user.listenerId, reqData->station->id);
+ json_object_object_add (j, "stationToken",
+ json_object_new_string (reqData->station->id));
+ json_object_object_add (j, "includeExtendedAttributes",
+ json_object_new_boolean (true));
+
+ method = "station.getStation";
break;
}
@@ -754,18 +552,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (song != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.deleteFeedback</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* feedback id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, song->feedbackId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=deleteFeedback&arg1=%s",
- ph->routeId, ph->user.listenerId, song->feedbackId);
+ json_object_object_add (j, "feedbackId",
+ json_object_new_string (song->feedbackId));
+
+ method = "station.deleteFeedback";
break;
}
@@ -787,18 +577,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
assert (seedId != NULL);
- snprintf (xmlSendBuf, sizeof (xmlSendBuf), "<?xml version=\"1.0\"?>"
- "<methodCall><methodName>station.deleteSeed</methodName>"
- "<params><param><value><int>%lu</int></value></param>"
- /* auth token */
- "<param><value><string>%s</string></value></param>"
- /* seed id */
- "<param><value><string>%s</string></value></param>"
- "</params></methodCall>", (unsigned long) timestamp,
- ph->user.authToken, seedId);
- snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
- "rid=%s&lid=%s&method=deleteSeed&arg1=%s",
- ph->routeId, ph->user.listenerId, seedId);
+ json_object_object_add (j, "seedId",
+ json_object_new_string (seedId));
+
+ method = "station.deleteMusic";
break;
}
@@ -866,24 +648,89 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
}
}
- if ((req->postData = PianoEncryptString (xmlSendBuf)) == NULL) {
- return PIANO_RET_OUT_OF_MEMORY;
+ /* standard parameter */
+ if (method != NULL) {
+ char *urlencAuthToken;
+
+ assert (ph->user.authToken != NULL);
+
+ urlencAuthToken = WaitressUrlEncode (ph->user.authToken);
+ assert (urlencAuthToken != NULL);
+
+ snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH
+ "method=%s&auth_token=%s&partner_id=%i&user_id=%s", method,
+ urlencAuthToken, ph->partnerId, ph->user.listenerId);
+
+ free (urlencAuthToken);
+
+ json_object_object_add (j, "userAuthToken",
+ json_object_new_string (ph->user.authToken));
+ json_object_object_add (j, "syncTime",
+ json_object_new_int (timestamp));
+ }
+
+ /* json to string */
+ jsonSendBuf = json_object_to_json_string (j);
+ if (encrypted) {
+ if ((req->postData = PianoEncryptString (jsonSendBuf)) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+ } else {
+ req->postData = strdup (jsonSendBuf);
}
+ json_object_put (j);
return PIANO_RET_OK;
}
+static char *PianoJsonStrdup (json_object *j, const char *key) {
+ return strdup (json_object_get_string (json_object_object_get (j, key)));
+}
+
+static void PianoJsonParseStation (json_object *j, PianoStation_t *s) {
+ s->name = PianoJsonStrdup (j, "stationName");
+ s->id = PianoJsonStrdup (j, "stationToken");
+ s->isCreator = !json_object_get_boolean (json_object_object_get (j,
+ "isShared"));
+ s->isQuickMix = json_object_get_boolean (json_object_object_get (j,
+ "isQuickMix"));
+}
+
/* parse xml response and update data structures/return new data structure
* @param piano handle
* @param initialized request (expects responseData to be a NUL-terminated
* string)
*/
PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
- PianoReturn_t ret = PIANO_RET_ERR;
+ PianoReturn_t ret = PIANO_RET_OK;
+ json_object *j, *result, *status;
assert (ph != NULL);
assert (req != NULL);
+ j = json_tokener_parse (req->responseData);
+
+ status = json_object_object_get (j, "stat");
+ if (status == NULL) {
+ json_object_put (j);
+ return PIANO_RET_INVALID_RESPONSE;
+ }
+
+ /* error handling */
+ if (strcmp (json_object_get_string (status), "ok") != 0) {
+ json_object *code = json_object_object_get (j, "code");
+ if (code == NULL) {
+ ret = PIANO_RET_INVALID_RESPONSE;
+ } else {
+ ret = json_object_get_int (code)+PIANO_RET_OFFSET;
+ }
+
+ json_object_put (j);
+ return ret;
+ }
+
+ result = json_object_object_get (j, "result");
+
switch (req->type) {
case PIANO_REQUEST_LOGIN: {
/* authenticate user */
@@ -894,29 +741,28 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
switch (reqData->step) {
case 0: {
- char *cryptedTimestamp = NULL;
-
- assert (req->responseData != NULL);
-
- /* abusing parseNarrative; has same xml structure */
- ret = PianoXmlParseNarrative (req->responseData, &cryptedTimestamp);
- if (ret == PIANO_RET_OK && cryptedTimestamp != NULL) {
- unsigned long timestamp = 0;
- const time_t realTimestamp = time (NULL);
- char *decryptedTimestamp = NULL;
- size_t decryptedSize;
-
- ret = PIANO_RET_ERR;
- if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp,
- &decryptedSize)) != NULL && decryptedSize > 4) {
- /* skip four bytes garbage(?) at beginning */
- timestamp = strtoul (decryptedTimestamp+4, NULL, 0);
- ph->timeOffset = realTimestamp - timestamp;
- ret = PIANO_RET_CONTINUE_REQUEST;
- }
- free (decryptedTimestamp);
+ /* decrypt timestamp */
+ const char *cryptedTimestamp = json_object_get_string (
+ json_object_object_get (result, "syncTime"));
+ unsigned long timestamp = 0;
+ const time_t realTimestamp = time (NULL);
+ char *decryptedTimestamp = NULL;
+ size_t decryptedSize;
+
+ ret = PIANO_RET_ERR;
+ if ((decryptedTimestamp = PianoDecryptString (cryptedTimestamp,
+ &decryptedSize)) != NULL && decryptedSize > 4) {
+ /* skip four bytes garbage(?) at beginning */
+ timestamp = strtoul (decryptedTimestamp+4, NULL, 0);
+ ph->timeOffset = realTimestamp - timestamp;
+ ret = PIANO_RET_CONTINUE_REQUEST;
}
- free (cryptedTimestamp);
+ free (decryptedTimestamp);
+ /* get auth token */
+ ph->partnerAuthToken = PianoJsonStrdup (result,
+ "partnerAuthToken");
+ ph->partnerId = json_object_get_int (
+ json_object_object_get (result, "partnerId"));
++reqData->step;
break;
}
@@ -927,48 +773,145 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
if (ph->user.listenerId != NULL) {
PianoDestroyUserInfo (&ph->user);
}
- ret = PianoXmlParseUserinfo (ph, req->responseData);
+ ph->user.listenerId = PianoJsonStrdup (result, "userId");
+ ph->user.authToken = PianoJsonStrdup (result,
+ "userAuthToken");
break;
}
break;
}
- case PIANO_REQUEST_GET_STATIONS:
+ case PIANO_REQUEST_GET_STATIONS: {
/* get stations */
assert (req->responseData != NULL);
-
- ret = PianoXmlParseStations (ph, req->responseData);
+
+ json_object *stations = json_object_object_get (result,
+ "stations"), *mix = NULL;
+
+ for (size_t i=0; i < json_object_array_length (stations); i++) {
+ PianoStation_t *tmpStation;
+ json_object *s = json_object_array_get_idx (stations, i);
+
+ if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ PianoJsonParseStation (s, tmpStation);
+
+ if (tmpStation->isQuickMix) {
+ /* fix flags on other stations later */
+ mix = json_object_object_get (s, "quickMixStationIds");
+ }
+
+ /* start new linked list or append */
+ if (ph->stations == NULL) {
+ ph->stations = tmpStation;
+ } else {
+ PianoStation_t *curStation = ph->stations;
+ while (curStation->next != NULL) {
+ curStation = curStation->next;
+ }
+ curStation->next = tmpStation;
+ }
+ }
+
+ /* fix quickmix flags */
+ if (mix != NULL) {
+ PianoStation_t *curStation = ph->stations;
+ while (curStation != NULL) {
+ for (size_t i = 0; i < json_object_array_length (mix); i++) {
+ json_object *id = json_object_array_get_idx (mix, i);
+ if (strcmp (json_object_get_string (id),
+ curStation->id) == 0) {
+ curStation->useQuickMix = true;
+ }
+ }
+ curStation = curStation->next;
+ }
+ }
break;
+ }
case PIANO_REQUEST_GET_PLAYLIST: {
/* get playlist, usually four songs */
PianoRequestDataGetPlaylist_t *reqData = req->data;
+ PianoSong_t *playlist = NULL;
assert (req->responseData != NULL);
assert (reqData != NULL);
- reqData->retPlaylist = NULL;
- ret = PianoXmlParsePlaylist (ph, req->responseData,
- &reqData->retPlaylist);
+ json_object *items = json_object_object_get (result, "items");
+ assert (items != NULL);
+
+ for (size_t i=0; i < json_object_array_length (items); i++) {
+ json_object *s = json_object_array_get_idx (items, i);
+ PianoSong_t *song;
+
+ if ((song = calloc (1, sizeof (*song))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ if (json_object_object_get (s, "artistName") == NULL) {
+ free (song);
+ continue;
+ }
+ song->audioUrl = strdup (json_object_get_string (json_object_object_get (json_object_object_get (json_object_object_get (s, "audioUrlMap"), "highQuality"), "audioUrl")));
+ song->artist = PianoJsonStrdup (s, "artistName");
+ song->album = PianoJsonStrdup (s, "albumName");
+ song->title = PianoJsonStrdup (s, "songName");
+ song->trackToken = PianoJsonStrdup (s, "trackToken");
+ song->stationId = PianoJsonStrdup (s, "stationId");
+ song->fileGain = json_object_get_double (
+ json_object_object_get (s, "trackGain"));
+ song->audioFormat = PIANO_AF_AACPLUS;
+ switch (json_object_get_int (json_object_object_get (s,
+ "songRating"))) {
+ case 1:
+ song->rating = PIANO_RATE_LOVE;
+ break;
+ }
+
+ /* begin linked list or append */
+ if (playlist == NULL) {
+ playlist = song;
+ } else {
+ PianoSong_t *curSong = playlist;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = song;
+ }
+ }
+
+ reqData->retPlaylist = playlist;
break;
}
- case PIANO_REQUEST_RATE_SONG:
+ case PIANO_REQUEST_RATE_SONG: {
/* love/ban song */
- assert (req->responseData != NULL);
-
- ret = PianoXmlParseSimple (req->responseData);
- if (ret == PIANO_RET_OK) {
- PianoRequestDataRateSong_t *reqData = req->data;
- reqData->song->rating = reqData->rating;
- }
+ PianoRequestDataRateSong_t *reqData = req->data;
+ reqData->song->rating = reqData->rating;
break;
+ }
case PIANO_REQUEST_ADD_FEEDBACK:
/* never ever use this directly, low-level call */
assert (0);
break;
+ case PIANO_REQUEST_RENAME_STATION: {
+ /* rename station and update PianoStation_t structure */
+ PianoRequestDataRenameStation_t *reqData = req->data;
+
+ assert (reqData != NULL);
+ assert (reqData->station != NULL);
+ assert (reqData->newName != NULL);
+
+ free (reqData->station->name);
+ reqData->station->name = strdup (reqData->newName);
+ break;
+ }
+
case PIANO_REQUEST_MOVE_SONG: {
/* move song to different station */
PianoRequestDataMoveSong_t *reqData = req->data;
@@ -977,107 +920,204 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (reqData != NULL);
assert (reqData->step < 2);
- ret = PianoXmlParseSimple (req->responseData);
- if (ret == PIANO_RET_OK && reqData->step == 0) {
+ if (reqData->step == 0) {
ret = PIANO_RET_CONTINUE_REQUEST;
++reqData->step;
}
break;
}
- case PIANO_REQUEST_RENAME_STATION:
- /* rename station and update PianoStation_t structure */
- assert (req->responseData != NULL);
-
- if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
- PianoRequestDataRenameStation_t *reqData = req->data;
+ case PIANO_REQUEST_DELETE_STATION: {
+ /* delete station from server and station list */
+ PianoStation_t *station = req->data;
- assert (reqData != NULL);
- assert (reqData->station != NULL);
- assert (reqData->newName != NULL);
+ assert (station != NULL);
- free (reqData->station->name);
- reqData->station->name = strdup (reqData->newName);
+ /* delete station from local station list */
+ PianoStation_t *curStation = ph->stations, *lastStation = NULL;
+ while (curStation != NULL) {
+ if (curStation == station) {
+ if (lastStation != NULL) {
+ lastStation->next = curStation->next;
+ } else {
+ /* first station in list */
+ ph->stations = curStation->next;
+ }
+ PianoDestroyStation (curStation);
+ free (curStation);
+ break;
+ }
+ lastStation = curStation;
+ curStation = curStation->next;
}
break;
+ }
+
+ case PIANO_REQUEST_SEARCH: {
+ /* search artist/song */
+ PianoRequestDataSearch_t *reqData = req->data;
+ PianoSearchResult_t *searchResult;
- case PIANO_REQUEST_DELETE_STATION:
- /* delete station from server and station list */
assert (req->responseData != NULL);
+ assert (reqData != NULL);
- if ((ret = PianoXmlParseSimple (req->responseData)) == PIANO_RET_OK) {
- PianoStation_t *station = req->data;
+ searchResult = &reqData->searchResult;
+ memset (searchResult, 0, sizeof (*searchResult));
- assert (station != NULL);
+ /* get artists */
+ json_object *artists = json_object_object_get (result, "artists");
+ if (artists != NULL) {
+ for (size_t i=0; i < json_object_array_length (artists); i++) {
+ json_object *a = json_object_array_get_idx (artists, i);
+ PianoArtist_t *artist;
- /* delete station from local station list */
- PianoStation_t *curStation = ph->stations, *lastStation = NULL;
- while (curStation != NULL) {
- if (curStation == station) {
- if (lastStation != NULL) {
- lastStation->next = curStation->next;
- } else {
- /* first station in list */
- ph->stations = curStation->next;
+ if ((artist = calloc (1, sizeof (*artist))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ artist->name = PianoJsonStrdup (a, "artistName");
+ artist->musicId = PianoJsonStrdup (a, "musicToken");
+
+ /* add result to linked list */
+ if (searchResult->artists == NULL) {
+ searchResult->artists = artist;
+ } else {
+ PianoArtist_t *curArtist = searchResult->artists;
+ while (curArtist->next != NULL) {
+ curArtist = curArtist->next;
}
- PianoDestroyStation (curStation);
- free (curStation);
- break;
+ curArtist->next = artist;
}
- lastStation = curStation;
- curStation = curStation->next;
}
}
- break;
- case PIANO_REQUEST_SEARCH: {
- /* search artist/song */
- PianoRequestDataSearch_t *reqData = req->data;
+ /* get songs */
+ json_object *songs = json_object_object_get (result, "songs");
+ if (songs != NULL) {
+ for (size_t i=0; i < json_object_array_length (songs); i++) {
+ json_object *s = json_object_array_get_idx (songs, i);
+ PianoSong_t *song;
- assert (req->responseData != NULL);
- assert (reqData != NULL);
+ if ((song = calloc (1, sizeof (*song))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- ret = PianoXmlParseSearch (req->responseData, &reqData->searchResult);
+ song->title = PianoJsonStrdup (s, "songName");
+ song->artist = PianoJsonStrdup (s, "artistName");
+ song->musicId = PianoJsonStrdup (s, "musicToken");
+
+ /* add result to linked list */
+ if (searchResult->songs == NULL) {
+ searchResult->songs = song;
+ } else {
+ PianoSong_t *curSong = searchResult->songs;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = song;
+ }
+ }
+ }
break;
}
case PIANO_REQUEST_CREATE_STATION: {
/* create station, insert new station into station list on success */
- assert (req->responseData != NULL);
+ PianoStation_t *tmpStation;
- ret = PianoXmlParseCreateStation (ph, req->responseData);
- break;
- }
-
- case PIANO_REQUEST_ADD_SEED: {
- /* add seed to station, updates station structure */
- PianoRequestDataAddSeed_t *reqData = req->data;
+ if ((tmpStation = calloc (1, sizeof (*tmpStation))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- assert (req->responseData != NULL);
- assert (reqData != NULL);
- assert (reqData->station != NULL);
+ PianoJsonParseStation (result, tmpStation);
- /* FIXME: update station data instead of replacing them */
- ret = PianoXmlParseAddSeed (ph, req->responseData, reqData->station);
+ /* start new linked list or append */
+ if (ph->stations == NULL) {
+ ph->stations = tmpStation;
+ } else {
+ PianoStation_t *curStation = ph->stations;
+ while (curStation->next != NULL) {
+ curStation = curStation->next;
+ }
+ curStation->next = tmpStation;
+ }
break;
}
+ case PIANO_REQUEST_ADD_SEED:
case PIANO_REQUEST_ADD_TIRED_SONG:
case PIANO_REQUEST_SET_QUICKMIX:
case PIANO_REQUEST_BOOKMARK_SONG:
case PIANO_REQUEST_BOOKMARK_ARTIST:
case PIANO_REQUEST_DELETE_FEEDBACK:
- assert (req->responseData != NULL);
-
- ret = PianoXmlParseSimple (req->responseData);
+ case PIANO_REQUEST_DELETE_SEED:
+ /* response unused */
break;
- case PIANO_REQUEST_GET_GENRE_STATIONS:
+ case PIANO_REQUEST_GET_GENRE_STATIONS: {
/* get genre stations */
- assert (req->responseData != NULL);
+ json_object *categories = json_object_object_get (result, "categories");
+ if (categories != NULL) {
+ for (size_t i = 0; i < json_object_array_length (categories); i++) {
+ json_object *c = json_object_array_get_idx (categories, i);
+ PianoGenreCategory_t *tmpGenreCategory;
+
+ if ((tmpGenreCategory = calloc (1,
+ sizeof (*tmpGenreCategory))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- ret = PianoXmlParseGenreExplorer (ph, req->responseData);
+ tmpGenreCategory->name = PianoJsonStrdup (c,
+ "categoryName");
+
+ /* get genre subnodes */
+ json_object *stations = json_object_object_get (c,
+ "stations");
+ if (stations != NULL) {
+ for (size_t k = 0;
+ k < json_object_array_length (stations); k++) {
+ json_object *s =
+ json_object_array_get_idx (stations, k);
+ PianoGenre_t *tmpGenre;
+
+ if ((tmpGenre = calloc (1,
+ sizeof (*tmpGenre))) == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ /* get genre attributes */
+ tmpGenre->name = PianoJsonStrdup (s,
+ "stationName");
+ tmpGenre->musicId = PianoJsonStrdup (s,
+ "stationToken");
+
+ /* append station */
+ if (tmpGenreCategory->genres == NULL) {
+ tmpGenreCategory->genres = tmpGenre;
+ } else {
+ PianoGenre_t *curGenre =
+ tmpGenreCategory->genres;
+ while (curGenre->next != NULL) {
+ curGenre = curGenre->next;
+ }
+ curGenre->next = tmpGenre;
+ }
+ }
+ }
+ /* append category */
+ if (ph->genreStations == NULL) {
+ ph->genreStations = tmpGenreCategory;
+ } else {
+ PianoGenreCategory_t *curCat = ph->genreStations;
+ while (curCat->next != NULL) {
+ curCat = curCat->next;
+ }
+ curCat->next = tmpGenreCategory;
+ }
+ }
+ }
break;
+ }
case PIANO_REQUEST_TRANSFORM_STATION: {
/* transform shared station into private and update isCreator flag */
@@ -1086,27 +1126,51 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (req->responseData != NULL);
assert (station != NULL);
- /* though this call returns a bunch of "new" data only this one is
- * changed and important (at the moment) */
- if ((ret = PianoXmlParseTranformStation (req->responseData)) ==
- PIANO_RET_OK) {
- station->isCreator = 1;
- }
+ station->isCreator = 1;
break;
}
case PIANO_REQUEST_EXPLAIN: {
/* explain why song was selected */
PianoRequestDataExplain_t *reqData = req->data;
+ const size_t strSize = 1024;
+ size_t pos = 0;
- assert (req->responseData != NULL);
assert (reqData != NULL);
- ret = PianoXmlParseNarrative (req->responseData, &reqData->retExplain);
+ json_object *explanations = json_object_object_get (result,
+ "explanations");
+ if (explanations != NULL) {
+ reqData->retExplain = malloc (strSize *
+ sizeof (*reqData->retExplain));
+ strncpy (reqData->retExplain, "We're playing this track "
+ "because it features ", strSize);
+ pos = strlen (reqData->retExplain);
+ for (size_t i=0; i < json_object_array_length (explanations); i++) {
+ json_object *e = json_object_array_get_idx (explanations,
+ i);
+ const char *s = json_object_get_string (
+ json_object_object_get (e, "focusTraitName"));
+
+ strncpy (&reqData->retExplain[pos], s, strSize-pos-1);
+ pos += strlen (s);
+ if (i < json_object_array_length (explanations)-2) {
+ strncpy (&reqData->retExplain[pos], ", ", strSize-pos-1);
+ pos += 2;
+ } else if (i == json_object_array_length (explanations)-2) {
+ strncpy (&reqData->retExplain[pos], " and ", strSize-pos-1);
+ pos += 5;
+ } else {
+ strncpy (&reqData->retExplain[pos], ".", strSize-pos-1);
+ pos += 1;
+ }
+ }
+ }
break;
}
case PIANO_REQUEST_GET_SEED_SUGGESTIONS: {
+#if 0
/* find similar artists */
PianoRequestDataGetSeedSuggestions_t *reqData = req->data;
@@ -1115,29 +1179,122 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
ret = PianoXmlParseSeedSuggestions (req->responseData,
&reqData->searchResult);
+#endif
break;
}
case PIANO_REQUEST_GET_STATION_INFO: {
/* get station information (seeds and feedback) */
PianoRequestDataGetStationInfo_t *reqData = req->data;
+ PianoStationInfo_t *info;
- assert (req->responseData != NULL);
assert (reqData != NULL);
- ret = PianoXmlParseGetStationInfo (req->responseData,
- &reqData->info);
- break;
- }
+ info = &reqData->info;
+ assert (info != NULL);
+
+ /* parse music seeds */
+ json_object *music = json_object_object_get (result, "music");
+ if (music != NULL) {
+ /* songs */
+ json_object *songs = json_object_object_get (music, "songs");
+ if (songs != NULL) {
+ for (size_t i = 0; i < json_object_array_length (songs); i++) {
+ json_object *s = json_object_array_get_idx (songs, i);
+ PianoSong_t *seedSong;
+
+ seedSong = calloc (1, sizeof (*seedSong));
+ if (seedSong == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
- case PIANO_REQUEST_DELETE_SEED: {
- assert (req->responseData != NULL);
+ seedSong->title = PianoJsonStrdup (s, "songName");
+ seedSong->artist = PianoJsonStrdup (s, "artistName");
+ seedSong->seedId = PianoJsonStrdup (s, "seedId");
- /* dummy function, checks for errors only */
- ret = PianoXmlParseTranformStation (req->responseData);
+ if (info->songSeeds == NULL) {
+ info->songSeeds = seedSong;
+ } else {
+ PianoSong_t *curSong = info->songSeeds;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = seedSong;
+ }
+ }
+ }
+
+ /* artists */
+ json_object *artists = json_object_object_get (music,
+ "artists");
+ if (artists != NULL) {
+ for (size_t i = 0; i < json_object_array_length (artists); i++) {
+ json_object *a = json_object_array_get_idx (artists, i);
+ PianoArtist_t *seedArtist;
+
+ seedArtist = calloc (1, sizeof (*seedArtist));
+ if (seedArtist == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ seedArtist->name = PianoJsonStrdup (a, "artistName");
+ seedArtist->seedId = PianoJsonStrdup (a, "seedId");
+
+ if (info->artistSeeds == NULL) {
+ info->artistSeeds = seedArtist;
+ } else {
+ PianoArtist_t *curArtist = info->artistSeeds;
+ while (curArtist->next != NULL) {
+ curArtist = curArtist->next;
+ }
+ curArtist->next = seedArtist;
+ }
+ }
+ }
+ }
+
+ /* parse feedback */
+ json_object *feedback = json_object_object_get (result,
+ "feedback");
+ if (feedback != NULL) {
+ json_object_object_foreach (feedback, key, val) {
+ for (size_t i = 0; i < json_object_array_length (val); i++) {
+ json_object *s = json_object_array_get_idx (val, i);
+ PianoSong_t *feedbackSong;
+
+ feedbackSong = calloc (1, sizeof (*feedbackSong));
+ if (feedbackSong == NULL) {
+ return PIANO_RET_OUT_OF_MEMORY;
+ }
+
+ feedbackSong->title = PianoJsonStrdup (s, "songName");
+ feedbackSong->artist = PianoJsonStrdup (s,
+ "artistName");
+ feedbackSong->feedbackId = PianoJsonStrdup (s,
+ "feedbackId");
+ feedbackSong->rating = json_object_get_boolean (
+ json_object_object_get (s, "isPositive")) ?
+ PIANO_RATE_LOVE : PIANO_RATE_BAN;
+
+
+ if (info->feedback == NULL) {
+ info->feedback = feedbackSong;
+ } else {
+ PianoSong_t *curSong = info->feedback;
+ while (curSong->next != NULL) {
+ curSong = curSong->next;
+ }
+ curSong->next = feedbackSong;
+ }
+ }
+ }
+ }
+ break;
}
}
+ json_object_put (j);
+
return ret;
}
@@ -1171,70 +1328,51 @@ const char *PianoErrorToStr (PianoReturn_t ret) {
return "Unknown.";
break;
- case PIANO_RET_XML_INVALID:
- return "Invalid XML.";
- break;
-
- case PIANO_RET_AUTH_TOKEN_INVALID:
- return "Invalid auth token.";
- break;
-
- case PIANO_RET_AUTH_USER_PASSWORD_INVALID:
- return "Username and/or password not correct.";
- break;
-
- case PIANO_RET_NOT_AUTHORIZED:
- return "Not authorized.";
- break;
-
- case PIANO_RET_PROTOCOL_INCOMPATIBLE:
- return "Protocol incompatible. Please upgrade " PACKAGE ".";
+ case PIANO_RET_INVALID_RESPONSE:
+ return "Invalid response.";
break;
- case PIANO_RET_READONLY_MODE:
- return "Request cannot be completed at this time, please try "
- "again later.";
- break;
-
- case PIANO_RET_STATION_CODE_INVALID:
- return "Station id is invalid.";
+ case PIANO_RET_CONTINUE_REQUEST:
+ /* never shown to the user */
+ assert (0);
+ return "Fix your program.";
break;
- case PIANO_RET_IP_REJECTED:
- return "Your ip address was rejected. Please setup a control "
- "proxy (see manpage).";
+ case PIANO_RET_OUT_OF_MEMORY:
+ return "Out of memory.";
break;
- case PIANO_RET_STATION_NONEXISTENT:
- return "Station does not exist.";
+ /* pandora error messages */
+ case PIANO_RET_P_INTERNAL:
+ return "Internal error.";
break;
- case PIANO_RET_OUT_OF_MEMORY:
- return "Out of memory.";
+ case PIANO_RET_P_CALL_NOT_ALLOWED:
+ return "Call not allowed.";
break;
- case PIANO_RET_OUT_OF_SYNC:
- return "Out of sync. Please correct your system's time.";
+ case PIANO_RET_P_INVALID_AUTH_TOKEN:
+ return "Invalid auth token.";
break;
- case PIANO_RET_PLAYLIST_END:
- return "Playlist end.";
+ case PIANO_RET_P_MAINTENANCE_MODE:
+ return "Maintenance mode.";
break;
- case PIANO_RET_QUICKMIX_NOT_PLAYABLE:
- return "Quickmix not playable.";
+ case PIANO_RET_P_MAX_STATIONS_REACHED:
+ return "Max number of stations reached.";
break;
- case PIANO_RET_REMOVING_TOO_MANY_SEEDS:
- return "Last seed cannot be removed.";
+ case PIANO_RET_P_READ_ONLY_MODE:
+ return "Read only mode. Try again later.";
break;
- case PIANO_RET_EXCESSIVE_ACTIVITY:
- return "Excessive activity.";
+ case PIANO_RET_P_STATION_DOES_NOT_EXIST:
+ return "Station does not exist.";
break;
- case PIANO_RET_DAILY_SKIP_LIMIT_REACHED:
- return "Daily skip limit reached.";
+ case PIANO_RET_P_INVALID_PARTNER_LOGIN:
+ return "Invalid partner login.";
break;
default: