Fix fetching cover art and fetch art for different releases.
[gmpc-discogs.git] / src / plugin.c
blobe01033aa22d0be69d09a47a148076780d7395f24
1 /* gmpc-discogs (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.
20 #include <stdio.h>
21 #include <string.h>
22 /* gtk and lib stuff */
23 #include <gtk/gtk.h>
24 #include <config.h>
25 #include <glib.h>
26 #include <glib/gi18n-lib.h>
27 #include <glib/gstdio.h>
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
32 #include <gmpc/plugin.h>
33 #include <gmpc/gmpc_easy_download.h>
34 #include <gmpc/metadata.h>
36 #include <config.h>
38 #define DISCOGS_API_KEY "332020810c"
39 #define DISCOGS_API_ROOT "http://www.discogs.com/"
41 #define LOG_DOMAIN "Gmpc.Provider.DiscoGS"
42 gmpcPlugin plugin;
44 typedef struct ii {
45 int type;
46 mpd_Song *song;
47 void (*callback)(GList *list, gpointer data);
48 gpointer data;
49 GList *releases;
50 GList *uris;
51 }ii;
54 static int discogs_get_enabled()
56 return cfg_get_single_value_as_int_with_default(config, "cover-discogs", "enable", TRUE);
58 static void discogs_set_enabled(int enabled)
60 cfg_set_single_value_as_int(config, "cover-discogs", "enable", enabled);
62 static int discogs_fetch_cover_priority(void){
63 return cfg_get_single_value_as_int_with_default(config, "cover-discogs", "priority", 80);
65 static void discogs_fetch_cover_priority_set(int priority){
66 cfg_set_single_value_as_int(config, "cover-discogs", "priority", priority);
69 static xmlNodePtr get_first_node_by_name(xmlNodePtr xml, gchar *name) {
70 if(name == NULL) return NULL;
71 if(xml) {
72 xmlNodePtr c = xml->xmlChildrenNode;
73 for(;c;c=c->next) {
74 if(c->name && xmlStrEqual(c->name, (xmlChar *) name))
75 return c;
78 return NULL;
81 static xmlNodePtr get_next_node_by_name(xmlNodePtr xml, gchar *name) {
82 if(name == NULL) return NULL;
83 if(xml) {
84 xmlNodePtr c = xml->next;
85 for(;c;c=c->next) {
86 if(c->name && xmlStrEqual(c->name, (xmlChar *) name))
87 return c;
90 return NULL;
93 static GList * __query_album_get_uri(mpd_Song *song, const gchar*data, gsize size)
95 GList *retv = NULL;
96 char *temp_b = g_utf8_casefold(song->album,-1);
97 xmlDocPtr doc;
98 /**
99 * Get artist name
101 if(size < 4 || strncmp(data, "<res",4))
103 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Invalid XML");
105 else
107 doc = xmlParseMemory(data,size);
108 if(doc)
110 xmlNodePtr root = xmlDocGetRootElement(doc);
111 if(root)
113 /* loop through all albums */
114 xmlNodePtr cur = get_first_node_by_name(root,"searchresults");
115 if(cur)
117 xmlNodePtr cur2 = get_first_node_by_name(cur,"result");
118 while(cur2)
120 xmlChar *temp = xmlGetProp(cur2, (xmlChar *)"type");
121 if(temp != NULL && xmlStrEqual(temp, (xmlChar *)"release"))
123 xmlNodePtr cur4 = get_first_node_by_name(cur2,"title");
124 if(cur4){
125 xmlChar *title = xmlNodeGetContent(cur4);
127 if(title)
129 char *temp_a = g_utf8_casefold((gchar *)title,-1);
130 /** Todo make this check fuzzy */
131 if(strstr((char *)temp_a, temp_b))
133 xmlNodePtr cur3 = get_first_node_by_name(cur2,"uri");
134 if(cur3){
135 xmlChar *xurl = xmlNodeGetContent(cur3);
136 retv = g_list_prepend(retv, g_strdup((char *)xurl));
137 xmlFree(xurl);
140 g_free(temp_a);
142 if(title)xmlFree(title);
145 if(temp) xmlFree(temp);
146 cur2 = get_next_node_by_name(cur2, "result");
147 }// while (Cur2)
150 xmlFreeDoc(doc);
153 g_free(temp_b);
154 return g_list_reverse(retv);
156 static GList *__query_album_get_uri_list(mpd_Song *song, const gchar*data, gsize size)
158 GList *retv = NULL;
159 xmlDocPtr doc;
160 if(size < 4 || strncmp(data, "<res",4))
162 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Invalid XML");
164 else
167 doc = xmlParseMemory(data,size);
168 if(doc)
170 xmlNodePtr root = xmlDocGetRootElement(doc);
171 if(root)
173 /* loop through all albums */
174 xmlNodePtr cur = get_first_node_by_name(root,"release");
175 if(cur)
177 xmlNodePtr cur2 = get_first_node_by_name(cur,"images");
178 if(cur2) {
179 xmlNodePtr cur3 = get_first_node_by_name(cur2,"image");
180 while(cur3){
181 xmlChar *temp = xmlGetProp(cur3, (xmlChar *)"type");
183 if(temp){
184 if(xmlStrEqual(temp, (xmlChar *)"primary"))
186 xmlChar *xurl = xmlGetProp(cur3, (xmlChar *)"uri");
187 xmlChar *size = xmlGetProp(cur3, (xmlChar *)"height");
188 MetaData *mtd = meta_data_new();
189 mtd->type = META_ALBUM_ART; mtd->plugin_name = plugin.name;
190 mtd->content_type = META_DATA_CONTENT_URI;
191 mtd->content = g_strdup((char *)xurl);
192 mtd->size = -1;
193 retv = g_list_prepend(retv,mtd);
194 if(xurl) xmlFree(xurl);
195 if(size) xmlFree(size);
196 } else if(xmlStrEqual(temp, (xmlChar *)"secondary"))
198 xmlChar *xurl = xmlGetProp(cur3, (xmlChar *)"uri");
199 xmlChar *size = xmlGetProp(cur3, (xmlChar *)"height");
201 MetaData *mtd = meta_data_new();
202 mtd->type = META_ALBUM_ART; mtd->plugin_name = plugin.name;
203 mtd->content_type = META_DATA_CONTENT_URI;
204 mtd->content = g_strdup((char *)xurl);
205 mtd->size = -1;
206 retv = g_list_append(retv,mtd);
207 if(xurl) xmlFree(xurl);
208 if(size) xmlFree(size);
211 xmlFree(temp);
213 cur3 = cur3->next;
218 xmlFreeDoc(doc);
221 return retv;
224 static void __query_get_album_art_uris(const GEADAsyncHandler *handle, GEADStatus status, gpointer data)
226 ii *i = (ii *) data;
227 if(status == GEAD_PROGRESS) return;
228 if(status == GEAD_DONE) {
229 goffset size;
230 const gchar *data = gmpc_easy_handler_get_data(handle, &size);
231 GList *list = __query_album_get_uri_list(i->song, data, (gsize)size);
232 i->uris = g_list_concat(i->uris, list);
234 if(i->releases) {
235 int j=0;
236 char furl[1024];
237 char *artist_uri = i->releases->data;
238 /* Hack to fix bug in discogs api */
239 for(j=strlen(artist_uri); artist_uri[j] != '/' && j > 0; j--);
240 snprintf(furl,1024,DISCOGS_API_ROOT"release%s?f=xml&api_key=%s", &artist_uri[j],DISCOGS_API_KEY);
241 i->releases = g_list_delete_link(i->releases, i->releases);
242 g_free(artist_uri);
243 gmpc_easy_async_downloader(furl, __query_get_album_art_uris, i);
244 return;
247 i->callback(i->uris, i->data);
248 g_free(i);
249 return;
251 i->callback(NULL, i->data);
252 g_free(i);
254 static void __query_get_album_art(const GEADAsyncHandler *handle, GEADStatus status, gpointer data)
256 ii *i = (ii *) data;
257 if(status == GEAD_PROGRESS) return;
258 if(status == GEAD_DONE) {
259 goffset size;
260 char furl[1024];
261 const gchar *data = gmpc_easy_handler_get_data(handle, &size);
262 i->releases =g_list_first( __query_album_get_uri(i->song, data, (gsize)(size)));
263 if(i->releases)
265 int j=0;
266 char *artist_uri = i->releases->data;
267 /* Hack to fix bug in discogs api */
268 for(j=strlen(artist_uri); artist_uri[j] != '/' && j > 0; j--);
269 snprintf(furl,1024,DISCOGS_API_ROOT"release%s?f=xml&api_key=%s", &artist_uri[j],DISCOGS_API_KEY);
270 i->releases = g_list_delete_link(i->releases, i->releases);
271 g_free(artist_uri);
272 gmpc_easy_async_downloader(furl, __query_get_album_art_uris, i);
273 return;
276 i->callback(NULL, i->data);
277 g_free(i);
280 /** other */
281 static void discogs_fetch_cover_album_art(ii *i)
283 char *artist = gmpc_easy_download_uri_escape(i->song->artist);
284 char *album = gmpc_easy_download_uri_escape(i->song->album);
285 g_log(LOG_DOMAIN, G_LOG_LEVEL_INFO, "Trying to fetch: %s:%s\n", artist, album);
286 char furl[1024];
287 snprintf(furl,1024,DISCOGS_API_ROOT"search?type=all&f=xml&q=%s%%20%s&api_key=%s", artist,album,DISCOGS_API_KEY);
288 gmpc_easy_async_downloader(furl, __query_get_album_art, i);
290 g_free(artist);
291 g_free(album);
294 * Artist
297 static gchar * __query_artist_get_uri(mpd_Song *song, const gchar*data, gsize size)
299 char *retv = NULL;
300 xmlDocPtr doc;
302 * Get artist name
304 if(size < 4 || strncmp(data, "<res",4))
306 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Invalid XML");
308 else
310 doc = xmlParseMemory(data,size);
311 if(doc)
313 xmlNodePtr root = xmlDocGetRootElement(doc);
314 if(root)
316 /* loop through all albums */
317 xmlNodePtr cur = get_first_node_by_name(root,"exactresults");
318 if(cur)
320 xmlNodePtr cur2 = get_first_node_by_name(cur,"result");
321 if(cur2) {
322 xmlNodePtr cur3 = get_first_node_by_name(cur2,"uri");
323 if(cur3){
324 xmlChar *xurl = xmlNodeGetContent(cur3);
325 retv = g_strdup((char *)xurl);
326 xmlFree(xurl);
331 xmlFreeDoc(doc);
335 return retv;
337 static GList *__query_artist_get_uri_list(mpd_Song *song, const gchar*data, gsize size)
339 GList *retv = NULL;
340 xmlDocPtr doc;
341 if(size < 4 || strncmp(data, "<res",4))
343 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Invalid XML");
345 else
347 doc = xmlParseMemory(data,size);
348 if(doc)
350 xmlNodePtr root = xmlDocGetRootElement(doc);
351 if(root)
353 /* loop through all albums */
354 xmlNodePtr cur = get_first_node_by_name(root,"artist");
355 if(cur)
357 xmlNodePtr cur2 = get_first_node_by_name(cur,"images");
358 if(cur2) {
359 xmlNodePtr cur3 = get_first_node_by_name(cur2,"image");
360 while(cur3 ){
361 xmlChar *temp = xmlGetProp(cur3, (xmlChar *)"type");
362 if(temp){
363 if(xmlStrEqual(temp, (xmlChar *)"primary"))
365 xmlChar *xurl = xmlGetProp(cur3, (xmlChar *)"uri");
366 xmlChar *size = xmlGetProp(cur3, (xmlChar *)"height");
367 MetaData *mtd = meta_data_new();
368 mtd->type = META_ARTIST_ART; mtd->plugin_name = plugin.name;
369 mtd->content_type = META_DATA_CONTENT_URI;
370 mtd->content = g_strdup((char *)xurl);
371 mtd->size = -1;
372 retv = g_list_prepend(retv,mtd);
373 if(xurl) xmlFree(xurl);
374 if(size) xmlFree(size);
375 } else if(xmlStrEqual(temp, (xmlChar *)"secondary"))
377 xmlChar *xurl = xmlGetProp(cur3, (xmlChar *)"uri");
378 xmlChar *size = xmlGetProp(cur3, (xmlChar *)"height");
379 MetaData *mtd = meta_data_new();
380 mtd->type = META_ARTIST_ART; mtd->plugin_name = plugin.name;
381 mtd->content_type = META_DATA_CONTENT_URI;
382 mtd->content = g_strdup((char *)xurl);
383 mtd->size = -1;
384 retv = g_list_append(retv,mtd);
385 if(xurl) xmlFree(xurl);
386 if(size) xmlFree(size);
389 xmlFree(temp);
391 cur3 = cur3->next;
396 xmlFreeDoc(doc);
400 return retv;
403 static void __query_get_artist_art_uris(const GEADAsyncHandler *handle, GEADStatus status, gpointer data)
405 ii *i = (ii *) data;
406 if(status == GEAD_PROGRESS) return;
407 if(status == GEAD_DONE) {
408 goffset size;
409 const gchar *data = gmpc_easy_handler_get_data(handle, &size);
410 GList *list = __query_artist_get_uri_list(i->song, data, (gsize)size);
411 i->callback(list, i->data);
412 g_free(i);
413 return;
415 i->callback(NULL, i->data);
416 g_free(i);
419 static void __query_get_artist_art(const GEADAsyncHandler *handle, GEADStatus status, gpointer data)
421 ii *i = (ii *) data;
422 if(status == GEAD_PROGRESS) return;
423 if(status == GEAD_DONE) {
424 gchar *artist_uri = NULL;
425 goffset size;
426 char furl[1024];
427 const gchar *data = gmpc_easy_handler_get_data(handle, &size);
428 artist_uri = __query_artist_get_uri(i->song, data, (gsize)(size));
429 if(artist_uri)
431 /* Hack to fix bug in discogs api */
432 if(strstr(artist_uri, "?") != NULL)
434 snprintf(furl,1024,"%s&f=xml&api_key=%s", artist_uri,DISCOGS_API_KEY);
435 }else{
436 snprintf(furl,1024,"%s?f=xml&api_key=%s", artist_uri,DISCOGS_API_KEY);
438 gmpc_easy_async_downloader(furl, __query_get_artist_art_uris, i);
439 return;
442 i->callback(NULL, i->data);
443 g_free(i);
446 static void discogs_fetch_artist_art(ii *i)
448 char *artist = gmpc_easy_download_uri_escape(i->song->artist);
449 char furl[1024];
450 snprintf(furl,1024,DISCOGS_API_ROOT"search?type=all&f=xml&q=%s&api_key=%s", artist,DISCOGS_API_KEY);
451 gmpc_easy_async_downloader(furl, __query_get_artist_art, i);
452 g_free(artist);
453 return ;
456 static void discogs_fetch_get_image(mpd_Song *song,MetaDataType type, void (*callback)(GList *list, gpointer data), gpointer user_data)
458 if(song->artist == NULL || discogs_get_enabled() == FALSE)
460 callback(NULL, user_data);
461 return ;
464 if(type == META_ARTIST_ART)
466 ii *i = g_malloc0(sizeof(*i));
467 i->type = META_ARTIST_ART;
468 i->song = song;
469 i->callback = callback;
470 i->data = user_data;
471 i->uris = NULL;
472 discogs_fetch_artist_art(i);
473 return ;
475 else if (type == META_ALBUM_ART && song->album &&
476 cfg_get_single_value_as_int_with_default(config,
477 "cover-discogs", "fetch-album", TRUE))
479 ii *i = g_malloc0(sizeof(*i));
480 i->type = META_ALBUM_ART;
481 i->song = song;
482 i->callback = callback;
483 i->data = user_data;
484 i->uris = NULL;
485 discogs_fetch_cover_album_art(i);
486 return ;
489 callback(NULL, user_data);
490 return ;
493 gmpcMetaDataPlugin lf_cover = {
494 .get_priority = discogs_fetch_cover_priority,
495 .set_priority = discogs_fetch_cover_priority_set,
496 .get_metadata = discogs_fetch_get_image
499 int plugin_api_version = PLUGIN_API_VERSION;
501 static void lf_init(void)
503 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
504 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
506 static const gchar *discogs_get_translation_domain(void)
508 return GETTEXT_PACKAGE;
510 gmpcPlugin plugin = {
511 .name = N_("DiscoGS Artist and Album Image Fetcher"),
512 .version = {PLUGIN_MAJOR_VERSION,PLUGIN_MINOR_VERSION,PLUGIN_MICRO_VERSION},
513 .plugin_type = GMPC_PLUGIN_META_DATA,
514 .init = lf_init,
515 .metadata = &lf_cover,
516 .get_enabled = discogs_get_enabled,
517 .set_enabled = discogs_set_enabled,
519 .get_translation_domain = discogs_get_translation_domain