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 int fetch_cover_art(mpd_Song
*song
,int type
, char **url
);
17 static char *endpoints
[ENDPOINTS
][2] =
19 {"com", "United States"},
20 {"co.uk", "United Kingdom"},
27 static GtkWidget
*wp_pref_vbox
= NULL
;
29 typedef struct amazon_song_info
{
40 static int amazon_get_enabled()
42 return cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "enable", TRUE
);
44 static void amazon_set_enabled(int enabled
)
46 cfg_set_single_value_as_int(config
, "cover-amazon", "enable", enabled
);
52 static int fetch_cover_priority(){
53 return cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "priority", 80);
56 static int fetch_metadata(mpd_Song
*song
,MetaDataType type
, char **path
)
60 if(song
->artist
== NULL
|| song
->album
== NULL
)
62 return META_DATA_UNAVAILABLE
;
64 if(type
!= META_ALBUM_ART
&& type
!= META_ALBUM_TXT
) {
65 return META_DATA_UNAVAILABLE
;
67 /* Always fetch it. */
68 fetch_cover_art(song
,type
, &url
);
71 return META_DATA_AVAILABLE
;
75 return META_DATA_UNAVAILABLE
;
78 static amazon_song_info
* amazon_song_info_new()
80 amazon_song_info
*asi
= g_malloc(sizeof(amazon_song_info
));
81 asi
->image_big
= NULL
;
82 asi
->image_medium
= NULL
;
83 asi
->image_small
= NULL
;
84 asi
->album_info
= NULL
;
87 static void amazon_song_info_free(amazon_song_info
*asi
)
89 if(asi
->image_big
!= NULL
) g_free(asi
->image_big
);
90 if(asi
->image_medium
!= NULL
) g_free(asi
->image_medium
);
91 if(asi
->image_small
!= NULL
) g_free(asi
->image_small
);
92 if(asi
->album_info
!= NULL
) g_free(asi
->album_info
);
97 static xmlNodePtr
get_first_node_by_name(xmlNodePtr xml
, gchar
*name
) {
99 xmlNodePtr c
= xml
->xmlChildrenNode
;
101 if(xmlStrEqual(c
->name
, (xmlChar
*) name
))
108 static amazon_song_info
*__cover_art_xml_get_image(char *data
,int size
)
110 xmlDocPtr doc
= xmlParseMemory(data
,size
);
113 xmlNodePtr root
= xmlDocGetRootElement(doc
);
114 xmlNodePtr cur
= get_first_node_by_name(root
, "Items");
115 amazon_song_info
*asi
= NULL
;
117 cur
= get_first_node_by_name(cur
, "Item");
119 xmlNodePtr child
= NULL
;
120 asi
= amazon_song_info_new();
121 if((child
= get_first_node_by_name(cur
, "LargeImage"))) {
122 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "URL"));
123 /* copy it, so we can free it, and don't need xmlFree */
124 asi
->image_big
= g_strdup((char *)temp
);
127 if((child
= get_first_node_by_name(cur
, "MediumImage"))){
128 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "URL"));
129 asi
->image_medium
= g_strdup((char *)temp
);
132 if((child
= get_first_node_by_name(cur
, "SmallImage"))){
133 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "URL"));
134 asi
->image_small
= g_strdup((char *)temp
);
138 if((child
= get_first_node_by_name(cur
, "EditorialReviews"))) {
139 child
= get_first_node_by_name(child
, "EditorialReview");
141 xmlChar
*temp
= xmlNodeGetContent(get_first_node_by_name(child
, "Content")); /* ugy, lazy */
142 asi
->album_info
= g_strdup((char *)temp
);
156 static int __fetch_metadata_amazon(char *sfield
, char *nartist
, char *stype
,char *nalbum
,int type
, char **url
)
159 gmpc_easy_download_struct data
= {NULL
, 0,-1, NULL
, NULL
};
162 int endpoint
= cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "location", 0);
163 char *endp
= endpoints
[endpoint
][0];
165 debug_printf(DEBUG_INFO
, "search-type: %s\n", stype
);
166 snprintf(furl
,1024, host
,endp
, AMAZONKEY
,sfield
, nartist
, stype
, nalbum
);
167 if(gmpc_easy_download(furl
, &data
))
169 amazon_song_info
*asi
= __cover_art_xml_get_image(data
.data
, data
.size
);
170 gmpc_easy_download_clean(&data
);
173 if(type
&META_ALBUM_ART
)
175 debug_printf(DEBUG_INFO
, "Trying to fetch album art");
176 gmpc_easy_download(asi
->image_big
, &data
);
177 if(data
.size
<= 900){
178 gmpc_easy_download_clean(&data
);
179 gmpc_easy_download(asi
->image_medium
, &data
);
180 if(data
.size
<= 900){
181 gmpc_easy_download_clean(&data
);
182 gmpc_easy_download(asi
->image_small
, &data
);
185 gmpc_easy_download_clean(&data
);
191 gchar
*imgpath
= NULL
;
192 gchar
*filename
= g_strdup_printf("%s-%s.jpg",nartist
,nalbum
);
193 imgpath
= gmpc_get_covers_path(filename
);
195 fp
= fopen(imgpath
, "wb");
198 fwrite(data
.data
, sizeof(char), data
.size
,fp
);
199 *url
= g_strdup(imgpath
);
205 gmpc_easy_download_clean(&data
);
209 else if(type
&META_ALBUM_TXT
)
211 debug_printf(DEBUG_INFO
, "Trying to fetch album txt");
215 gchar
*filename
, *imgpath
;
216 filename
= g_strdup_printf("%s-%s.albuminfo",nartist
,nalbum
);
217 imgpath
= gmpc_get_covers_path(filename
);
219 fp
= fopen(imgpath
, "w");
223 *url
= g_strdup(imgpath
);
225 * Quick 'n Dirty html stripper
227 for(j
=0; j
< strlen(asi
->album_info
);j
++)
229 if((asi
->album_info
)[j
] == '<') depth
++;
230 else if((asi
->album_info
)[j
] == '>' && depth
) depth
--;
233 fputc((asi
->album_info
)[j
],fp
);
243 amazon_song_info_free(asi
);
249 static int fetch_cover_art(mpd_Song
*song
,int type
, char **url
)
252 gchar
*artist
= g_uri_escape_string(song
->artist
,NULL
, TRUE
);
253 gchar
*album
= g_uri_escape_string(song
->album
,NULL
, TRUE
);
255 retval
= __fetch_metadata_amazon(
259 if(!retval
&& song
->performer
)
261 gchar
*performer
= g_uri_escape_string(song
->performer
, NULL
, TRUE
);
262 retval
= __fetch_metadata_amazon(
264 "Performer", performer
,
278 static void amazon_cover_art_enable_toggle(GtkWidget
*wid
)
280 int kk
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid
));
281 amazon_set_enabled(kk
);
283 static void amazon_cover_art_pref_destroy(GtkWidget
*container
)
285 gtk_container_remove(GTK_CONTAINER(container
), wp_pref_vbox
);
287 static void amazon_cover_art_pref_selection_changed(GtkWidget
*box
)
289 cfg_set_single_value_as_int(config
, "cover-amazon", "location", gtk_combo_box_get_active(GTK_COMBO_BOX(box
)));
292 static void amazon_cover_art_pref_construct(GtkWidget
*container
)
294 GtkWidget
*enable_cg
= gtk_check_button_new_with_mnemonic("_Enable amazon as cover art source");
295 GtkWidget
*label
= NULL
;
296 GtkWidget
*selection
= NULL
;
297 GtkWidget
*hbox
= NULL
;
299 wp_pref_vbox
= gtk_vbox_new(FALSE
,6);
301 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable_cg
), amazon_get_enabled());
303 g_signal_connect(G_OBJECT(enable_cg
), "toggled", G_CALLBACK(amazon_cover_art_enable_toggle
), NULL
);
304 gtk_box_pack_start(GTK_BOX(wp_pref_vbox
), enable_cg
, FALSE
, FALSE
, 0);
307 hbox
= gtk_hbox_new(FALSE
, 6);
308 label
= gtk_label_new("Location:");
309 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
,0);
310 selection
= gtk_combo_box_new_text();
311 for(i
=0;i
<ENDPOINTS
;i
++)
312 gtk_combo_box_append_text(GTK_COMBO_BOX(selection
), endpoints
[i
][1]);
313 gtk_combo_box_set_active(GTK_COMBO_BOX(selection
),cfg_get_single_value_as_int_with_default(config
, "cover-amazon", "location", 0));
314 g_signal_connect(G_OBJECT(selection
), "changed",G_CALLBACK(amazon_cover_art_pref_selection_changed
), NULL
);
316 gtk_box_pack_start(GTK_BOX(hbox
), selection
, TRUE
,TRUE
,0);
317 gtk_box_pack_start(GTK_BOX(wp_pref_vbox
), hbox
, FALSE
, FALSE
, 0);
319 gtk_container_add(GTK_CONTAINER(container
), wp_pref_vbox
);
320 gtk_widget_show_all(container
);
323 gmpcPrefPlugin cam_pref
= {
324 .construct
= amazon_cover_art_pref_construct
,
325 .destroy
= amazon_cover_art_pref_destroy
328 gmpcMetaDataPlugin cam_cover
= {
329 .get_priority
= fetch_cover_priority
,
330 .get_image
= fetch_metadata
333 int plugin_api_version
= PLUGIN_API_VERSION
;
334 gmpcPlugin plugin
= {
335 .name
= "Amazon Cover Fetcher",
336 .version
= {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
},
337 .plugin_type
= GMPC_PLUGIN_META_DATA
,
339 .metadata
= &cam_cover
,
340 .get_enabled
= amazon_get_enabled
,
341 .set_enabled
= amazon_set_enabled