summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libpiano/piano.h17
-rw-r--r--src/libpiano/request.c91
-rw-r--r--src/libpiano/response.c117
-rw-r--r--src/main.c39
4 files changed, 237 insertions, 27 deletions
diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h
index 1a84d4a..f7b215f 100644
--- a/src/libpiano/piano.h
+++ b/src/libpiano/piano.h
@@ -96,6 +96,7 @@ typedef struct PianoSong {
char *feedbackId;
char *detailUrl;
char *trackToken;
+ char *adToken;
float fileGain;
unsigned int length; /* song length in seconds */
PianoSongRating_t rating;
@@ -179,6 +180,8 @@ typedef enum {
PIANO_REQUEST_DELETE_SEED = 22,
PIANO_REQUEST_GET_SETTINGS = 23,
PIANO_REQUEST_CHANGE_SETTINGS = 24,
+ PIANO_REQUEST_GET_AD_METADATA = 25,
+ PIANO_REQUEST_REGISTER_AD = 26,
} PianoRequestType_t;
typedef struct PianoRequest {
@@ -266,6 +269,20 @@ typedef struct {
PianoTristate_t explicitContentFilter;
} PianoRequestDataChangeSettings_t;
+typedef struct {
+ char *token;
+ PianoSong_t *song;
+ PianoAudioQuality_t quality;
+ char **retToken;
+ size_t retTokenCount;
+} PianoRequestDataGetAdMetadata_t;
+
+typedef struct {
+ char **token;
+ size_t tokenCount;
+ PianoStation_t *station;
+} PianoRequestDataRegisterAd_t;
+
/* pandora error code offset */
#define PIANO_RET_OFFSET 1024
typedef enum {
diff --git a/src/libpiano/request.c b/src/libpiano/request.c
index 02b4b43..317def8 100644
--- a/src/libpiano/request.c
+++ b/src/libpiano/request.c
@@ -76,6 +76,10 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
json_object_new_string ("5"));
json_object_object_add (j, "includeUrls",
json_object_new_boolean (true));
+ json_object_object_add (j, "returnDeviceType",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "returnUpdatePromptVersions",
+ json_object_new_boolean (true));
snprintf (req->urlPath, sizeof (req->urlPath),
PIANO_RPC_PATH "method=auth.partnerLogin");
break;
@@ -95,6 +99,36 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
json_object_new_string (ph->partner.authToken));
json_object_object_add (j, "syncTime",
json_object_new_int (timestamp));
+ json_object_object_add (j, "includePandoraOneInfo",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeDemographics",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeAdAttributes",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeShuffleInsteadOfQuickMix",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "returnCollectTrackLifetimeStats",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "xplatformAdCapable",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "returnUserstate",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeListeningHours",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeDailySkipLimit",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeSkipDelay",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeAdvertiserAttributes",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includePlaylistAttributes",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeSkipAttributes",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeStationExpirationTime",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeStationDescription",
+ json_object_new_boolean (true));
CURL * const curl = curl_easy_init ();
urlencAuthToken = curl_easy_escape (curl,
@@ -134,6 +168,20 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
json_object_new_string (reqData->station->id));
json_object_object_add (j, "includeTrackLength",
json_object_new_boolean (true));
+ json_object_object_add (j, "includeAudioToken",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "xplatformAdCapable",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeAudioReceiptUrl",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeCompetitiveSepIndicator",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeCompletePlaylist",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeTrackOptions",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "audioAdPodCapable",
+ json_object_new_boolean (true));
method = "station.getPlaylist";
break;
@@ -440,6 +488,49 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req,
break;
}
+ case PIANO_REQUEST_GET_AD_METADATA: {
+ PianoRequestDataGetAdMetadata_t *reqData = req->data;
+
+ assert (reqData != NULL);
+ assert (reqData->token != NULL);
+
+ json_object_object_add (j, "adToken",
+ json_object_new_string (reqData->token));
+ json_object_object_add (j, "returnAdTrackingTokens",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "supportAudioAds",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeBannerAd",
+ json_object_new_boolean (true));
+ json_object_object_add (j, "includeListeningHours",
+ json_object_new_boolean (true));
+
+ method = "ad.getAdMetadata";
+ break;
+ }
+
+ case PIANO_REQUEST_REGISTER_AD: {
+ PianoRequestDataRegisterAd_t *reqData = req->data;
+
+ assert (reqData != NULL);
+ assert (reqData->token != NULL);
+ assert (reqData->tokenCount > 0);
+ assert (reqData->station != NULL);
+ assert (reqData->station->id != NULL);
+
+ json_object_object_add (j, "stationId",
+ json_object_new_string (reqData->station->id));
+ json_object * const token = json_object_new_array ();
+ for (size_t i = 0; i < reqData->tokenCount; i++) {
+ json_object_array_add (token,
+ json_object_new_string (reqData->token[i]));
+ }
+ json_object_object_add (j, "adTrackingTokens", token);
+
+ method = "ad.registerAd";
+ break;
+ }
+
/* "high-level" wrapper */
case PIANO_REQUEST_RATE_SONG: {
/* love/ban song */
diff --git a/src/libpiano/response.c b/src/libpiano/response.c
index 7f0cee0..a920c07 100644
--- a/src/libpiano/response.c
+++ b/src/libpiano/response.c
@@ -23,6 +23,7 @@ THE SOFTWARE.
#include "../config.h"
+#include <stdio.h>
#include <json.h>
#include <string.h>
#include <assert.h>
@@ -34,7 +35,12 @@ THE SOFTWARE.
#include "crypt.h"
static char *PianoJsonStrdup (json_object *j, const char *key) {
- return strdup (json_object_get_string (json_object_object_get (j, key)));
+ json_object * const o = json_object_object_get (j, key);
+ if (o != NULL) {
+ return strdup (json_object_get_string (o));
+ } else {
+ return NULL;
+ }
}
static void PianoJsonParseStation (json_object *j, PianoStation_t *s) {
@@ -70,6 +76,39 @@ static void PianoStrpcat (char * restrict dest, const char * restrict src,
*dest = '\0';
}
+static bool audioMapSelect (json_object * const map,
+ const PianoAudioQuality_t quality, PianoSong_t * const song) {
+ assert (map != NULL);
+ assert (quality != PIANO_AQ_UNKNOWN);
+ assert (song != NULL);
+
+ /* get audio url based on selected quality */
+ static const char *qualityMap[] = {"", "lowQuality", "mediumQuality",
+ "highQuality"};
+ assert (quality < sizeof (qualityMap)/sizeof (*qualityMap));
+ static const char *formatMap[] = {"", "aacplus", "mp3"};
+
+ json_object * const item =
+ json_object_object_get (map, qualityMap[quality]);
+
+ if (item == NULL) {
+ /* requested quality is not available */
+ return false;
+ }
+
+ const char *encoding = json_object_get_string (
+ json_object_object_get (item, "encoding"));
+ assert (encoding != NULL);
+ for (size_t k = 0; k < sizeof (formatMap)/sizeof (*formatMap); k++) {
+ if (strcmp (formatMap[k], encoding) == 0) {
+ song->audioFormat = k;
+ break;
+ }
+ }
+ song->audioUrl = PianoJsonStrdup (item, "audioUrl");
+ return true;
+}
+
/* parse xml response and update data structures/return new data structure
* @param piano handle
* @param initialized request (expects responseData to be a NUL-terminated
@@ -230,34 +269,10 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
return PIANO_RET_OUT_OF_MEMORY;
}
- if (json_object_object_get (s, "artistName") == NULL) {
- free (song);
- continue;
- }
-
/* get audio url based on selected quality */
- static const char *qualityMap[] = {"", "lowQuality", "mediumQuality",
- "highQuality"};
- assert (reqData->quality < sizeof (qualityMap)/sizeof (*qualityMap));
- static const char *formatMap[] = {"", "aacplus", "mp3"};
- json_object *map = json_object_object_get (s, "audioUrlMap");
- assert (map != NULL);
-
+ json_object * const map = json_object_object_get (s, "audioUrlMap");
if (map != NULL) {
- map = json_object_object_get (map, qualityMap[reqData->quality]);
-
- if (map != NULL) {
- const char *encoding = json_object_get_string (
- json_object_object_get (map, "encoding"));
- assert (encoding != NULL);
- for (size_t k = 0; k < sizeof (formatMap)/sizeof (*formatMap); k++) {
- if (strcmp (formatMap[k], encoding) == 0) {
- song->audioFormat = k;
- break;
- }
- }
- song->audioUrl = PianoJsonStrdup (map, "audioUrl");
- } else {
+ if (!audioMapSelect (map, reqData->quality, song)) {
/* requested quality is not available */
ret = PIANO_RET_QUALITY_UNAVAILABLE;
free (song);
@@ -273,6 +288,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
song->stationId = PianoJsonStrdup (s, "stationId");
song->coverArt = PianoJsonStrdup (s, "albumArtUrl");
song->detailUrl = PianoJsonStrdup (s, "songDetailUrl");
+ song->adToken = PianoJsonStrdup (s, "adToken");
song->fileGain = json_object_get_double (
json_object_object_get (s, "trackGain"));
song->length = json_object_get_int (
@@ -610,6 +626,53 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
break;
}
+
+ case PIANO_REQUEST_GET_AD_METADATA: {
+ PianoRequestDataGetAdMetadata_t *reqData = req->data;
+
+ assert (reqData != NULL);
+ assert (reqData->song != NULL);
+ assert (reqData->quality != PIANO_AQ_UNKNOWN);
+
+ json_object *token = json_object_object_get (result,
+ "adTrackingTokens");
+ if (token != NULL) {
+ reqData->retTokenCount = json_object_array_length (token);
+ reqData->retToken = malloc (reqData->retTokenCount *
+ sizeof (*reqData->retToken));
+ for (size_t i = 0; i < reqData->retTokenCount; i++) {
+ json_object * const t = json_object_array_get_idx (token,
+ i);
+ assert (t != NULL);
+ reqData->retToken[i] = strdup (json_object_get_string (t));
+ printf ("added tracking token %s\n", reqData->retToken[i]);
+ }
+ } else {
+ reqData->retTokenCount = 0;
+ reqData->retToken = NULL;
+ }
+
+ PianoSong_t * const song = reqData->song;
+ json_object * const map = json_object_object_get (result, "audioUrlMap");
+ if (map != NULL) {
+ if (!audioMapSelect (map, reqData->quality, song)) {
+ /* requested quality is not available */
+ ret = PIANO_RET_QUALITY_UNAVAILABLE;
+ break;
+ }
+ }
+ song->artist = PianoJsonStrdup (result, "companyName");
+ song->title = PianoJsonStrdup (result, "title");
+ song->album = strdup ("");
+ song->fileGain = json_object_get_double (
+ json_object_object_get (result, "trackGain"));
+ break;
+ }
+
+ case PIANO_REQUEST_REGISTER_AD: {
+ printf ("req->responseData: %s\n", req->responseData);
+ break;
+ }
}
cleanup:
diff --git a/src/main.c b/src/main.c
index b113f4e..a04ec18 100644
--- a/src/main.c
+++ b/src/main.c
@@ -211,6 +211,7 @@ static void BarMainGetPlaylist (BarApp_t *app) {
PianoReturn_t pRet;
CURLcode wRet;
PianoRequestDataGetPlaylist_t reqData;
+
reqData.station = app->curStation;
reqData.quality = app->settings.audioQuality;
@@ -239,6 +240,44 @@ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) {
const PianoSong_t * const curSong = app->playlist;
assert (curSong != NULL);
+ /* ads */
+ PianoReturn_t pRet;
+ CURLcode wRet;
+
+ /* is this an advertising track? */
+ if (curSong->adToken != NULL) {
+ PianoRequestDataGetAdMetadata_t adReqData;
+
+ memset (&adReqData, 0, sizeof (adReqData));
+ adReqData.token = curSong->adToken;
+ adReqData.song = curSong;
+ adReqData.quality = app->settings.audioQuality;
+
+ BarUiMsg (&app->settings, MSG_INFO, "Fetching ads with token %s... ",
+ adReqData.token);
+ BarUiPianoCall (app, PIANO_REQUEST_GET_AD_METADATA,
+ &adReqData, &pRet, &wRet);
+
+ /* got token? */
+ if (adReqData.retTokenCount > 0) {
+ PianoRequestDataRegisterAd_t regReqData;
+
+ regReqData.token = adReqData.retToken;
+ regReqData.tokenCount = adReqData.retTokenCount;
+ regReqData.station = app->curStation;
+
+ BarUiMsg (&app->settings, MSG_INFO, "Registering ad... ");
+ BarUiPianoCall (app, PIANO_REQUEST_REGISTER_AD, &regReqData, &pRet,
+ &wRet);
+
+ /* delete */
+ for (size_t i = 0; i < adReqData.retTokenCount; i++) {
+ free (adReqData.retToken[i]);
+ }
+ free (adReqData.retToken);
+ }
+ }
+
BarUiPrintSong (&app->settings, curSong, app->curStation->isQuickMix ?
PianoFindStationById (app->ph.stations,
curSong->stationId) : NULL);