1 /* gmpc-last.fm (GMPC plugin)
2 * Copyright (C) 2006-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.
25 #include <glib/gi18n-lib.h>
26 #include <glib/gstdio.h>
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30 #include <gmpc/plugin.h>
31 #include <gmpc/gmpc_easy_download.h>
32 #include <gmpc/metadata.h>
34 #define LASTFM_API_KEY "ec1cdd08d574e93fa6ef9ad861ae795a"
35 #define LASTFM_API_ROOT "http://ws.audioscrobbler.com/2.0/"
38 typedef struct Query
{
40 void (*callback
)(GList
*list
, gpointer data
);
44 static int lastfm_get_enabled()
46 return cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "enable", TRUE
);
48 static void lastfm_set_enabled(int enabled
)
50 cfg_set_single_value_as_int(config
, "cover-lastfm", "enable", enabled
);
53 static int lastfm_fetch_cover_priority(void){
54 return cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "priority", 80);
56 static void lastfm_fetch_cover_priority_set(int priority
){
57 cfg_set_single_value_as_int(config
, "cover-lastfm", "priority", priority
);
60 static xmlNodePtr
get_first_node_by_name(xmlNodePtr xml
, gchar
*name
) {
61 if(name
== NULL
) return NULL
;
63 xmlNodePtr c
= xml
->xmlChildrenNode
;
65 if(c
->name
&& xmlStrEqual(c
->name
, (xmlChar
*) name
))
72 static GList
*__lastfm_art_xml_get_artist_image(const char *data
, gint size
, MetaDataType mtype
)
76 if(size
<= 0 || data
== NULL
|| data
[0] != '<')
79 doc
= xmlParseMemory(data
,size
);
82 xmlNodePtr root
= xmlDocGetRootElement(doc
);
85 /* loop through all albums */
86 xmlNodePtr cur
= get_first_node_by_name(root
,"images");
89 xmlNodePtr cur2
= cur
->xmlChildrenNode
;
90 for(;cur2
;cur2
= cur2
->next
)
94 if (xmlStrEqual(cur2
->name
, (xmlChar
*)"image"))
96 xmlNodePtr cur3
= cur2
->xmlChildrenNode
;
97 for(;cur3
;cur3
= cur3
->next
)
99 if(xmlStrEqual(cur3
->name
, (xmlChar
*)"sizes"))
101 xmlNodePtr cur4
= cur3
->xmlChildrenNode
;
102 for(;cur4
;cur4
= cur4
->next
)
104 if(xmlStrEqual(cur4
->name
, (xmlChar
*)"size"))
106 xmlChar
*temp
= xmlGetProp(cur4
, (xmlChar
*)"name");
110 * We want large image, but if that is not available, get the medium one
112 if(xmlStrEqual(temp
, (xmlChar
*)"original"))
114 xmlChar
*xurl
= xmlNodeGetContent(cur4
);
116 if(strstr((char *)xurl
, "noartist") == NULL
){
117 MetaData
*mtd
= meta_data_new();
119 mtd
->plugin_name
= plugin
.name
;
120 mtd
->content_type
= META_DATA_CONTENT_URI
;
121 mtd
->content
= g_strdup((char *)xurl
); mtd
->size
= 0;
122 list
=g_list_prepend(list
, mtd
);
126 }/*else if(xmlStrEqual(temp, (xmlChar *)"large") || xmlStrEqual(temp, (xmlChar *)"extralarge"))
128 xmlChar *xurl = xmlNodeGetContent(cur2);
131 if(strstr((char *)xurl, "noartist") == NULL){
132 MetaData *mtd = meta_data_new();
134 mtd->plugin_name = plugin.name;
135 mtd->content_type = META_DATA_CONTENT_URI;
136 mtd->content = g_strdup((char *)xurl); mtd->size = 0;
137 list =g_list_prepend(list, mtd);
155 return g_list_reverse(list
);
157 static GList
* __lastfm_art_xml_get_image(const char* data
, gint size
, char* type
, MetaDataType mtype
)
161 if(size
<= 0 || data
== NULL
|| data
[0] != '<')
164 doc
= xmlParseMemory(data
,size
);
167 xmlNodePtr root
= xmlDocGetRootElement(doc
);
170 /* loop through all albums */
171 xmlNodePtr cur
= get_first_node_by_name(root
,type
);
174 xmlNodePtr cur2
= cur
->xmlChildrenNode
;
175 for(;cur2
;cur2
= cur2
->next
)
179 if (xmlStrEqual(cur2
->name
, (xmlChar
*)"image"))
182 xmlChar
*temp
= xmlGetProp(cur2
, (xmlChar
*)"size");
186 * We want large image, but if that is not available, get the medium one
188 if(xmlStrEqual(temp
, (xmlChar
*)"medium"))
190 xmlChar
*xurl
= xmlNodeGetContent(cur2
);
192 if(strstr((char *)xurl
, "noartist") == NULL
){
193 MetaData
*mtd
= meta_data_new();
195 mtd
->plugin_name
= plugin
.name
;
196 mtd
->content_type
= META_DATA_CONTENT_URI
;
197 mtd
->content
= g_strdup((char *)xurl
); mtd
->size
= 0;
198 list
=g_list_append(list
, mtd
);
202 }else if(xmlStrEqual(temp
, (xmlChar
*)"large") || xmlStrEqual(temp
, (xmlChar
*)"extralarge"))
204 xmlChar
*xurl
= xmlNodeGetContent(cur2
);
207 if(strstr((char *)xurl
, "noartist") == NULL
){
208 MetaData
*mtd
= meta_data_new();
210 mtd
->plugin_name
= plugin
.name
;
211 mtd
->content_type
= META_DATA_CONTENT_URI
;
212 mtd
->content
= g_strdup((char *)xurl
); mtd
->size
= 0;
213 list
=g_list_prepend(list
, mtd
);
230 /* get similar genres */
231 static MetaData
* __lastfm_art_xml_get_genre_similar(const gchar
* l_data
, gint l_size
)
233 if(l_size
<= 0 || l_data
== NULL
|| l_data
[0] != '<')
236 MetaData
* mtd
= NULL
;
237 xmlDocPtr doc
= xmlParseMemory(l_data
, l_size
);
240 xmlNodePtr root
= xmlDocGetRootElement(doc
);
241 xmlNodePtr cur
= get_first_node_by_name(root
, "similartags");
244 xmlNodePtr cur2
= cur
->xmlChildrenNode
;
245 for(; cur2
!= NULL
; cur2
= cur2
->next
)
247 if(xmlStrEqual(cur2
->name
, (xmlChar
*) "tag"))
249 xmlNodePtr cur3
= cur2
->xmlChildrenNode
;
250 for(; cur3
!= NULL
; cur3
= cur3
->next
)
252 if(xmlStrEqual(cur3
->name
, (xmlChar
*) "name"))
254 xmlChar
* temp
= xmlNodeGetContent(cur3
);
259 mtd
= meta_data_new();
260 mtd
->type
= META_GENRE_SIMILAR
;
261 mtd
->plugin_name
= plugin
.name
;
262 mtd
->content_type
= META_DATA_CONTENT_TEXT_LIST
;
266 mtd
->content
= g_list_prepend((GList
*) mtd
->content
, g_strdup((char *)temp
));
275 mtd
->content
= g_list_reverse((GList
*) mtd
->content
); /* to have the match-order */
286 static MetaData
* __lastfm_art_xml_get_artist_similar(const gchar
* data
, gint size
)
288 if(size
<= 0 || data
== NULL
|| data
[0] != '<')
291 MetaData
*mtd
= NULL
;
292 xmlDocPtr doc
= xmlParseMemory(data
,size
);
295 xmlNodePtr root
= xmlDocGetRootElement(doc
);
296 xmlNodePtr cur
= get_first_node_by_name(root
, "similarartists");
299 xmlNodePtr cur2
= cur
->xmlChildrenNode
;
300 for(;cur2
;cur2
=cur2
->next
)
302 if(xmlStrEqual(cur2
->name
, (xmlChar
*)"artist"))
304 xmlNodePtr cur3
= cur2
->xmlChildrenNode
;
305 for(;cur3
;cur3
=cur3
->next
)
307 if(xmlStrEqual(cur3
->name
, (xmlChar
*)"name"))
309 xmlChar
*temp
= xmlNodeGetContent(cur3
);
313 mtd
= meta_data_new();
314 mtd
->type
= META_ARTIST_SIMILAR
;
315 mtd
->plugin_name
= plugin
.name
;
316 mtd
->content_type
= META_DATA_CONTENT_TEXT_LIST
;
320 mtd
->content
= g_list_prepend((GList
*) mtd
->content
, g_strdup((char *)temp
));
328 mtd
->content
= g_list_reverse((GList
*) mtd
->content
);
339 static MetaData
* __lastfm_art_xml_get_song_similar(const gchar
* data
, gint size
)
341 if(size
<= 0 || data
== NULL
|| data
[0] != '<')
344 MetaData
*mtd
= NULL
;
345 xmlDocPtr doc
= xmlParseMemory(data
,size
);
348 xmlNodePtr root
= xmlDocGetRootElement(doc
);
349 xmlNodePtr cur
= get_first_node_by_name(root
, "similartracks");
352 xmlNodePtr cur2
= cur
->xmlChildrenNode
;
353 for(;cur2
;cur2
=cur2
->next
)
355 if(xmlStrEqual(cur2
->name
, (xmlChar
*)"track"))
357 xmlNodePtr cur3
= cur2
->xmlChildrenNode
;
358 xmlChar
*artist
= NULL
;
359 xmlChar
*title
= NULL
;
360 for(;cur3
;cur3
=cur3
->next
)
362 if(xmlStrEqual(cur3
->name
, (xmlChar
*)"name"))
364 xmlChar
*temp
= xmlNodeGetContent(cur3
);
367 else if (xmlStrEqual(cur3
->name
, (xmlChar
*)"artist"))
369 xmlNodePtr cur4
= get_first_node_by_name(cur3
, "name");
371 xmlChar
*temp
= xmlNodeGetContent(cur4
);
376 if(artist
&& title
) {
378 mtd
= meta_data_new();
379 mtd
->type
= META_SONG_SIMILAR
;
380 mtd
->plugin_name
= plugin
.name
;
381 mtd
->content_type
= META_DATA_CONTENT_TEXT_LIST
;
385 mtd
->content
= g_list_prepend((GList
*) mtd
->content
, g_strdup_printf("%s::%s", artist
, title
));
387 if(artist
) xmlFree(artist
);
388 if(title
) xmlFree(title
);
392 mtd
->content
= g_list_reverse((GList
*) mtd
->content
);
409 static gchar
* __lastfm_art_xml_get_artist_bio(const gchar
* data
, gint size
)
411 xmlDocPtr doc
= xmlParseMemory(data
,size
);
415 xmlNodePtr root
= xmlDocGetRootElement(doc
);
416 xmlNodePtr bio
= get_first_node_by_name(get_first_node_by_name(get_first_node_by_name(root
,"artist"),"bio"),"content");
419 xmlChar
*temp
= xmlNodeGetContent(bio
);
420 info
= g_strdup((gchar
*) temp
);
433 static void pref_destroy(GtkWidget
*con
)
435 GtkWidget
*child
= gtk_bin_get_child(GTK_BIN(con
));
437 gtk_container_remove(GTK_CONTAINER(con
), child
);
441 static void pref_enable_fetch(GtkWidget
*con
, gpointer data
)
443 MetaDataType type
= GPOINTER_TO_INT(data
);
444 int state
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(con
));
446 case META_ARTIST_ART
:
447 cfg_set_single_value_as_int(config
, "cover-lastfm", "fetch-art-artist",state
);
450 cfg_set_single_value_as_int(config
, "cover-lastfm", "fetch-art-album",state
);
452 case META_ARTIST_SIMILAR
:
453 cfg_set_single_value_as_int(config
, "cover-lastfm", "fetch-similar-artist",state
);
455 case META_SONG_SIMILAR
:
456 cfg_set_single_value_as_int(config
, "cover-lastfm", "fetch-similar-song",state
);
458 case META_GENRE_SIMILAR
:
459 cfg_set_single_value_as_int(config
, "cover-lastfm", "fetch-similar-genre", state
);
461 case META_ARTIST_TXT
:
462 cfg_set_single_value_as_int(config
, "cover-lastfm", "fetch-biography-artist",state
);
470 static void pref_construct(GtkWidget
*con
)
472 GtkWidget
*frame
,*vbox
;
473 GtkWidget
*a_a_ck
, *a_b_ck
, *a_s_ck
,*c_a_ck
, *s_s_ck
, *s_g_ck
;
476 * Enable/Disable checkbox
478 frame
= gtk_frame_new("");
479 gtk_label_set_markup(GTK_LABEL(gtk_frame_get_label_widget(GTK_FRAME(frame
))), "<b>Fetch</b>");
480 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_NONE
);
481 vbox
= gtk_vbox_new(FALSE
,6);
482 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 12);
483 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
485 /* Fetch artist art */
486 a_a_ck
= gtk_check_button_new_with_label(_("Artist images"));
487 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(a_a_ck
),
488 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-art-artist", TRUE
));
489 gtk_box_pack_start(GTK_BOX(vbox
), a_a_ck
, FALSE
, TRUE
, 0);
490 g_signal_connect(G_OBJECT(a_a_ck
), "toggled", G_CALLBACK(pref_enable_fetch
), GINT_TO_POINTER(META_ARTIST_ART
));
492 /* Fetch artist text*/
493 a_b_ck
= gtk_check_button_new_with_label(_("Artist biography"));
494 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(a_b_ck
),
495 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-biography-artist", TRUE
));
496 gtk_box_pack_start(GTK_BOX(vbox
), a_b_ck
, FALSE
, TRUE
, 0);
497 g_signal_connect(G_OBJECT(a_b_ck
), "toggled", G_CALLBACK(pref_enable_fetch
), GINT_TO_POINTER(META_ARTIST_TXT
));
499 /* Fetch similar artists */
500 a_s_ck
= gtk_check_button_new_with_label(_("Similar artists"));
501 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(a_s_ck
),
502 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-similar-artist", TRUE
));
503 gtk_box_pack_start(GTK_BOX(vbox
), a_s_ck
, FALSE
, TRUE
, 0);
504 g_signal_connect(G_OBJECT(a_s_ck
), "toggled", G_CALLBACK(pref_enable_fetch
), GINT_TO_POINTER(META_ARTIST_SIMILAR
));
506 /* Fetch artist art */
507 c_a_ck
= gtk_check_button_new_with_label(_("Album cover"));
508 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(c_a_ck
),
509 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-art-album", TRUE
));
510 gtk_box_pack_start(GTK_BOX(vbox
), c_a_ck
, FALSE
, TRUE
, 0);
511 g_signal_connect(G_OBJECT(c_a_ck
), "toggled", G_CALLBACK(pref_enable_fetch
), GINT_TO_POINTER(META_ALBUM_ART
));
513 /* Fetch similar songs */
514 s_s_ck
= gtk_check_button_new_with_label(_("Similar songs"));
515 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(s_s_ck
),
516 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-similar-song", TRUE
));
517 gtk_box_pack_start(GTK_BOX(vbox
), s_s_ck
, FALSE
, TRUE
, 0);
518 g_signal_connect(G_OBJECT(s_s_ck
), "toggled", G_CALLBACK(pref_enable_fetch
), GINT_TO_POINTER(META_SONG_SIMILAR
));
520 /* Fetch similar genre */
521 s_g_ck
= gtk_check_button_new_with_label(_("Similar genres"));
522 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(s_g_ck
),
523 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-similar-genre", TRUE
));
524 gtk_box_pack_start(GTK_BOX(vbox
), s_g_ck
, FALSE
, TRUE
, 0);
525 g_signal_connect(G_OBJECT(s_g_ck
), "toggled", G_CALLBACK(pref_enable_fetch
), GINT_TO_POINTER(META_GENRE_SIMILAR
));
528 if(!lastfm_get_enabled()) {
529 gtk_widget_set_sensitive(GTK_WIDGET(vbox
), FALSE
);
532 gtk_widget_show_all(frame
);
533 gtk_container_add(GTK_CONTAINER(con
), frame
);
538 static void similar_song_callback(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer user_data
)
540 Query
*q
= (Query
*)user_data
;
542 if(status
== GEAD_PROGRESS
) return;
543 if(status
== GEAD_DONE
)
546 const char *data
= gmpc_easy_handler_get_data(handle
, &size
);
547 MetaData
*mtd
= __lastfm_art_xml_get_song_similar((char *)data
,(gint
)size
);
549 list
= g_list_append(list
, mtd
);
552 q
->callback(list
, q
->user_data
);
553 g_slice_free(Query
, q
);
558 static void similar_artist_callback(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer user_data
)
560 Query
*q
= (Query
*)user_data
;
562 if(status
== GEAD_PROGRESS
) return;
563 if(status
== GEAD_DONE
)
566 const gchar
* data
= gmpc_easy_handler_get_data(handle
, &size
);
567 MetaData
*mtd
= __lastfm_art_xml_get_artist_similar(data
, size
);
569 list
= g_list_append(list
, mtd
);
572 q
->callback(list
, q
->user_data
);
573 g_slice_free(Query
, q
);
578 static void similar_genre_callback(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer user_data
)
580 if(status
== GEAD_PROGRESS
)
583 Query
* q
= (Query
*) user_data
;
585 if(status
== GEAD_DONE
)
588 const gchar
* data
= gmpc_easy_handler_get_data(handle
, &size
);
589 MetaData
* mtd
= __lastfm_art_xml_get_genre_similar(data
, size
);
591 list
= g_list_append(list
, mtd
);
593 q
->callback(list
, q
->user_data
);
594 g_slice_free(Query
, q
);
597 * Get biograpy new style
600 static void biography_callback(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer user_data
)
602 Query
*q
= (Query
*)user_data
;
604 if(status
== GEAD_PROGRESS
) return;
605 if(status
== GEAD_DONE
)
608 const gchar
*data
= gmpc_easy_handler_get_data(handle
, &size
);
609 char* url
= __lastfm_art_xml_get_artist_bio(data
, size
);
615 for(j
=0; j
< strlen(url
);j
++)
617 if(url
[j
] == '<') depth
++;
618 else if(url
[j
] == '>' && depth
) depth
--;
621 /* Quick and dirty html unescape*/
622 if(strncasecmp(&url
[j
], "<", 4) == 0){
626 }else if (strncasecmp(&url
[j
], ">", 4) == 0){
630 }else if (strncasecmp(&url
[j
], """, 6) == 0){
634 }else if (strncasecmp(&url
[j
], "&", 5) == 0){
647 MetaData
*mtd
= meta_data_new();
648 mtd
->type
= META_ARTIST_TXT
;
649 mtd
->plugin_name
= plugin
.name
;
650 mtd
->content_type
= META_DATA_CONTENT_TEXT
;
653 list
= g_list_append(list
, mtd
);
659 q
->callback(list
, q
->user_data
);
660 g_slice_free(Query
, q
);
663 * Get album images new style
666 static void album_image_callback(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer user_data
)
668 Query
*q
= (Query
*)user_data
;
670 if(status
== GEAD_PROGRESS
) return;
671 if(status
== GEAD_DONE
)
674 const gchar
* data
= gmpc_easy_handler_get_data(handle
, &size
);
675 list
= __lastfm_art_xml_get_image(data
, size
, "album", META_ALBUM_ART
);
677 q
->callback(list
, q
->user_data
);
678 g_slice_free(Query
, q
);
681 * Get artist image new style
683 static void artist_image_callback(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer user_data
)
685 Query
*q
= (Query
*)user_data
;
687 if(status
== GEAD_PROGRESS
) return;
688 if(status
== GEAD_DONE
)
691 const gchar
* data
= gmpc_easy_handler_get_data(handle
, &size
);
692 list
= __lastfm_art_xml_get_artist_image(data
, size
, META_ARTIST_ART
);
695 q
->callback(list
, q
->user_data
);
696 g_slice_free(Query
, q
);
699 static void lastfm_fetch_get_uris(mpd_Song
*song
, MetaDataType type
, void (*callback
)(GList
*list
, gpointer data
), gpointer user_data
)
701 g_debug("Query last.fm api v2");
702 if(song
->artist
!= NULL
&& type
== META_ARTIST_ART
&&
703 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-art-artist", TRUE
))
706 gchar
*artist
= gmpc_easy_download_uri_escape(song
->artist
);
707 Query
*q
= g_slice_new0(Query
);
709 q
->callback
= callback
;
710 q
->user_data
= user_data
;
711 snprintf(furl
,1024,LASTFM_API_ROOT
"?method=artist.getImages&artist=%s&api_key=%s", artist
,LASTFM_API_KEY
);
712 g_debug("url: '%s'", furl
);
713 gmpc_easy_async_downloader(furl
, artist_image_callback
, q
);
717 else if (song
->artist
!= NULL
&& song
->album
!= NULL
&& type
== META_ALBUM_ART
&&
718 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-art-album", TRUE
))
721 gchar
*artist
= gmpc_easy_download_uri_escape(song
->artist
);
722 gchar
*album
= gmpc_easy_download_uri_escape(song
->album
);
723 Query
*q
= g_slice_new0(Query
);
725 q
->callback
= callback
;
726 q
->user_data
= user_data
;
727 snprintf(furl
,1024,LASTFM_API_ROOT
"?method=album.getinfo&artist=%s&album=%s&api_key=%s", artist
,album
,LASTFM_API_KEY
);
728 g_debug("url: '%s'", furl
);
729 gmpc_easy_async_downloader(furl
, album_image_callback
, q
);
735 /* Fetch artist info */
736 else if (song
->artist
!= NULL
&& type
== META_ARTIST_TXT
&&
737 cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-biography-artist", TRUE
))
741 gchar
*artist
= gmpc_easy_download_uri_escape(song
->artist
);
742 Query
*q
= g_slice_new0(Query
);
744 q
->callback
= callback
;
745 q
->user_data
= user_data
;
746 snprintf(furl
,1024, LASTFM_API_ROOT
"?method=artist.getinfo&artist=%s&api_key=%s", artist
,LASTFM_API_KEY
);
747 g_debug("url: '%s'", furl
);
748 gmpc_easy_async_downloader(furl
, biography_callback
, q
);
753 else if (song
->artist
!= NULL
&& type
== META_ARTIST_SIMILAR
754 && cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-similar-artist", TRUE
))
757 char *artist
= gmpc_easy_download_uri_escape(song
->artist
);
758 Query
*q
= g_slice_new0(Query
);
760 q
->callback
= callback
;
761 q
->user_data
= user_data
;
762 snprintf(furl
,1024,LASTFM_API_ROOT
"?method=artist.getsimilar&artist=%s&api_key=%s", artist
,LASTFM_API_KEY
);
763 g_debug("url: '%s'", furl
);
765 gmpc_easy_async_downloader(furl
, similar_artist_callback
, q
);
768 else if (song
->genre
!= NULL
&& type
== META_GENRE_SIMILAR
769 && cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-similar-genre", TRUE
))
771 Query
*q
= g_slice_new0(Query
);
772 q
->callback
= callback
;
773 q
->user_data
= user_data
;
775 gchar
* genre
= gmpc_easy_download_uri_escape(song
->genre
);
776 gchar
* furl
= g_strdup_printf(LASTFM_API_ROOT
"?method=tag.getsimilar&tag=%s&api_key=%s", genre
, LASTFM_API_KEY
);
777 g_debug("url: '%s'", furl
);
778 gmpc_easy_async_downloader(furl
, similar_genre_callback
, q
);
783 }else if (song
->title
!= NULL
&& song
->artist
!= NULL
&& type
== META_SONG_SIMILAR
&& cfg_get_single_value_as_int_with_default(config
, "cover-lastfm", "fetch-similar-song", TRUE
))
787 char *artist
= gmpc_easy_download_uri_escape(song
->artist
);
788 char *title
= gmpc_easy_download_uri_escape(song
->title
);
789 Query
*q
= g_slice_new0(Query
);
791 q
->callback
= callback
;
792 q
->user_data
= user_data
;
793 snprintf(furl
,1024,LASTFM_API_ROOT
"?method=track.getsimilar&artist=%s&track=%s&api_key=%s", artist
,title
,LASTFM_API_KEY
);
794 g_debug("url: '%s'", furl
);
796 gmpc_easy_async_downloader(furl
, similar_song_callback
, q
);
800 callback(NULL
, user_data
);
802 gmpcPrefPlugin pref
= {
803 .construct
= pref_construct
,
804 .destroy
= pref_destroy
810 gmpcMetaDataPlugin lf_cover
= {
811 .get_priority
= lastfm_fetch_cover_priority
,
812 .set_priority
= lastfm_fetch_cover_priority_set
,
813 .get_metadata
= lastfm_fetch_get_uris
816 int plugin_api_version
= PLUGIN_API_VERSION
;
817 static void lastfm_init(void)
819 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
820 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
822 static const gchar
*lastfm_get_translation_domain(void)
824 return GETTEXT_PACKAGE
;
826 gmpcPlugin plugin
= {
827 .name
= N_("Last FM metadata fetcher"),
828 .version
= {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
},
829 .plugin_type
= GMPC_PLUGIN_META_DATA
,
831 .metadata
= &lf_cover
,
833 .get_enabled
= lastfm_get_enabled
,
834 .set_enabled
= lastfm_set_enabled
,
835 .get_translation_domain
= lastfm_get_translation_domain
838 /* vim:set ts=4 sw=4: */