1 /* gmpc-lastfmradio (GMPC plugin)
2 * Copyright (C) 2008-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpcwiki.sarine.nl/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <gmpc/plugin.h>
25 #include <gmpc/config1.h>
27 #include <gmpc/gmpc_easy_download.h>
28 #include "lfr_plugin.h"
32 #define HANDSHAKE "http://ws.audioscrobbler.com/radio/handshake.php?version=1.1.1&platform=linux&username=%s&passwordmd5=%s&debug=0&partner="
33 #define REQURL "http://ws.audioscrobbler.com/radio/adjust.php?session=%s&url=lastfm://%s/%s%s&debug=0"
34 #define NPURL "http://ws.audioscrobbler.com/radio/np.php?session=%s"
36 #define BUFFER_SIZE 256
39 QUERY_TYPE_PLAYSTREAM,
43 typedef struct _Query{
49 char buffer[BUFFER_SIZE];
56 class Last:FM from G:Object
58 private gchar *username = {cfg_get_single_value_as_string(config,CONFIG_NAME, "username")} destroywith g_free;
59 private gchar *password_md5 = {cfg_get_single_value_as_string(config,CONFIG_NAME, "password")} destroywith g_free;
60 private gchar *sessionid = {NULL} destroywith g_free;
61 private gchar *stream_url = {NULL} destroywith g_free;
66 file_stream_read_done(GInputStream *stream, GAsyncResult *res,gpointer data)
69 Self *self = SELF(q->self);
71 gsize read = g_input_stream_read_finish(stream, res, &error);
76 q->data = g_realloc(q->data, (q->data_size+read+1)*sizeof(char));
77 memcpy(&(q->data[q->data_size]), q->buffer, read);
78 q->data[q->data_size+read] = '\0';
82 g_input_stream_read_async(G_INPUT_STREAM(stream),
83 q->buffer, BUFFER_SIZE,
84 G_PRIORITY_DEFAULT, NULL,
85 (GAsyncReadyCallback)self_file_stream_read_done, q);
88 g_input_stream_close(stream,NULL, NULL);
89 g_object_unref(stream);
92 gchar *msg = g_markup_printf_escaped("Libgio failed to read Last.FM: '<i>%s</i>'",
94 self_error_callback(self,TRUE,msg);
98 /* Remove anything we got */
103 g_object_unref(q->file);
106 if(q->type == QUERY_TYPE_SESSION)
108 gchar **lines = g_strsplit(q->data, "\n", 0);
110 for(i=0; lines && lines[i];i++) {
111 gchar **values = g_strsplit(lines[i], "=", 2);
113 if(strcmp(values[0], "session") == 0)
115 self->_priv->sessionid = g_strdup(values[1]);
117 else if (strcmp(values[0], "stream_url") == 0)
119 self->_priv->stream_url = g_strdup(values[1]);
125 self_connection_changed(self, self_is_connected(self));
126 if(self->_priv->sessionid == NULL || self->_priv->stream_url == NULL){
127 self_error_callback(self, TRUE,"Last.FM: Invalid password or username");
130 else if (q->type == QUERY_TYPE_PLAYSTREAM)
132 if(strncmp(q->data, "response=OK", strlen("response=OK")) == 0)
133 self_play_stream(self, self->_priv->stream_url);
135 self_error_callback(self, FALSE,"Last.FM: Radio station is not playable");
137 }else if (q->type == QUERY_TYPE_NP)
139 mpd_Song *song = mpd_newSong();
140 gchar **lines = g_strsplit(q->data, "\n", 0);
142 for(i=0; lines && lines[i];i++) {
143 gchar **values = g_strsplit(lines[i], "=", 2);
144 if(values && values[0] && values[1]) {
145 if(strcmp(values[0], "artist") == 0)
147 song->artist = g_strdup(values[1]);
149 else if (strcmp(values[0], "album") == 0)
151 song->album = g_strdup(values[1]);
153 else if (strcmp(values[0], "track") == 0)
155 song->title = g_strdup(values[1]);
161 if(song->artist && song->title)
163 self_song_info_available(self, song);
165 self_error_callback(self, FALSE,"Last.FM: Insufficient song information available");
166 self_song_info_available(self, NULL);
173 if(q->data) g_free(q->data);
176 self_processing(self, FALSE);
182 file_open_ready(GFile *file, GAsyncResult *res,gpointer data)
185 Self *self = SELF(q->self);
186 GError *error = NULL;
187 //Self *self = q->self;
188 GFileInputStream *stream = g_file_read_finish(file, res, &error);
190 if(error == NULL && stream)
192 /* Start reading the file. */
193 g_input_stream_read_async(G_INPUT_STREAM(stream), q->buffer,
194 BUFFER_SIZE,G_PRIORITY_DEFAULT,
196 (GAsyncReadyCallback)self_file_stream_read_done, q);
199 if(stream) g_object_unref(stream);
201 gchar *msg = g_markup_printf_escaped("Libgio failed to read Last.FM: '<i>%s</i>'",
203 self_error_callback(self,TRUE,msg);
208 g_object_unref(q->file);
210 self_processing(self, FALSE);
215 do_playstream(self, const gchar *url)
218 if(self->_priv->sessionid == NULL) return;
219 self_processing(self, TRUE);
220 q = g_malloc0(sizeof(*q));
222 q->file = g_file_new_for_uri(url);
223 q->type = QUERY_TYPE_PLAYSTREAM;
224 g_file_read_async(q->file, G_PRIORITY_DEFAULT ,NULL,(GAsyncReadyCallback) self_file_open_ready, q);
243 return (self->_priv->sessionid != NULL && self->_priv->stream_url);
248 signal last NONE (BOOLEAN)
250 connection_changed(self, gboolean connected)
254 signal last NONE (STRING)
256 play_stream(self,gchar *stream)
260 signal last NONE (BOOLEAN)
262 processing(self, gboolean processing)
266 signal last NONE (POINTER)
268 song_info_available(self, mpd_Song *song)
275 signal last NONE (BOOLEAN,STRING)
277 error_callback(self, gboolean fatal,const gchar *errormsg)
286 return self->_priv->username;
292 return self->_priv->stream_url;
296 set_login(self, const gchar *username, const gchar *password)
298 self->_priv->username = g_strdup(username);
301 const gchar *chk = NULL;
302 GChecksum *ck = g_checksum_new(G_CHECKSUM_MD5);
303 g_checksum_update(ck,(guchar *)password, -1);
304 chk = g_checksum_get_string(ck);
306 self->_priv->password_md5 = g_strdup(chk);
309 cfg_set_single_value_as_string(config,CONFIG_NAME, "username", self->_priv->username);
310 cfg_set_single_value_as_string(config, CONFIG_NAME, "password", self->_priv->password_md5);
318 /* check requirments */
319 if(self->_priv->username == NULL || self->_priv->password_md5 == NULL) return;
321 /* If we have a session reset it. and signal disconnect */
322 if(self->_priv->stream_url) { g_free(self->_priv->stream_url); self->_priv->stream_url = NULL; }
323 if(self->_priv->sessionid) { g_free(self->_priv->sessionid); self->_priv->sessionid = NULL; }
324 self_connection_changed(self, self_is_connected(self));
325 self_processing(self, TRUE);
326 /* Create request url */
327 url = g_strdup_printf(HANDSHAKE, self->_priv->username, self->_priv->password_md5);
329 q = g_malloc0(sizeof(*q));
331 q->file = g_file_new_for_uri(url);
332 q->type = QUERY_TYPE_SESSION;
333 g_file_read_async(q->file, G_PRIORITY_DEFAULT ,NULL,(GAsyncReadyCallback) self_file_open_ready, q);
338 * Different play things
342 play_artist_radio(self, const gchar *artist)
346 if(self->_priv->sessionid == NULL) return;
347 temp = gmpc_easy_download_uri_escape(artist);
348 url = g_strdup_printf(REQURL, self->_priv->sessionid, "artist", temp,"");
349 self_do_playstream(self, url);
355 play_similar_artist_radio(self, const gchar *artist)
359 if(self->_priv->sessionid == NULL) return;
360 temp = gmpc_easy_download_uri_escape(artist);
361 url = g_strdup_printf(REQURL, self->_priv->sessionid, "artist", temp,"/similarartists");
362 self_do_playstream(self, url);
369 play_tag_radio(self, const gchar *artist)
373 if(self->_priv->sessionid == NULL) return;
374 temp = gmpc_easy_download_uri_escape(artist);
375 url = g_strdup_printf(REQURL, self->_priv->sessionid, "globaltags", temp,"");
376 self_do_playstream(self, url);
382 play_group_radio(self, const gchar *artist)
386 if(self->_priv->sessionid == NULL) return;
387 temp = gmpc_easy_download_uri_escape(artist);
388 url = g_strdup_printf(REQURL, self->_priv->sessionid, "group", temp,"");
389 self_do_playstream(self, url);
395 play_user_radio(self,const gchar *artist)
399 if(self->_priv->sessionid == NULL) return;
401 temp = gmpc_easy_download_uri_escape(artist);
403 temp = gmpc_easy_download_uri_escape(self->_priv->username);
404 url = g_strdup_printf(REQURL, self->_priv->sessionid, "user", temp,"/personal");
405 self_do_playstream(self, url);
411 play_neighbours_radio(self,const gchar *artist)
415 if(self->_priv->sessionid == NULL) return;
418 temp = gmpc_easy_download_uri_escape(artist);
420 temp = gmpc_easy_download_uri_escape(self->_priv->username);
421 url = g_strdup_printf(REQURL, self->_priv->sessionid, "user", temp,"/neighbours");
422 self_do_playstream(self, url);
428 play_recommendations (self)
432 if(self->_priv->sessionid == NULL) return;
433 temp = gmpc_easy_download_uri_escape(self->_priv->username);
434 url = g_strdup_printf(REQURL, self->_priv->sessionid, "user", temp,"/recommended");
435 self_do_playstream(self, url);
441 play_loved_radio (self)
445 if(self->_priv->sessionid == NULL) return;
446 temp = gmpc_easy_download_uri_escape(self->_priv->username);
447 url = g_strdup_printf(REQURL, self->_priv->sessionid, "user", temp,"/loved");
448 self_do_playstream(self, url);
456 get_current_song (self)
460 /* check requirments */
461 if(self->_priv->username == NULL || self->_priv->password_md5 == NULL) return;
462 self_processing(self, TRUE);
463 /* Create request url */
464 url = g_strdup_printf(NPURL, self->_priv->sessionid);
466 q = g_malloc0(sizeof(*q));
468 q->file = g_file_new_for_uri(url);
469 q->type = QUERY_TYPE_NP;
470 g_file_read_async(q->file, G_PRIORITY_DEFAULT ,NULL,(GAsyncReadyCallback) self_file_open_ready, q);