Implement search integration
[gmpc-magnatune.git] / src / magnatune.c
blob28de125ca70501dd13ddb6dc07f015132a55742b
1 /* gmpc-magnatune (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.
20 #include <stdio.h>
21 #include <string.h>
22 #include <gtk/gtk.h>
23 #include <axl/axl.h>
24 #include <gmpc/plugin.h>
25 #include <gmpc/gmpc_easy_download.h>
26 #include <gmpc/misc.h>
27 #include <libmpd/libmpd-internal.h>
28 #include "magnatune.h"
30 static void magnatune_cleanup_xml();
31 static axlDoc *magnatune_xmldoc = NULL;
32 GMutex *mt_db_lock = NULL;
34 /* hack fix this */
35 int magnatune_end_download();
37 /**
38 * Makes a copy with all &#<id>; decoded
40 static char * url_decode(const char *string)
42 char *retv = NULL;
43 if(string && string[0] != '\0')
45 int i;
46 const gchar *a = string;
47 gchar *b;
48 b = retv= g_malloc0((strlen(string)+1)*sizeof(char));
49 while(*a){
50 if(*a == '&'){
51 const char *c = a;
52 while(*c != ';')c++;
53 /* skip &# */
54 a+=2;
55 *b= (char)atoi(a);
56 a = c;
58 else
59 *b = *a;
60 b++;
61 a++;
64 return retv;
67 void magnatune_db_destroy(void)
69 if(mt_db_lock)
71 g_mutex_lock(mt_db_lock);
72 g_mutex_unlock(mt_db_lock);
73 g_mutex_free(mt_db_lock);
75 if(magnatune_xmldoc)
77 axl_doc_free(magnatune_xmldoc);
78 axl_end();
79 magnatune_xmldoc = NULL;
83 /* run this before using the other fucntions */
84 void magnatune_db_init()
86 mt_db_lock = g_mutex_new();
87 axl_init();
90 void magnatune_db_open()
92 gchar *path = gmpc_get_user_path("magnatune.xml");
94 g_mutex_lock(mt_db_lock);
95 /**
96 * if the db doesn't exists exit
98 if(!g_file_test(path, G_FILE_TEST_EXISTS))
100 g_free(path);
101 g_mutex_unlock(mt_db_lock);
102 return;
105 * if open close it
107 if(magnatune_xmldoc)
109 axl_doc_free(magnatune_xmldoc);
110 magnatune_xmldoc = NULL;
112 magnatune_xmldoc = axl_doc_parse_from_file(path, NULL);
114 g_mutex_unlock(mt_db_lock);
115 g_free(path);
118 gboolean magnatune_db_has_data()
120 gboolean data = FALSE;
121 g_mutex_lock(mt_db_lock);
122 data = (magnatune_xmldoc != NULL)? TRUE:FALSE;
123 g_mutex_unlock(mt_db_lock);
124 return data;
127 MpdData * magnatune_db_get_genre_list()
129 MpdData *list = NULL;
130 axlNode *root;
131 axlNode *cur;
132 /** check if there is data */
133 g_mutex_lock(mt_db_lock);
134 if(magnatune_xmldoc == NULL)
136 g_mutex_unlock(mt_db_lock);
137 return NULL;
140 root = axl_doc_get_root(magnatune_xmldoc);
141 cur = axl_node_get_first_child(root);
142 while(cur != NULL)
144 axlNode *cur2;
145 if(NODE_CMP_NAME(cur, "Album"))
147 const char *genre = NULL;
148 cur2 = axl_node_get_first_child(cur);
149 while(cur2 != NULL)
151 if(NODE_CMP_NAME(cur2, "magnatunegenres"))
153 gchar **tokens = NULL;
154 genre = axl_node_get_content(cur2,NULL);
155 if(genre)
157 int i =0;
158 tokens = g_strsplit(genre,",",0);
159 for(i=0; tokens[i];i++)
161 list = mpd_new_data_struct_append(list);
162 list->type = MPD_DATA_TYPE_TAG;
163 list->tag_type = MPD_TAG_ITEM_GENRE;
164 list->tag = url_decode(tokens[i]);
166 g_strfreev (tokens);
169 cur2 = axl_node_get_next(cur2);
173 cur = axl_node_get_next(cur);
175 g_mutex_unlock(mt_db_lock);
176 return misc_mpddata_remove_duplicate_songs(list);
179 gchar *magnatune_db_get_value(const char *artist, const char *album, int type)
181 gchar *retval = NULL;
182 axlNode *root;
183 axlNode *cur;
184 /** check if there is data */
185 g_mutex_lock(mt_db_lock);
186 if(magnatune_xmldoc == NULL || !artist )
188 g_mutex_unlock(mt_db_lock);
189 return NULL;
192 root = axl_doc_get_root(magnatune_xmldoc);
193 cur = axl_node_get_first_child(root);
194 while(cur != NULL && !retval)
196 axlNode *cur2;
197 if(NODE_CMP_NAME(cur, "Album"))
199 const char *gartist = NULL;
200 const char *galbum = NULL;
201 const char *gvalue = NULL;
202 cur2 = axl_node_get_first_child(cur);
203 while(cur2 != NULL && !retval)
205 if(!gartist && NODE_CMP_NAME(cur2, "artist"))
207 gartist = axl_node_get_content(cur2,NULL);
209 else if(!galbum && NODE_CMP_NAME(cur2, "albumname"))
211 galbum= axl_node_get_content(cur2,NULL);
213 else if(!gvalue && NODE_CMP_NAME(cur2, ((type== META_ARTIST_ART)?"artistphoto":"cover_small")))
215 gvalue= axl_node_get_content(cur2,NULL);
217 cur2 = axl_node_get_next(cur2);
219 if(gvalue && artist && !strncmp(gartist, artist,strlen(artist)))
221 if(type == META_ARTIST_ART)
223 retval = url_decode(gvalue);
225 else if(galbum && !strcmp(galbum, album)){
226 retval = url_decode(gvalue);
231 cur = axl_node_get_next(cur);
234 g_mutex_unlock(mt_db_lock);
235 return retval;
238 * This removes duplicate/unused fields from every Track entry,
239 * This remove around 1/2 the file size
240 * NOT THREAD SAFE, USE INTERNALLY
242 static void magnatune_cleanup_xml()
244 axlNode *root;
245 axlNode *cur;
246 if(!magnatune_xmldoc)
247 return;
248 root = axl_doc_get_root(magnatune_xmldoc);
249 cur = axl_node_get_first_child(root);
250 while(cur != NULL)
252 axlNode *cur2;
253 if(NODE_CMP_NAME(cur, "Album"))
255 cur2 = axl_node_get_first_child(cur);
256 while(cur2 != NULL)
258 if(NODE_CMP_NAME(cur2, "Track"))
260 axlNode *cur3 = axl_node_get_first_child(cur2) ;
261 while(cur3) {
262 if(NODE_CMP_NAME(cur3, "albumname") ||
263 NODE_CMP_NAME(cur3, "artist") ||
264 NODE_CMP_NAME(cur3, "artistdesc") ||
265 NODE_CMP_NAME(cur3, "home") ||
266 NODE_CMP_NAME (cur3, "artistphoto") ||
267 NODE_CMP_NAME (cur3, "mp3lofi") ||
268 NODE_CMP_NAME (cur3, "albumsku") ||
269 NODE_CMP_NAME (cur3, "mp3genre") ||
270 NODE_CMP_NAME (cur3, "magnatunegenres") ||
271 NODE_CMP_NAME (cur3, "license") ||
272 NODE_CMP_NAME (cur3, "album_notes") ||
273 NODE_CMP_NAME (cur3, "launchdate") ||
274 NODE_CMP_NAME (cur3, "buy") ||
275 NODE_CMP_NAME (cur3, "moods") )
278 axl_node_remove(cur3,TRUE);
280 cur3 = axl_node_get_first_child(cur2) ;
281 } else {
282 cur3 = axl_node_get_next(cur3);
285 cur2 = axl_node_get_next(cur2);
287 else if(NODE_CMP_NAME(cur2, "album_notes") ||
288 NODE_CMP_NAME (cur2, "buy") ||
289 NODE_CMP_NAME (cur2, "launchdate"))
291 axl_node_remove(cur2,TRUE);
292 cur2 = axl_node_get_first_child(cur);
293 } else {
294 cur2 = axl_node_get_next(cur2);
298 cur = axl_node_get_next(cur);
303 void magnatune_db_download_xml_thread(gpointer data)
305 gmpc_easy_download_struct *dld = data;
307 g_mutex_lock(mt_db_lock);
309 if(magnatune_xmldoc)
311 axl_doc_free(magnatune_xmldoc);
313 magnatune_xmldoc = NULL;
315 g_mutex_unlock(mt_db_lock);
316 if(gmpc_easy_download("http://www.magnatune.com/info/album_info.xml", dld))
318 g_mutex_lock(mt_db_lock);
319 gchar *path = NULL;
320 magnatune_xmldoc = axl_doc_parse(dld->data, dld->size, NULL);
321 magnatune_cleanup_xml();
322 path = gmpc_get_user_path("magnatune.xml");
323 axl_doc_dump_to_file(magnatune_xmldoc, path);
324 g_free(path);
325 g_mutex_unlock(mt_db_lock);
327 else
329 g_mutex_lock(mt_db_lock);
330 /* update */
331 gchar *path = gmpc_get_user_path("magnatune.xml");
332 if(g_file_test(path, G_FILE_TEST_EXISTS))
334 magnatune_xmldoc = axl_doc_parse_from_file(path, NULL);
336 g_free(path);
337 g_mutex_unlock(mt_db_lock);
340 * cleanup
342 gmpc_easy_download_clean(dld);
343 g_free(dld);
347 g_idle_add(magnatune_end_download, NULL);
350 void magnatune_db_download_xml(ProgressCallback cb, gpointer data )
352 gmpc_easy_download_struct *dld = g_malloc0(sizeof(*dld));
353 dld->callback_data = data;
354 dld->callback = cb;
355 dld->max_size = -1;
356 dld->data = NULL;
357 dld->size = 0;
358 g_thread_create((GThreadFunc)magnatune_db_download_xml_thread, dld,FALSE, NULL);
362 MpdData * magnatune_db_get_artist_list(char *wanted_genre)
364 MpdData *list = NULL;
365 axlNode *root;
366 axlNode *cur;
367 /** check if there is data */
368 g_mutex_lock(mt_db_lock);
369 if(magnatune_xmldoc == NULL || wanted_genre == NULL)
371 g_mutex_unlock(mt_db_lock);
372 return NULL;
375 root = axl_doc_get_root(magnatune_xmldoc);
376 cur = axl_node_get_first_child(root);
377 while(cur != NULL)
379 axlNode *cur2;
380 if(NODE_CMP_NAME(cur, "Album"))
382 const char *genre = NULL;
383 const char *artist = NULL;
384 cur2 = axl_node_get_first_child(cur);
385 while(cur2 != NULL)
387 if(NODE_CMP_NAME(cur2, "magnatunegenres"))
389 genre = axl_node_get_content(cur2,NULL);
391 else if(NODE_CMP_NAME(cur2, "artist"))
393 artist = axl_node_get_content(cur2,NULL);
395 cur2 = axl_node_get_next(cur2);
397 if(genre && artist && strstr(genre, wanted_genre))
399 list = mpd_new_data_struct_append(list);
400 list->type = MPD_DATA_TYPE_TAG;
401 list->tag_type = MPD_TAG_ITEM_ARTIST;
402 list->tag = url_decode(artist);
407 cur = axl_node_get_next(cur);
409 g_mutex_unlock(mt_db_lock);
410 return misc_mpddata_remove_duplicate_songs(list);
413 MpdData *magnatune_db_get_album_list(char *wanted_genre,char *wanted_artist)
415 MpdData *list = NULL;
416 axlNode *root;
417 axlNode *cur;
418 /** check if there is data */
419 g_mutex_lock(mt_db_lock);
420 if(magnatune_xmldoc == NULL || wanted_genre == NULL || wanted_artist == NULL)
422 g_mutex_unlock(mt_db_lock);
423 return NULL;
426 root = axl_doc_get_root(magnatune_xmldoc);
427 cur = axl_node_get_first_child(root);
428 while(cur != NULL)
430 axlNode *cur2;
431 if(NODE_CMP_NAME(cur, "Album"))
433 const char *genre = NULL;
434 const char *album = NULL;
435 const char *artist = NULL;
436 cur2 = axl_node_get_first_child(cur);
437 while(cur2 != NULL)
439 if(NODE_CMP_NAME(cur2, "magnatunegenres"))
441 genre = axl_node_get_content(cur2,NULL);
443 else if(NODE_CMP_NAME(cur2, "artist"))
445 artist = axl_node_get_content(cur2,NULL);
447 else if(NODE_CMP_NAME(cur2, "albumname"))
449 album = axl_node_get_content(cur2,NULL);
451 cur2 = axl_node_get_next(cur2);
453 if(genre && artist&&album && strstr(genre, wanted_genre) && !strcmp(artist, wanted_artist))
455 list = mpd_new_data_struct_append(list);
456 list->type = MPD_DATA_TYPE_TAG;
457 list->tag_type = MPD_TAG_ITEM_ALBUM;
458 list->tag = url_decode(album);
461 cur = axl_node_get_next(cur);
463 g_mutex_unlock(mt_db_lock);
464 return mpd_data_get_first(list);
468 GList * magnatune_db_get_url_list(const char *wanted_genre,const char *wanted_artist, const char *wanted_album)
470 GList *list = NULL;
471 axlNode *root;
472 axlNode *cur;
473 /** check if there is data */
474 g_mutex_lock(mt_db_lock);
475 if(magnatune_xmldoc == NULL || wanted_genre == NULL)
477 g_mutex_unlock(mt_db_lock);
478 return NULL;
481 root = axl_doc_get_root(magnatune_xmldoc);
482 cur = axl_node_get_first_child(root);
483 while(cur != NULL)
485 axlNode *cur2;
486 if(NODE_CMP_NAME(cur, "Album"))
488 const char *genre = NULL;
489 const char *album = NULL;
490 const char *artist = NULL;
491 gboolean add_urls = FALSE;
492 axlNode *tracks;
493 cur2 = axl_node_get_first_child(cur);
494 while(cur2 != NULL)
496 if(NODE_CMP_NAME(cur2, "magnatunegenres"))
498 genre = axl_node_get_content(cur2,NULL);
500 else if(NODE_CMP_NAME(cur2, "artist"))
502 artist = axl_node_get_content(cur2,NULL);
504 else if(NODE_CMP_NAME(cur2, "albumname"))
506 album = axl_node_get_content(cur2,NULL);
508 cur2 = axl_node_get_next(cur2);
510 if(genre && strstr(genre, wanted_genre))
512 if(wanted_artist && wanted_album) {
513 if(!strcmp(wanted_artist,artist) && !strcmp(wanted_album, album)) {
514 add_urls = TRUE;
516 }else if(wanted_artist) {
517 if(!strcmp(wanted_artist, artist)) {
518 add_urls = TRUE;
520 } else {
521 add_urls = TRUE;
524 if(add_urls)
526 cur2 = axl_node_get_first_child(cur);
527 while(cur2)
529 if (NODE_CMP_NAME(cur2, "Track"))
531 tracks = axl_node_get_first_child(cur2);
532 while(tracks)
534 if (NODE_CMP_NAME(tracks, "url"))
536 const char *url = axl_node_get_content(tracks,NULL);
537 list = g_list_append(list, url_decode(url));
539 tracks = axl_node_get_next(tracks);
542 cur2 = axl_node_get_next(cur2);
546 cur = axl_node_get_next(cur);
548 g_mutex_unlock(mt_db_lock);
549 return list;
552 static int custcmp(const gchar *hay, const gchar *needle)
554 int retv = -1;
555 gchar *sa1 = g_utf8_casefold(needle, -1);
556 gchar *sa = g_utf8_normalize(sa1,-1,G_NORMALIZE_ALL_COMPOSE);
557 gchar *sb1 = g_utf8_casefold(hay, -1);
558 gchar *sb = g_utf8_normalize(sb1,-1,G_NORMALIZE_ALL_COMPOSE);
559 retv = (strstr(sb, sa) == NULL);
560 g_free(sa1);g_free(sa);
561 g_free(sb1);g_free(sb);
562 return retv;
565 MpdData* magnatune_db_get_song_list(const char *wanted_genre,const char *wanted_artist, const char *wanted_album, gboolean exact)
567 MpdData *data = NULL;
568 axlNode * root;
569 axlNode * cur;
570 int (*cmpfunc)(const char *hay, const char *needle);
571 if(exact)
573 cmpfunc = strcmp;
574 }else{
575 cmpfunc = custcmp;
577 /** check if there is data */
578 g_mutex_lock(mt_db_lock);
579 if(magnatune_xmldoc == NULL )
581 g_mutex_unlock(mt_db_lock);
582 return NULL;
585 root = axl_doc_get_root(magnatune_xmldoc);
586 cur = axl_node_get_first_child(root);
587 while(cur != NULL)
589 axlNode * cur2;
590 if(NODE_CMP_NAME(cur, (const char *)"Album"))
592 const char *year = NULL;
593 const char *temp = NULL;
594 const char *genre = NULL;
595 const char *album = NULL;
596 const char *artist = NULL;
598 gboolean add_urls = FALSE;
599 axlNode * tracks;
600 cur2 = axl_node_get_first_child(cur);
601 while(cur2 != NULL)
603 if(NODE_CMP_NAME(cur2, (const char *)"magnatunegenres"))
605 genre = axl_node_get_content(cur2,NULL);
607 else if(NODE_CMP_NAME(cur2, (const char *)"artist"))
609 artist = axl_node_get_content(cur2,NULL);
611 else if(NODE_CMP_NAME(cur2, (const char *)"albumname"))
613 album = axl_node_get_content(cur2,NULL);
616 cur2 = axl_node_get_next(cur2);
618 add_urls = TRUE;
619 if(wanted_genre && add_urls) {
620 add_urls = FALSE;
621 if(genre)
623 int i=0;
624 gchar **str = g_strsplit(genre, ",", 0);
625 for(i=0; str && str[i];i++)
627 if(cmpfunc(str[i], wanted_genre) == 0) add_urls = TRUE;
629 if(str)g_strfreev(str);
632 if(wanted_album && add_urls) {
633 add_urls = (album && cmpfunc(album, wanted_album) == 0);
635 if(wanted_artist && add_urls) {
636 add_urls = (artist && cmpfunc(artist, wanted_artist) == 0);
639 if(add_urls)
641 cur2 = axl_node_get_first_child(cur);
642 while(cur2)
644 if (NODE_CMP_NAME(cur2, (const char *)"Track"))
646 const char *tn = NULL;
647 int stime = 0;
648 const char *url= NULL;
649 const char *name = NULL;
651 tracks = axl_node_get_first_child(cur2);
654 while(tracks)
656 if (NODE_CMP_NAME(tracks, (const char *)"url")&& !url)
658 url= axl_node_get_content(tracks,NULL);
660 else if (NODE_CMP_NAME(tracks, (const char *)"trackname")&& !name)
662 name = axl_node_get_content(tracks,NULL);
665 else if (NODE_CMP_NAME(tracks, (const char *)"tracknum")&& !tn)
667 tn = axl_node_get_content(tracks,NULL);
669 else if (NODE_CMP_NAME(tracks, "seconds")&& !stime)
671 const char *eurl = axl_node_get_content(tracks, NULL);
672 stime = atoi(eurl);
674 else if (NODE_CMP_NAME(tracks, (const char *)"year")&& !year)
676 year = axl_node_get_content(tracks,NULL);
678 tracks = axl_node_get_next(tracks);
681 if(url){
682 data = (MpdData *)mpd_new_data_struct_append((MpdData *)data);
683 data->type = MPD_DATA_TYPE_SONG;
684 data->song = mpd_newSong();
685 data->song->file = url_decode(url);
686 data->song->title = url_decode(name);
687 data->song->album = url_decode(album);
688 data->song->artist = url_decode(artist);
689 data->song->genre = url_decode(genre);
690 data->song->date = url_decode(year);
691 data->song->track = url_decode(tn);
692 data->song->time = stime;
695 cur2 = axl_node_get_next(cur2);
699 cur = axl_node_get_next(cur);
701 g_mutex_unlock(mt_db_lock);
702 return data;