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); | 
