Use gmpc's escape function
[gmpc-lastfmradio.git] / src / last-fm.gob
blobdf3f8f07f418bdd04ce349e6963de47f28ae0a9c
1 /* gmpc-lastfmradio (GMPC plugin)
2  * Copyright (C) 2008-2009 Qball Cow <qball@sarine.nl>
3  * Project homepage: http://gmpcwiki.sarine.nl/
4  
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.
20 requires 2.0.10
22 %h{
23 #include <config.h>
24 #include <gmpc/plugin.h>
25 #include <gmpc/config1.h>
26 #include <gio/gio.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 
37 typedef enum {
38     QUERY_TYPE_SESSION,
39     QUERY_TYPE_PLAYSTREAM,
40     QUERY_TYPE_NP
41 }QueryType;
43 typedef struct _Query{
44     GFile       *file;
45     QueryType type;
46     LastFM      *self;
48     /* private */
49     char    buffer[BUFFER_SIZE];
50     char    *data;
51     gsize   data_size;
52 }Query;
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;
64     private
65     void
66     file_stream_read_done(GInputStream *stream, GAsyncResult *res,gpointer data)
67     {
68         Query *q = data;
69         Self *self = SELF(q->self);
70         GError *error = NULL;
71         gsize read = g_input_stream_read_finish(stream, res, &error);
73         if(read> 0)
74         {
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';
80             q->data_size+=read;
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);
86             return;
87         }
88         g_input_stream_close(stream,NULL, NULL);
89         g_object_unref(stream);
90         if(error)
91         {
92             gchar *msg = g_markup_printf_escaped("Libgio failed to read Last.FM: '<i>%s</i>'", 
93                     error->message);
94             self_error_callback(self,TRUE,msg);
95             g_free(msg);
96             g_error_free(error);
97             error = NULL;
98             /* Remove anything we got */
99             q->data_size = 0;
100             g_free(q->data);
101             q->data = NULL;
102         }
103         g_object_unref(q->file);
104         if(q->data_size > 0)
105         {
106             if(q->type == QUERY_TYPE_SESSION)
107             {
108                 gchar **lines = g_strsplit(q->data, "\n", 0);
109                 int i;
110                 for(i=0; lines && lines[i];i++) {
111                     gchar **values = g_strsplit(lines[i], "=", 2);
112                     if(values) {
113                         if(strcmp(values[0], "session") == 0)
114                         {
115                             self->_priv->sessionid = g_strdup(values[1]);
116                         }
117                         else if (strcmp(values[0], "stream_url") == 0)
118                         {
119                             self->_priv->stream_url = g_strdup(values[1]);
120                         }
121                     }
122                     g_strfreev(values);
123                 }
124                 g_strfreev(lines);
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");
128                 }
129             }
130             else if (q->type == QUERY_TYPE_PLAYSTREAM)
131             {
132                 if(strncmp(q->data, "response=OK", strlen("response=OK")) == 0)
133                     self_play_stream(self, self->_priv->stream_url);
134                 else
135                     self_error_callback(self, FALSE,"Last.FM: Radio station is not playable");
137             }else if (q->type == QUERY_TYPE_NP)
138             {
139                 mpd_Song *song = mpd_newSong();
140                 gchar **lines = g_strsplit(q->data, "\n", 0);
141                 int i;
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)
146                         {
147                              song->artist = g_strdup(values[1]);
148                         }
149                         else if (strcmp(values[0], "album") == 0)
150                         {
151                             song->album = g_strdup(values[1]);
152                         }
153                         else if (strcmp(values[0], "track") == 0)
154                         {
155                             song->title = g_strdup(values[1]);
156                         }
157                     }
158                     g_strfreev(values);
159                 }
160                 g_strfreev(lines);
161                 if(song->artist && song->title)
162                 {
163                     self_song_info_available(self, song);
164                     }else{
165                         self_error_callback(self, FALSE,"Last.FM: Insufficient song information available");
166                         self_song_info_available(self, NULL);
167                     }
168                 mpd_freeSong(song);
169             }
170         }
172         /* error */
173         if(q->data) g_free(q->data);
174         g_free(q);
176         self_processing(self, FALSE);
177         return;
178     }
180     private
181     void
182     file_open_ready(GFile *file, GAsyncResult *res,gpointer data)
183     {
184         Query *q = 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)
191         {
192             /* Start reading the file. */
193             g_input_stream_read_async(G_INPUT_STREAM(stream), q->buffer, 
194                     BUFFER_SIZE,G_PRIORITY_DEFAULT, 
195                     NULL, 
196                     (GAsyncReadyCallback)self_file_stream_read_done, q);
197             return;
198         }
199         if(stream) g_object_unref(stream);
200         if(error) {
201             gchar *msg = g_markup_printf_escaped("Libgio failed to read Last.FM: '<i>%s</i>'", 
202                     error->message);
203             self_error_callback(self,TRUE,msg);
204             g_free(msg);
205             g_error_free(error);
206         }
208         g_object_unref(q->file);
209         g_free(q);
210         self_processing(self, FALSE);
211     }
213     private 
214     void
215     do_playstream(self, const gchar *url)
216     {
217         Query *q;
218         if(self->_priv->sessionid == NULL) return;
219         self_processing(self, TRUE);
220         q = g_malloc0(sizeof(*q)); 
221         q->self = self;
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);
225     }
229     /**
230      * Public stuff
231      */
232     public
233     Last:FM
234     *new (void)
235     {
236         return GET_NEW;
237     }
239     public
240     gboolean
241     is_connected(self)
242     {
243         return (self->_priv->sessionid != NULL && self->_priv->stream_url);
244     }
245     /**
246      * Signals
247      */
248     signal last NONE (BOOLEAN)
249     void
250     connection_changed(self, gboolean connected)
251     {
252     }
254     signal last NONE (STRING)
255     void
256     play_stream(self,gchar *stream)
257     {
258     }
260     signal last NONE (BOOLEAN)
261     void
262     processing(self, gboolean processing)
263     {
264     }
266     signal last NONE (POINTER)
267     void
268     song_info_available(self, mpd_Song *song)
269     {
271     }
272     /**
273      *
274      */
275     signal last NONE (BOOLEAN,STRING)
276     void
277     error_callback(self, gboolean fatal,const gchar *errormsg)
278     {
280     }
282     public 
283     const gchar *
284     get_username(self)
285     {
286         return self->_priv->username;
287     }
288     public
289     const gchar *
290     get_stream_url(self)
291     {
292         return self->_priv->stream_url;
293     }
295     public void
296     set_login(self, const gchar *username, const gchar *password)
297     {
298         self->_priv->username = g_strdup(username);
299         /* Calc checksum */
300         if(password) {
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);
305             if(chk)
306                 self->_priv->password_md5 = g_strdup(chk);
307             g_checksum_free(ck);
308         }
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);
311     }
313     public void
314     connect(self)
315     {
316         gchar *url;
317         Query *q;
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)); 
330         q->self = self;
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);
335         g_free(url);
336     }
337     /* 
338      * Different play things
339      */
340     public
341     void
342     play_artist_radio(self, const gchar *artist)
343     {
344         gchar *url;
345         gchar *temp;
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);
350         g_free(temp);
351         g_free(url);
352     }
353     public
354     void
355     play_similar_artist_radio(self, const gchar *artist)
356     {
357         gchar *url;
358         gchar *temp;
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);
363         g_free(temp);
364         g_free(url);
365     }
367     public
368         void
369     play_tag_radio(self, const gchar *artist)
370     {
371         gchar *url;
372         gchar *temp;
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);
377         g_free(temp);
378         g_free(url);
379     }
380    public
381         void
382     play_group_radio(self, const gchar *artist)
383     {
384         gchar *url;
385         gchar *temp;
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);
390         g_free(temp);
391         g_free(url);
392     }
393     public
394     void
395     play_user_radio(self,const gchar *artist)
396     {
397         gchar *url;
398         gchar *temp;
399         if(self->_priv->sessionid == NULL) return;
400         if(artist)
401             temp = gmpc_easy_download_uri_escape(artist);
402         else
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);
406         g_free(temp);
407         g_free(url);
408     }
409     public
410     void
411     play_neighbours_radio(self,const gchar *artist)
412     {
413         gchar *url;
414         gchar *temp;
415         if(self->_priv->sessionid == NULL) return;
416     
417         if(artist)
418             temp = gmpc_easy_download_uri_escape(artist);
419         else
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);
423         g_free(temp);
424         g_free(url);
425     }
426     public
427     void
428     play_recommendations (self)
429     {
430         gchar *url;
431         gchar *temp;
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);
436         g_free(temp);
437         g_free(url);
438     }
439     public
440     void
441     play_loved_radio (self)
442     {
443         gchar *url;
444         gchar *temp;
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);
449         g_free(temp);
450         g_free(url);
451     }
454     public
455     void
456     get_current_song (self)
457     {
458         gchar *url;
459         Query *q;
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)); 
467         q->self = self;
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);
472         g_free(url);
473     }