From bbd317d85069dee9c9cffff609d1d1fd086cbcb7 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Mon, 21 Mar 2011 12:46:21 +0100 Subject: piano: Add getStation support Response contains artist/song seeds and feedback data. --- src/libpiano/piano.c | 69 ++++++++++++++---- src/libpiano/piano.h | 16 +++++ src/libpiano/xml.c | 194 +++++++++++++++++++++++++++++++++++++++++---------- src/libpiano/xml.h | 1 + 4 files changed, 227 insertions(+), 53 deletions(-) diff --git a/src/libpiano/piano.c b/src/libpiano/piano.c index a53c102..e475799 100644 --- a/src/libpiano/piano.c +++ b/src/libpiano/piano.c @@ -57,32 +57,29 @@ void PianoInit (PianoHandle_t *ph) { (unsigned long) time (NULL) % 10000000); } -/* free complete search result - * @public yes - * @param search result +/* destroy artist linked list */ -void PianoDestroySearchResult (PianoSearchResult_t *searchResult) { +void PianoDestroyArtists (PianoArtist_t *artists) { PianoArtist_t *curArtist, *lastArtist; - PianoSong_t *curSong, *lastSong; - curArtist = searchResult->artists; + curArtist = artists; while (curArtist != NULL) { free (curArtist->name); free (curArtist->musicId); + free (curArtist->seedId); lastArtist = curArtist; curArtist = curArtist->next; free (lastArtist); } +} - curSong = searchResult->songs; - while (curSong != NULL) { - free (curSong->title); - free (curSong->artist); - free (curSong->musicId); - lastSong = curSong; - curSong = curSong->next; - free (lastSong); - } +/* free complete search result + * @public yes + * @param search result + */ +void PianoDestroySearchResult (PianoSearchResult_t *searchResult) { + PianoDestroyArtists (searchResult->artists); + PianoDestroyPlaylist (searchResult->songs); } /* free single station @@ -128,12 +125,20 @@ void PianoDestroyPlaylist (PianoSong_t *playlist) { free (curSong->stationId); free (curSong->album); free (curSong->artistMusicId); + free (curSong->feedbackId); + free (curSong->seedId); lastSong = curSong; curSong = curSong->next; free (lastSong); } } +void PianoDestroyStationInfo (PianoStationInfo_t *info) { + PianoDestroyPlaylist (info->feedback); + PianoDestroyPlaylist (info->songSeeds); + PianoDestroyArtists (info->artistSeeds); +} + /* destroy genre linked list */ void PianoDestroyGenres (PianoGenre_t *genres) { @@ -708,6 +713,28 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req, break; } + case PIANO_REQUEST_GET_STATION_INFO: { + /* get station information (seeds and feedback) */ + PianoRequestDataGetStationInfo_t *reqData = req->data; + + assert (reqData != NULL); + assert (reqData->station != NULL); + + snprintf (xmlSendBuf, sizeof (xmlSendBuf), "" + "station.getStation" + "%lu" + /* auth token */ + "%s" + /* station id */ + "%s" + "", (unsigned long) timestamp, + ph->user.authToken, reqData->station->id); + snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH + "rid=%s&lid=%s&method=getStation&arg1=%s", + ph->routeId, ph->user.listenerId, reqData->station->id); + break; + } + /* "high-level" wrapper */ case PIANO_REQUEST_RATE_SONG: { /* love/ban song */ @@ -1029,6 +1056,18 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { &reqData->searchResult); break; } + + case PIANO_REQUEST_GET_STATION_INFO: { + /* get station information (seeds and feedback) */ + PianoRequestDataGetStationInfo_t *reqData = req->data; + + assert (req->responseData != NULL); + assert (reqData != NULL); + + ret = PianoXmlParseGetStationInfo (req->responseData, + &reqData->info); + break; + } } return ret; diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h index 509be9e..c91b7e9 100644 --- a/src/libpiano/piano.h +++ b/src/libpiano/piano.h @@ -71,6 +71,8 @@ typedef struct PianoSong { char *coverArt; char *musicId; char *title; + char *seedId; + char *feedbackId; float fileGain; PianoSongRating_t rating; PianoAudioFormat_t audioFormat; @@ -83,6 +85,7 @@ typedef struct PianoSong { typedef struct PianoArtist { char *name; char *musicId; + char *seedId; int score; struct PianoArtist *next; } PianoArtist_t; @@ -113,6 +116,12 @@ typedef struct PianoSearchResult { PianoArtist_t *artists; } PianoSearchResult_t; +typedef struct { + PianoSong_t *songSeeds; + PianoArtist_t *artistSeeds; + PianoSong_t *feedback; +} PianoStationInfo_t; + typedef enum { /* 0 is reserved: memset (x, 0, sizeof (x)) */ PIANO_REQUEST_LOGIN = 1, @@ -134,6 +143,7 @@ typedef enum { PIANO_REQUEST_GET_SEED_SUGGESTIONS = 17, PIANO_REQUEST_BOOKMARK_SONG = 18, PIANO_REQUEST_BOOKMARK_ARTIST = 19, + PIANO_REQUEST_GET_STATION_INFO = 20, } PianoRequestType_t; typedef struct PianoRequest { @@ -209,6 +219,11 @@ typedef struct { PianoSearchResult_t searchResult; } PianoRequestDataGetSeedSuggestions_t; +typedef struct { + PianoStation_t *station; + PianoStationInfo_t info; +} PianoRequestDataGetStationInfo_t; + typedef enum { PIANO_RET_ERR = 0, PIANO_RET_OK = 1, @@ -232,6 +247,7 @@ void PianoInit (PianoHandle_t *); void PianoDestroy (PianoHandle_t *); void PianoDestroyPlaylist (PianoSong_t *); void PianoDestroySearchResult (PianoSearchResult_t *); +void PianoDestroyStationInfo (PianoStationInfo_t *); PianoReturn_t PianoRequest (PianoHandle_t *, PianoRequest_t *, PianoRequestType_t); diff --git a/src/libpiano/xml.c b/src/libpiano/xml.c index 158eef2..19871c1 100644 --- a/src/libpiano/xml.c +++ b/src/libpiano/xml.c @@ -27,6 +27,7 @@ THE SOFTWARE. #include #include #include +#include #include "piano.h" #include "crypt.h" @@ -275,6 +276,12 @@ static void PianoXmlParsePlaylistCb (const char *key, const ezxml_t value, } else { song->rating = PIANO_RATE_NONE; } + } else if (strcmp ("isPositive", key) == 0) { + if (strcmp (valueStr, "1") == 0) { + song->rating = PIANO_RATE_LOVE; + } else { + song->rating = PIANO_RATE_BAN; + } } else if (strcmp ("stationId", key) == 0) { song->stationId = strdup (valueStr); } else if (strcmp ("albumTitle", key) == 0) { @@ -295,6 +302,8 @@ static void PianoXmlParsePlaylistCb (const char *key, const ezxml_t value, song->testStrategy = atoi (valueStr); } else if (strcmp ("songType", key) == 0) { song->songType = atoi (valueStr); + } else if (strcmp ("feedbackId", key) == 0) { + song->feedbackId = strdup (valueStr); } } @@ -490,6 +499,32 @@ PianoReturn_t PianoXmlParseAddSeed (PianoHandle_t *ph, char *xml, return PIANO_RET_OK; } +static PianoReturn_t PianoXmlParsePlaylistStruct (ezxml_t xml, + PianoSong_t **retSong) { + PianoSong_t *playlist = *retSong, *tmpSong; + + if ((tmpSong = calloc (1, sizeof (*tmpSong))) == NULL) { + return PIANO_RET_OUT_OF_MEMORY; + } + + PianoXmlStructParser (ezxml_child (xml, "struct"), PianoXmlParsePlaylistCb, + tmpSong); + /* begin linked list or append */ + if (playlist == NULL) { + playlist = tmpSong; + } else { + PianoSong_t *curSong = playlist; + while (curSong->next != NULL) { + curSong = curSong->next; + } + curSong->next = tmpSong; + } + + *retSong = playlist; + + return PIANO_RET_OK; +} + /* parses playlist; used when searching too * @param piano handle * @param xml document @@ -498,7 +533,7 @@ PianoReturn_t PianoXmlParseAddSeed (PianoHandle_t *ph, char *xml, PianoReturn_t PianoXmlParsePlaylist (PianoHandle_t *ph, char *xml, PianoSong_t **retPlaylist) { ezxml_t xmlDoc, dataNode; - PianoReturn_t ret; + PianoReturn_t ret = PIANO_RET_OK; if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) { return ret; @@ -509,30 +544,15 @@ PianoReturn_t PianoXmlParsePlaylist (PianoHandle_t *ph, char *xml, for (dataNode = ezxml_child (dataNode, "value"); dataNode; dataNode = dataNode->next) { - PianoSong_t *tmpSong; - - if ((tmpSong = calloc (1, sizeof (*tmpSong))) == NULL) { - ezxml_free (xmlDoc); - return PIANO_RET_OUT_OF_MEMORY; - } - - PianoXmlStructParser (ezxml_child (dataNode, "struct"), - PianoXmlParsePlaylistCb, tmpSong); - /* begin linked list or append */ - if (*retPlaylist == NULL) { - *retPlaylist = tmpSong; - } else { - PianoSong_t *curSong = *retPlaylist; - while (curSong->next != NULL) { - curSong = curSong->next; - } - curSong->next = tmpSong; + if ((ret = PianoXmlParsePlaylistStruct (dataNode, retPlaylist)) != + PIANO_RET_OK) { + break; } } ezxml_free (xmlDoc); - return PIANO_RET_OK; + return ret; } /* parse simple answers like this: @@ -614,26 +634,10 @@ static void PianoXmlParseSearchCb (const char *key, const ezxml_t value, } else if (strcmp ("songs", key) == 0) { for (curNode = ezxml_child (ezxml_get (value, "array", 0, "data", -1), "value"); curNode; curNode = curNode->next) { - /* FIXME: copy & waste */ - PianoSong_t *tmpSong; - - if ((tmpSong = calloc (1, sizeof (*tmpSong))) == NULL) { - /* fail silently */ + if (PianoXmlParsePlaylistStruct (curNode, &searchResult->songs) != + PIANO_RET_OK) { break; } - - PianoXmlStructParser (ezxml_child (curNode, "struct"), - PianoXmlParsePlaylistCb, tmpSong); - /* begin linked list or append */ - if (searchResult->songs == NULL) { - searchResult->songs = tmpSong; - } else { - PianoSong_t *curSong = searchResult->songs; - while (curSong->next != NULL) { - curSong = curSong->next; - } - curSong->next = tmpSong; - } } } } @@ -824,3 +828,117 @@ PianoReturn_t PianoXmlParseNarrative (char *xml, char **retNarrative) { return ret; } +/* seed bag, required because seedId is not part of artist/song struct in + * pandora's xml response + */ +struct PianoXmlParseSeedBag { + char *seedId; + PianoSong_t *song; + PianoArtist_t *artist; +}; + +/* parse seed struct + */ +static void PianoXmlParseSeedCb (const char *key, const ezxml_t value, + void *data) { + struct PianoXmlParseSeedBag *bag = data; + + if (strcmp ("song", key) == 0) { + if ((bag->song = calloc (1, sizeof (*bag->song))) == NULL) { + return; + } + + PianoXmlStructParser (ezxml_child (value, "struct"), + PianoXmlParsePlaylistCb, bag->song); + } else if (strcmp ("artist", key) == 0) { + if ((bag->artist = calloc (1, sizeof (*bag->artist))) == NULL) { + return; + } + + PianoXmlStructParser (ezxml_child (value, "struct"), + PianoXmlParseSearchArtistCb, bag->artist); + } else if (strcmp ("seedId", key) == 0) { + char *valueStr = PianoXmlGetNodeText (value); + bag->seedId = strdup (valueStr); + } +} + +/* parse getStation xml struct + */ +static void PianoXmlParseGetStationInfoCb (const char *key, const ezxml_t value, + void *data) { + PianoStationInfo_t *info = data; + + if (strcmp ("seeds", key) == 0) { + const ezxml_t dataNode = ezxml_get (value, "array", 0, "data", -1); + for (ezxml_t seedNode = ezxml_child (dataNode, "value"); seedNode; + seedNode = seedNode->next) { + struct PianoXmlParseSeedBag bag; + memset (&bag, 0, sizeof (bag)); + + PianoXmlStructParser (ezxml_child (seedNode, "struct"), + PianoXmlParseSeedCb, &bag); + + /* FIXME: use if-clause */ + assert (bag.seedId != NULL); + assert (bag.song != NULL || bag.artist != NULL); + + if (bag.song != NULL) { + bag.song->seedId = bag.seedId; + + if (info->songSeeds == NULL) { + info->songSeeds = bag.song; + } else { + PianoSong_t *curSong = info->songSeeds; + while (curSong->next != NULL) { + curSong = curSong->next; + } + curSong->next = bag.song; + } + } else if (bag.artist != NULL) { + bag.artist->seedId = bag.seedId; + + if (info->artistSeeds == NULL) { + info->artistSeeds = bag.artist; + } else { + PianoArtist_t *curSong = info->artistSeeds; + while (curSong->next != NULL) { + curSong = curSong->next; + } + curSong->next = bag.artist; + } + } else { + free (bag.seedId); + } + } + } else if (strcmp ("feedback", key) == 0) { + const ezxml_t dataNode = ezxml_get (value, "array", 0, "data", -1); + for (ezxml_t feedbackNode = ezxml_child (dataNode, "value"); feedbackNode; + feedbackNode = feedbackNode->next) { + if (PianoXmlParsePlaylistStruct (feedbackNode, &info->feedback) != + PIANO_RET_OK) { + break; + } + } + } +} + +/* parse getStation response + */ +PianoReturn_t PianoXmlParseGetStationInfo (char *xml, + PianoStationInfo_t *stationInfo) { + ezxml_t xmlDoc, dataNode; + PianoReturn_t ret; + + if ((ret = PianoXmlInitDoc (xml, &xmlDoc)) != PIANO_RET_OK) { + return ret; + } + + dataNode = ezxml_get (xmlDoc, "params", 0, "param", 0, "value", 0, "struct", -1); + PianoXmlStructParser (dataNode, PianoXmlParseGetStationInfoCb, stationInfo); + + ezxml_free (xmlDoc); + + return PIANO_RET_OK; +} + diff --git a/src/libpiano/xml.h b/src/libpiano/xml.h index f21c765..c5f3988 100644 --- a/src/libpiano/xml.h +++ b/src/libpiano/xml.h @@ -42,6 +42,7 @@ PianoReturn_t PianoXmlParseGenreExplorer (PianoHandle_t *ph, PianoReturn_t PianoXmlParseTranformStation (const char *searchXml); PianoReturn_t PianoXmlParseNarrative (const char *xml, char **retNarrative); PianoReturn_t PianoXmlParseSeedSuggestions (char *, PianoSearchResult_t *); +PianoReturn_t PianoXmlParseGetStationInfo (char *, PianoStationInfo_t *); char *PianoXmlEncodeString (const char *s); -- cgit v1.2.3