diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libpiano/piano.h | 17 | ||||
-rw-r--r-- | src/libpiano/request.c | 91 | ||||
-rw-r--r-- | src/libpiano/response.c | 117 | ||||
-rw-r--r-- | src/main.c | 39 |
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: @@ -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, ®ReqData, &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); |