From bbd317d85069dee9c9cffff609d1d1fd086cbcb7 Mon Sep 17 00:00:00 2001
From: Lars-Dominik Braun <lars@6xq.net>
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(-)

(limited to 'src')

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);
 
-- 
cgit v1.2.3