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.
22 /* gtk and lib stuff */
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>
38 #define DISCOGS_API_KEY "332020810c"
39 #define DISCOGS_API_ROOT "http://www.discogs.com/"
41 #define LOG_DOMAIN "Gmpc.Provider.DiscoGS"
47 void (*callback
)(GList
*list
, gpointer data
);
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
;
72 xmlNodePtr c
= xml
->xmlChildrenNode
;
74 if(c
->name
&& xmlStrEqual(c
->name
, (xmlChar
*) name
))
81 static xmlNodePtr
get_next_node_by_name(xmlNodePtr xml
, gchar
*name
) {
82 if(name
== NULL
) return NULL
;
84 xmlNodePtr c
= xml
->next
;
86 if(c
->name
&& xmlStrEqual(c
->name
, (xmlChar
*) name
))
93 static GList
* __query_album_get_uri(mpd_Song
*song
, const gchar
*data
, gsize size
)
96 char *temp_b
= g_utf8_casefold(song
->album
,-1);
101 if(size
< 4 || strncmp(data
, "<res",4))
103 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Invalid XML");
107 doc
= xmlParseMemory(data
,size
);
110 xmlNodePtr root
= xmlDocGetRootElement(doc
);
113 /* loop through all albums */
114 xmlNodePtr cur
= get_first_node_by_name(root
,"searchresults");
117 xmlNodePtr cur2
= get_first_node_by_name(cur
,"result");
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");
125 xmlChar
*title
= xmlNodeGetContent(cur4
);
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");
135 xmlChar
*xurl
= xmlNodeGetContent(cur3
);
136 retv
= g_list_prepend(retv
, g_strdup((char *)xurl
));
142 if(title
)xmlFree(title
);
145 if(temp
) xmlFree(temp
);
146 cur2
= get_next_node_by_name(cur2
, "result");
154 return g_list_reverse(retv
);
156 static GList
*__query_album_get_uri_list(mpd_Song
*song
, const gchar
*data
, gsize size
)
160 if(size
< 4 || strncmp(data
, "<res",4))
162 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Invalid XML");
167 doc
= xmlParseMemory(data
,size
);
170 xmlNodePtr root
= xmlDocGetRootElement(doc
);
173 /* loop through all albums */
174 xmlNodePtr cur
= get_first_node_by_name(root
,"release");
177 xmlNodePtr cur2
= get_first_node_by_name(cur
,"images");
179 xmlNodePtr cur3
= get_first_node_by_name(cur2
,"image");
181 xmlChar
*temp
= xmlGetProp(cur3
, (xmlChar
*)"type");
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
);
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
);
206 retv
= g_list_append(retv
,mtd
);
207 if(xurl
) xmlFree(xurl
);
208 if(size
) xmlFree(size
);
224 static void __query_get_album_art_uris(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer data
)
227 if(status
== GEAD_PROGRESS
) return;
228 if(status
== GEAD_DONE
) {
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
);
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
);
243 gmpc_easy_async_downloader(furl
, __query_get_album_art_uris
, i
);
247 i
->callback(i
->uris
, i
->data
);
251 i
->callback(NULL
, i
->data
);
254 static void __query_get_album_art(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer data
)
257 if(status
== GEAD_PROGRESS
) return;
258 if(status
== GEAD_DONE
) {
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
)));
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
);
272 gmpc_easy_async_downloader(furl
, __query_get_album_art_uris
, i
);
276 i
->callback(NULL
, i
->data
);
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
);
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
);
297 static gchar
* __query_artist_get_uri(mpd_Song
*song
, const gchar
*data
, gsize size
)
304 if(size
< 4 || strncmp(data
, "<res",4))
306 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Invalid XML");
310 doc
= xmlParseMemory(data
,size
);
313 xmlNodePtr root
= xmlDocGetRootElement(doc
);
316 /* loop through all albums */
317 xmlNodePtr cur
= get_first_node_by_name(root
,"exactresults");
320 xmlNodePtr cur2
= get_first_node_by_name(cur
,"result");
322 xmlNodePtr cur3
= get_first_node_by_name(cur2
,"uri");
324 xmlChar
*xurl
= xmlNodeGetContent(cur3
);
325 retv
= g_strdup((char *)xurl
);
337 static GList
*__query_artist_get_uri_list(mpd_Song
*song
, const gchar
*data
, gsize size
)
341 if(size
< 4 || strncmp(data
, "<res",4))
343 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Invalid XML");
347 doc
= xmlParseMemory(data
,size
);
350 xmlNodePtr root
= xmlDocGetRootElement(doc
);
353 /* loop through all albums */
354 xmlNodePtr cur
= get_first_node_by_name(root
,"artist");
357 xmlNodePtr cur2
= get_first_node_by_name(cur
,"images");
359 xmlNodePtr cur3
= get_first_node_by_name(cur2
,"image");
361 xmlChar
*temp
= xmlGetProp(cur3
, (xmlChar
*)"type");
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
);
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
);
384 retv
= g_list_append(retv
,mtd
);
385 if(xurl
) xmlFree(xurl
);
386 if(size
) xmlFree(size
);
403 static void __query_get_artist_art_uris(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer data
)
406 if(status
== GEAD_PROGRESS
) return;
407 if(status
== GEAD_DONE
) {
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
);
415 i
->callback(NULL
, i
->data
);
419 static void __query_get_artist_art(const GEADAsyncHandler
*handle
, GEADStatus status
, gpointer data
)
422 if(status
== GEAD_PROGRESS
) return;
423 if(status
== GEAD_DONE
) {
424 gchar
*artist_uri
= NULL
;
427 const gchar
*data
= gmpc_easy_handler_get_data(handle
, &size
);
428 artist_uri
= __query_artist_get_uri(i
->song
, data
, (gsize
)(size
));
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
);
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
);
442 i
->callback(NULL
, i
->data
);
446 static void discogs_fetch_artist_art(ii
*i
)
448 char *artist
= gmpc_easy_download_uri_escape(i
->song
->artist
);
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
);
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
);
464 if(type
== META_ARTIST_ART
)
466 ii
*i
= g_malloc0(sizeof(*i
));
467 i
->type
= META_ARTIST_ART
;
469 i
->callback
= callback
;
472 discogs_fetch_artist_art(i
);
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
;
482 i
->callback
= callback
;
485 discogs_fetch_cover_album_art(i
);
489 callback(NULL
, user_data
);
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
,
515 .metadata
= &lf_cover
,
516 .get_enabled
= discogs_get_enabled
,
517 .set_enabled
= discogs_set_enabled
,
519 .get_translation_domain
= discogs_get_translation_domain