From e8e208430901ed10550a3889fa0ecfaaad6c3863 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sun, 19 Apr 2015 15:02:18 +0200 Subject: [PATCH 1/2] Recognize ad tokens --- src/libpiano/piano.h | 17 ++++++++ src/libpiano/request.c | 91 +++++++++++++++++++++++++++++++++++++++++ src/libpiano/response.c | 82 +++++++++++++++++++++++++++++++++++++ src/main.c | 38 +++++++++++++++++ 4 files changed, 228 insertions(+) diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h index 0ec6c493..de65be6c 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 2f9c91a6..97060db5 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, @@ -135,6 +169,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; @@ -444,6 +492,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 4b706e26..273ac03b 100644 --- a/src/libpiano/response.c +++ b/src/libpiano/response.c @@ -23,6 +23,7 @@ THE SOFTWARE. #include "../config.h" +#include #include #include #include @@ -88,6 +89,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 @@ -308,6 +342,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_object_get_ex (s, "trackGain", &v) ? json_object_get_double (v) : 0.0; song->length = json_object_object_get_ex (s, "trackLength", &v) ? @@ -651,6 +686,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 6388cde1..6ce177df 100644 --- a/src/main.c +++ b/src/main.c @@ -242,6 +242,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); From 77af5f5ec71d7d9340f2e78d526c587fc6d12b59 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Wed, 10 Jul 2019 12:58:34 +0200 Subject: [PATCH 2/2] Do not ignore ad tracks --- src/libpiano/response.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libpiano/response.c b/src/libpiano/response.c index 273ac03b..1af4e822 100644 --- a/src/libpiano/response.c +++ b/src/libpiano/response.c @@ -298,6 +298,11 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { return PIANO_RET_OUT_OF_MEMORY; } + if (json_object_object_get_ex (s, "adToken", NULL)) { + song->adToken = PianoJsonStrdup (s, "adToken"); + playlist = PianoListAppendP (playlist, song); + continue; + } if (!json_object_object_get_ex (s, "artistName", NULL)) { free (song); continue; @@ -342,7 +347,6 @@ 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_object_get_ex (s, "trackGain", &v) ? json_object_get_double (v) : 0.0; song->length = json_object_object_get_ex (s, "trackLength", &v) ?