Skip to content

Commit

Permalink
dirty: Ads
Browse files Browse the repository at this point in the history
  • Loading branch information
PromyLOPh committed Sep 27, 2015
1 parent 9dbd0f3 commit fbfc5f1
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 27 deletions.
17 changes: 17 additions & 0 deletions src/libpiano/piano.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
91 changes: 91 additions & 0 deletions src/libpiano/request.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down
117 changes: 90 additions & 27 deletions src/libpiano/response.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ THE SOFTWARE.

#include "../config.h"

#include <stdio.h>
#include <json.h>
#include <string.h>
#include <assert.h>
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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 (
Expand Down Expand Up @@ -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:
Expand Down
39 changes: 39 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit fbfc5f1

Please sign in to comment.