4 #include <libxml/parser.h>
5 #include <libxml/tree.h>
6 #include <libmpd/debug_printf.h>
7 #include <gmpc/plugin.h>
8 #include <gmpc/gmpc_easy_download.h>
10 #define AMAZONKEY "14TC04B24356BPHXW1R2"
12 static char * host
= "http://ecs.amazonaws.%s/onca/xml?Service=AWSECommerceService&Operation=ItemSearch&SearchIndex=Music&ResponseGroup=Images,EditorialReview&AWSAccessKeyId=%s&%s=%s&%s=%s";
14 static void amazon_cover_art_pref_construct(GtkWidget
*container
);
15 static void amazon_cover_art_pref_destroy(GtkWidget
*container
);
16 static int fetch_cover_art(mpd_Song
*song
,int type
, char **url
);
17 static int fetch_cover_priority();
19 static int fetch_metadata(mpd_Song
*song
,MetaDataType type
, char **path
);
21 static void amazon_set_enabled(int enabled
);
22 static int amazon_get_enabled();
25 static char *endpoints
[ENDPOINTS
][2] =
27 {"com", "United States"},
28 {"co.uk", "United Kingdom"},
35 static GtkWidget
*wp_pref_vbox
= NULL
;
37 typedef struct amazon_song_info
{
48 static int amazon_get_enabled()
50 return cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "enable", TRUE
);
52 static void amazon_set_enabled(int enabled
)
54 cfg_set_single_value_as_int(config
, "cover-amazon", "enable", enabled
);
57 static int fetch_cover_priority(){
58 return cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "priority", 80);
62 static int fetch_metadata(mpd_Song
*song
,MetaDataType type
, char **path
)
66 if(song
->artist
== NULL
|| song
->album
== NULL
)
68 return META_DATA_UNAVAILABLE
;
70 if(type
!= META_ALBUM_ART
&& type
!= META_ALBUM_TXT
) {
71 return META_DATA_UNAVAILABLE
;
73 /* Always fetch it. */
74 fetch_cover_art(song
,type
, &url
);
77 return META_DATA_AVAILABLE
;
81 return META_DATA_UNAVAILABLE
;
84 static amazon_song_info
* amazon_song_info_new()
86 amazon_song_info
*asi
= g_malloc(sizeof(amazon_song_info
));
87 asi
->image_big
= NULL
;
88 asi
->image_medium
= NULL
;
89 asi
->image_small
= NULL
;
90 asi
->album_info
= NULL
;
93 static void amazon_song_info_free(amazon_song_info
*asi
)
95 if(asi
->image_big
!= NULL
) g_free(asi
->image_big
);
96 if(asi
->image_medium
!= NULL
) g_free(asi
->image_medium
);
97 if(asi
->image_small
!= NULL
) g_free(asi
->image_small
);
98 if(asi
->album_info
!= NULL
) g_free(asi
->album_info
);
103 static xmlNodePtr
get_first_node_by_name(xmlNodePtr xml
, gchar
*name
) {
105 xmlNodePtr c
= xml
->xmlChildrenNode
;
107 if(xmlStrEqual(c
->name
, (xmlChar
*) name
))
114 static amazon_song_info
*__cover_art_xml_get_image(char *data
,int size
)
116 xmlDocPtr doc
= xmlParseMemory(data
,size
);
119 xmlNodePtr root
= xmlDocGetRootElement(doc
);
120 xmlNodePtr cur
= get_first_node_by_name(root
, "Items");
121 amazon_song_info
*asi
= NULL
;
123 cur
= get_first_node_by_name(cur
, "Item");
125 xmlNodePtr child
= NULL
;
126 asi
= amazon_song_info_new();
127 if((child
= get_first_node_by_name(cur
, "LargeImage"))) {
128 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "URL"));
129 /* copy it, so we can free it, and don't need xmlFree */
130 asi
->image_big
= g_strdup((char *)temp
);
133 if((child
= get_first_node_by_name(cur
, "MediumImage"))){
134 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "URL"));
135 asi
->image_medium
= g_strdup((char *)temp
);
138 if((child
= get_first_node_by_name(cur
, "SmallImage"))){
139 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "URL"));
140 asi
->image_small
= g_strdup((char *)temp
);
144 if((child
= get_first_node_by_name(cur
, "EditorialReviews"))) {
145 child
= get_first_node_by_name(child
, "EditorialReview");
147 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "Content")); /* ugy, lazy */
148 asi
->album_info
= g_strdup((char *)temp
);
162 static int __fetch_metadata_amazon(char *sfield
, char *nartist
, char *stype
,char *nalbum
,int type
, char **url
)
165 gmpc_easy_download_struct data
= {NULL
, 0,-1, NULL
, NULL
};
168 int endpoint
= cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "location", 0);
169 char *endp
= endpoints
[endpoint
][0];
171 debug_printf(DEBUG_INFO
, "search-type: %s\n", stype
);
172 snprintf(furl
,1024, host
,endp
, AMAZONKEY
,sfield
, nartist
, stype
, nalbum
);
173 printf("amazon url: '%s'\n", furl
);
174 if(gmpc_easy_download(furl
, &data
))
176 amazon_song_info
*asi
= __cover_art_xml_get_image(data
.data
, data
.size
);
177 gmpc_easy_download_clean(&data
);
180 if(type
&META_ALBUM_ART
)
182 debug_printf(DEBUG_INFO
, "Trying to fetch album art");
183 gmpc_easy_download(asi
->image_big
, &data
);
184 if(data
.size
<= 900){
185 gmpc_easy_download_clean(&data
);
186 gmpc_easy_download(asi
->image_medium
, &data
);
187 if(data
.size
<= 900){
188 gmpc_easy_download_clean(&data
);
189 gmpc_easy_download(asi
->image_small
, &data
);
192 gmpc_easy_download_clean(&data
);
198 gchar
*imgpath
= NULL
;
199 gchar
*filename
= g_strdup_printf("%s-%s.jpg",nartist
,nalbum
);
200 imgpath
= gmpc_get_covers_path(filename
);
202 fp
= fopen(imgpath
, "wb");
205 fwrite(data
.data
, sizeof(char), data
.size
,fp
);
206 *url
= g_strdup(imgpath
);
212 gmpc_easy_download_clean(&data
);
216 else if(type
&META_ALBUM_TXT
)
218 debug_printf(DEBUG_INFO
, "Trying to fetch album txt");
222 gchar
*filename
, *imgpath
;
223 filename
= g_strdup_printf("%s-%s.albuminfo",nartist
,nalbum
);
224 imgpath
= gmpc_get_covers_path(filename
);
226 fp
= fopen(imgpath
, "w");
230 *url
= g_strdup(imgpath
);
232 * Quick 'n Dirty html stripper
234 for(j
=0; j
< strlen(asi
->album_info
);j
++)
236 if((asi
->album_info
)[j
] == '<') depth
++;
237 else if((asi
->album_info
)[j
] == '>' && depth
) depth
--;
240 fputc((asi
->album_info
)[j
],fp
);
250 amazon_song_info_free(asi
);
256 static int fetch_cover_art(mpd_Song
*song
,int type
, char **url
)
259 gchar
*artist
= g_uri_escape_string(song
->artist
,NULL
, TRUE
);
260 gchar
*album
= g_uri_escape_string(song
->album
,NULL
, TRUE
);
262 retval
= __fetch_metadata_amazon(
266 if(!retval
&& song
->performer
)
268 gchar
*performer
= g_uri_escape_string(song
->performer
, NULL
, TRUE
);
269 retval
= __fetch_metadata_amazon(
271 "Performer", performer
,
285 static void amazon_cover_art_enable_toggle(GtkWidget
*wid
)
287 int kk
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid
));
288 amazon_set_enabled(kk
);
290 static void amazon_cover_art_pref_destroy(GtkWidget
*container
)
292 gtk_container_remove(GTK_CONTAINER(container
), wp_pref_vbox
);
294 static void amazon_cover_art_pref_selection_changed(GtkWidget
*box
)
296 cfg_set_single_value_as_int(config
, "cover-amazon", "location", gtk_combo_box_get_active(GTK_COMBO_BOX(box
)));
299 static void amazon_cover_art_pref_construct(GtkWidget
*container
)
301 GtkWidget
*enable_cg
= gtk_check_button_new_with_mnemonic("_Enable amazon as cover art source");
302 GtkWidget
*label
= NULL
;
303 GtkWidget
*selection
= NULL
;
304 GtkWidget
*hbox
= NULL
;
306 wp_pref_vbox
= gtk_vbox_new(FALSE
,6);
308 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable_cg
), amazon_get_enabled());
310 g_signal_connect(G_OBJECT(enable_cg
), "toggled", G_CALLBACK(amazon_cover_art_enable_toggle
), NULL
);
311 gtk_box_pack_start(GTK_BOX(wp_pref_vbox
), enable_cg
, FALSE
, FALSE
, 0);
314 hbox
= gtk_hbox_new(FALSE
, 6);
315 label
= gtk_label_new("Location:");
316 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
,0);
317 selection
= gtk_combo_box_new_text();
318 for(i
=0;i
<ENDPOINTS
;i
++)
319 gtk_combo_box_append_text(GTK_COMBO_BOX(selection
), endpoints
[i
][1]);
320 gtk_combo_box_set_active(GTK_COMBO_BOX(selection
),cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "location", 0));
321 g_signal_connect(G_OBJECT(selection
), "changed",G_CALLBACK(amazon_cover_art_pref_selection_changed
), NULL
);
323 gtk_box_pack_start(GTK_BOX(hbox
), selection
, TRUE
,TRUE
,0);
324 gtk_box_pack_start(GTK_BOX(wp_pref_vbox
), hbox
, FALSE
, FALSE
, 0);
326 gtk_container_add(GTK_CONTAINER(container
), wp_pref_vbox
);
327 gtk_widget_show_all(container
);
330 gmpcPrefPlugin cam_pref
= {
331 .construct
= amazon_cover_art_pref_construct
,
332 .destroy
= amazon_cover_art_pref_destroy
335 gmpcMetaDataPlugin cam_cover
= {
336 .get_priority
= fetch_cover_priority
,
337 .get_image
= fetch_metadata
340 int plugin_api_version
= PLUGIN_API_VERSION
;
341 gmpcPlugin plugin
= {
342 .name
= "Amazon Cover Fetcher",
343 .version
= {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
},
344 .plugin_type
= GMPC_PLUGIN_META_DATA
,
346 .metadata
= &cam_cover
,
347 .get_enabled
= amazon_get_enabled
,
348 .set_enabled
= amazon_set_enabled