summaryrefslogtreecommitdiff
path: root/src/libpiano/piano.c
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2012-04-27 15:15:40 +0200
committerLars-Dominik Braun <lars@6xq.net>2012-05-01 11:04:50 +0200
commit63c86dce1e248aa03a99792f56077dc176bcd295 (patch)
treee702be797a6e1b84f780dc6bf3fab504eb853d71 /src/libpiano/piano.c
parentbd9291bfc016a5f16fd81495dbe779b47627ade0 (diff)
downloadpianobar-63c86dce1e248aa03a99792f56077dc176bcd295.tar.gz
pianobar-63c86dce1e248aa03a99792f56077dc176bcd295.tar.bz2
pianobar-63c86dce1e248aa03a99792f56077dc176bcd295.zip
piano: Move to JSON API (v5)
In random order: Thanks to Chris Eby for his work on pithos, to an anonymous coward for sending me his Android client, to Alex Howard for sending me the webOS client, to ZigZagJoe for providing a temporary fix and thanks to everyone who sent Pandora a message. Although there are a few rough edges here and there this fixes #236.
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: