summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2011-03-21 12:46:21 +0100
committerLars-Dominik Braun <lars@6xq.net>2011-03-21 12:46:21 +0100
commitbbd317d85069dee9c9cffff609d1d1fd086cbcb7 (patch)
tree776c92f38b866a09c54ae7ccf58ec0748c7a1bf9
parentb6a0245796794228614c092c63b51bf50970fa08 (diff)
downloadpianobar-windows-bbd317d85069dee9c9cffff609d1d1fd086cbcb7.tar.gz
pianobar-windows-bbd317d85069dee9c9cffff609d1d1fd086cbcb7.tar.bz2
pianobar-windows-bbd317d85069dee9c9cffff609d1d1fd086cbcb7.zip
piano: Add getStation support
Response contains artist/song seeds and feedback data.
-rw-r--r--src/libpiano/piano.c69
-rw-r--r--src/libpiano/piano.h16
-rw-r--r--src/libpiano/xml.c194
-rw-r--r--src/libpiano/xml.h1
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), "<?xml version=\"1.0\"?>"
+ "<methodCall><methodName>station.getStation</methodName>"
+ "<params><param><value><int>%lu</int></value></param>"
+ /* auth token */
+ "<param><value><string>%s</string></value></param>"
+ /* station id */
+ "<param><value><string>%s</string></value></param>"
+ "</params></methodCall>", (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 <string.h>
#include <stdlib.h>
#include <ezxml.h>
+#include <assert.h>
#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: <?xml version="1.0" encoding="UTF-8"?>
@@ -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);