1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <pthread.h>
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 
28 #include <wctype.h>
29 #include <errno.h>
30 
31 /* For stat/opendir */
32 
33 #include <limits.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <dirent.h>
38 
39 
40 #include <config.h>
41 
42 #include <gmerlin/tree.h>
43 #include <treeprivate.h>
44 
45 #include <gmerlin/charset.h>
46 
47 #include <gmerlin/utils.h>
48 #include <gmerlin/translation.h>
49 
50 
51 
52 #include <gmerlin/log.h>
53 #define LOG_DOMAIN "album"
54 
55 #ifdef HAVE_INOTIFY
56 #include <sys/inotify.h>
57 #endif
58 
59 /*
60  *  This must be called, whenever the tracks in an album
61  *  change.
62  */
63 
delete_shuffle_list(bg_album_t * album)64 static void delete_shuffle_list(bg_album_t * album)
65   {
66   bg_shuffle_list_destroy(album->com->shuffle_list);
67   album->com->shuffle_list = NULL;
68   }
69 
new_filename(bg_album_t * album)70 static char * new_filename(bg_album_t * album)
71   {
72   /*
73    *  Album filenames are constructed like "aXXXXXXXX.xml",
74    *  where XXXXXXXX is a hexadecimal unique identifier
75    */
76   char * template = NULL;
77   char * path = NULL;
78   char * ret = NULL;
79   char * pos;
80 
81   template = bg_sprintf("%s/a%%08x.xml", album->com->directory);
82 
83   path = bg_create_unique_filename(template);
84 
85   if(!path)
86     goto fail;
87 
88   pos = strrchr(path, '/');
89 
90   pos++;
91   ret = bg_strdup(NULL, pos);
92   free(path);
93 
94   fail:
95   if(template)
96     free(template);
97 
98   return ret;
99   }
100 
bg_album_set_default_location(bg_album_t * album)101 void bg_album_set_default_location(bg_album_t * album)
102   {
103   if(!album->xml_file)
104     {
105     album->xml_file = new_filename(album);
106     }
107   }
108 
entry_from_track_info(bg_album_common_t * com,bg_album_entry_t * entry,bg_track_info_t * track_info,int update_name)109 static void entry_from_track_info(bg_album_common_t * com,
110                                   bg_album_entry_t * entry,
111                                   bg_track_info_t  * track_info,
112                                   int update_name)
113   {
114   int i;
115   int name_set = 0;
116   entry->num_audio_streams = track_info->num_audio_streams;
117 
118   entry->num_video_streams = 0;
119   entry->num_still_streams = 0;
120 
121   for(i = 0; i < track_info->num_video_streams; i++)
122     {
123     if(track_info->video_streams[i].format.framerate_mode ==
124        GAVL_FRAMERATE_STILL)
125       entry->num_still_streams++;
126     else
127       entry->num_video_streams++;
128     }
129 
130   entry->num_subtitle_streams = track_info->num_subtitle_streams;
131 
132   if(!entry->name || update_name)
133     {
134     if(entry->name)
135       {
136       free(entry->name);
137       entry->name = NULL;
138       }
139 
140     if(entry->name_w)
141       {
142       free(entry->name_w);
143       entry->name_w = NULL;
144       entry->len_w = 0;
145       }
146 
147     /* Track info has a name */
148 
149     if(com && com->use_metadata && com->metadata_format)
150       {
151       entry->name = bg_create_track_name(&track_info->metadata,
152                                          com->metadata_format);
153       if(entry->name)
154         name_set = 1;
155       }
156 
157     if(!name_set)
158       {
159       if(track_info->name)
160         {
161         entry->name = bg_strdup(entry->name, track_info->name);
162         }
163       /* Take filename minus extension */
164       else
165         {
166         entry->name =
167           bg_get_track_name_default(entry->location,
168                                     entry->index, entry->total_tracks);
169         }
170       }
171     }
172   entry->duration = track_info->duration;
173   entry->flags &= ~BG_ALBUM_ENTRY_ERROR;
174 
175   if(track_info->url)
176     {
177     entry->location = bg_strdup(entry->location, track_info->url);
178     entry->index = 0;
179     entry->total_tracks = 1;
180     entry->flags = BG_ALBUM_ENTRY_REDIRECTOR;
181     }
182 
183   }
184 
bg_album_update_entry(bg_album_t * album,bg_album_entry_t * entry,bg_track_info_t * track_info,int callback,int update_name)185 void bg_album_update_entry(bg_album_t * album,
186                            bg_album_entry_t * entry,
187                            bg_track_info_t  * track_info,
188                            int callback, int update_name)
189   {
190   entry_from_track_info(album->com, entry, track_info, update_name);
191   if(callback)
192     bg_album_entry_changed(album, entry);
193   }
194 
195 bg_album_entry_t *
bg_album_entry_create_from_track_info(bg_track_info_t * track_info,const char * url)196 bg_album_entry_create_from_track_info(bg_track_info_t * track_info,
197                                       const char * url)
198   {
199   bg_album_entry_t * ret;
200   ret = bg_album_entry_create();
201   ret->location = bg_strdup(ret->location, url);
202   entry_from_track_info(NULL, ret, track_info, 1);
203   return ret;
204   }
205 
bg_album_create(bg_album_common_t * com,bg_album_type_t type,bg_album_t * parent)206 bg_album_t * bg_album_create(bg_album_common_t * com, bg_album_type_t type,
207                              bg_album_t * parent)
208   {
209   bg_album_t * ret = calloc(1, sizeof(*ret));
210   ret->com = com;
211   ret->parent = parent;
212   ret->type = type;
213 #ifdef HAVE_INOTIFY
214   ret->inotify_wd = -1;
215 #endif
216   return ret;
217   }
218 
bg_album_get_name(bg_album_t * a)219 char * bg_album_get_name(bg_album_t * a)
220   {
221   return a->name;
222   }
223 
bg_album_get_num_entries(bg_album_t * a)224 int bg_album_get_num_entries(bg_album_t * a)
225   {
226   int ret = 0;
227   bg_album_entry_t * entry;
228   entry = a->entries;
229 
230   while(entry)
231     {
232     ret++;
233     entry = entry->next;
234     }
235   return ret;
236   }
237 
bg_album_get_entry(bg_album_t * a,int i)238 bg_album_entry_t * bg_album_get_entry(bg_album_t * a, int i)
239   {
240   bg_album_entry_t * ret;
241   ret = a->entries;
242 
243   while(i--)
244     {
245     if(!ret)
246       return NULL;
247     ret = ret->next;
248     }
249   return ret;
250   }
251 
252 
253 /* Add items */
254 
insertion_done(bg_album_t * album,int start,int num)255 static void insertion_done(bg_album_t * album, int start, int num)
256   {
257   switch(album->type)
258     {
259     case BG_ALBUM_TYPE_INCOMING:
260     case BG_ALBUM_TYPE_FAVOURITES:
261       break;
262     case BG_ALBUM_TYPE_REGULAR:
263     case BG_ALBUM_TYPE_TUNER:
264       if(!album->xml_file)
265         album->xml_file = new_filename(album);
266       break;
267     case BG_ALBUM_TYPE_REMOVABLE:
268     case BG_ALBUM_TYPE_PLUGIN:
269       break;
270     }
271   delete_shuffle_list(album);
272   if(album->insert_callback)
273     album->insert_callback(album, start, num, album->insert_callback_data);
274   }
275 
bg_album_insert_entries_after(bg_album_t * album,bg_album_entry_t * new_entries,bg_album_entry_t * before)276 void bg_album_insert_entries_after(bg_album_t * album,
277                                    bg_album_entry_t * new_entries,
278                                    bg_album_entry_t * before)
279   {
280   bg_album_entry_t * last_new_entry;
281   int start, num;
282 
283   if(!new_entries)
284     return;
285 
286   last_new_entry = new_entries;
287 
288   num = 1;
289   while(last_new_entry->next)
290     {
291     last_new_entry = last_new_entry->next;
292     num++;
293     }
294 
295   if(!before)
296     {
297     last_new_entry->next = album->entries;
298     album->entries = new_entries;
299     start = 0;
300     }
301   else
302     {
303     start = bg_album_get_index(album, before) + 1;
304     last_new_entry->next = before->next;
305     before->next = new_entries;
306     }
307 
308   insertion_done(album, start, num);
309 
310   }
311 
bg_album_insert_entries_before(bg_album_t * album,bg_album_entry_t * new_entries,bg_album_entry_t * after)312 void bg_album_insert_entries_before(bg_album_t * album,
313                                     bg_album_entry_t * new_entries,
314                                     bg_album_entry_t * after)
315   {
316   bg_album_entry_t * before;
317   bg_album_entry_t * last_new_entry;
318   int start, num;
319 
320   if(!new_entries)
321     return;
322 
323   last_new_entry = new_entries;
324 
325   num = 1;
326   while(last_new_entry->next)
327     {
328     last_new_entry = last_new_entry->next;
329     num++;
330     }
331 
332   /* Fill empty album */
333 
334   if(!album->entries)
335     {
336     album->entries = new_entries;
337     start = 0;
338     }
339 
340   /* Append as first item */
341 
342   else if(after == album->entries)
343     {
344     last_new_entry->next = album->entries;
345     album->entries = new_entries;
346     start = 0;
347     }
348   else
349     {
350     before = album->entries;
351     start = 1;
352     while(before->next != after)
353       {
354       before = before->next;
355       start++;
356       }
357     before->next = new_entries;
358     last_new_entry->next = after;
359     }
360 
361   insertion_done(album, start, num);
362 
363   }
364 
bg_album_insert_urls_before(bg_album_t * a,char ** locations,const char * plugin,int prefer_edl,bg_album_entry_t * after)365 void bg_album_insert_urls_before(bg_album_t * a,
366                                  char ** locations,
367                                  const char * plugin,
368                                  int prefer_edl,
369                                  bg_album_entry_t * after)
370   {
371   int i = 0;
372   bg_album_entry_t * new_entries;
373 
374   while(locations[i])
375     {
376     new_entries = bg_album_load_url(a, locations[i], plugin, prefer_edl);
377     bg_album_insert_entries_before(a, new_entries, after);
378     //    bg_album_changed(a);
379     i++;
380     }
381   }
382 
bg_album_insert_file_before(bg_album_t * a,char * file,const char * plugin,int prefer_edl,bg_album_entry_t * after,time_t mtime)383 void bg_album_insert_file_before(bg_album_t * a,
384                                  char * file,
385                                  const char * plugin,
386                                  int prefer_edl,
387                                  bg_album_entry_t * after,
388                                  time_t mtime)
389   {
390   bg_album_entry_t * new_entries;
391   bg_album_entry_t * e;
392 
393   new_entries = bg_album_load_url(a, file, plugin, prefer_edl);
394   e = new_entries;
395   while(e)
396     {
397     e->mtime = mtime;
398     e->flags |= BG_ALBUM_ENTRY_SYNC;
399     e = e->next;
400     }
401   bg_album_insert_entries_before(a, new_entries, after);
402   //    bg_album_changed(a);
403 
404   }
405 
406 
bg_album_insert_urls_after(bg_album_t * a,char ** locations,const char * plugin,int prefer_edl,bg_album_entry_t * before)407 void bg_album_insert_urls_after(bg_album_t * a,
408                                 char ** locations,
409                                 const char * plugin,
410                                 int prefer_edl,
411                                 bg_album_entry_t * before)
412   {
413   int i = 0;
414   bg_album_entry_t * new_entries;
415 
416   while(locations[i])
417     {
418     new_entries = bg_album_load_url(a, locations[i], plugin, prefer_edl);
419     bg_album_insert_entries_after(a, new_entries, before);
420 
421     before = new_entries;
422     if(before)
423       {
424       while(before->next)
425         before = before->next;
426       }
427     //    bg_album_changed(a);
428     i++;
429     }
430   }
431 
432 /* Inserts a string of the type text/uri-list into the album */
433 
bg_album_insert_urilist_after(bg_album_t * a,const char * str,int len,bg_album_entry_t * before)434 void bg_album_insert_urilist_after(bg_album_t * a, const char * str,
435                                    int len, bg_album_entry_t * before)
436   {
437   char ** uri_list;
438 
439   uri_list = bg_urilist_decode(str, len);
440 
441   if(!uri_list)
442     return;
443 
444   bg_album_insert_urls_after(a, uri_list, NULL, 0, before);
445 
446   bg_urilist_free(uri_list);
447   }
448 
bg_album_insert_urilist_before(bg_album_t * a,const char * str,int len,bg_album_entry_t * after)449 void bg_album_insert_urilist_before(bg_album_t * a, const char * str,
450                                     int len, bg_album_entry_t * after)
451   {
452   char ** uri_list;
453 
454   uri_list = bg_urilist_decode(str, len);
455 
456   if(!uri_list)
457     return;
458 
459   bg_album_insert_urls_before(a, uri_list, NULL, 0, after);
460 
461   bg_urilist_free(uri_list);
462   }
463 
get_playlist_location(const char * orig,int strip_leading,const char * prefix)464 static char * get_playlist_location(const char * orig,
465                                     int strip_leading,
466                                     const char * prefix)
467   {
468   int i;
469   char * pos;
470   if(!strncmp(orig, "file://", 7))
471     orig += 7;
472 
473   if((*orig == '/') && strip_leading)
474     {
475     for(i = 0; i < strip_leading; i++)
476       {
477       pos = strchr(orig+1, '/');
478       if(pos)
479         orig = pos;
480       else
481         return NULL;
482       }
483     }
484   if(prefix)
485     return bg_sprintf("%s%s", prefix, orig);
486   else
487     return bg_strdup(NULL, orig);
488   }
489 
bg_album_entries_save_extm3u(bg_album_entry_t * e,const char * name,int strip_leading,const char * prefix)490 int bg_album_entries_save_extm3u(bg_album_entry_t * e, const char * name,
491                                  int strip_leading, const char * prefix)
492   {
493   FILE * out;
494   char * tmp_string;
495 
496   if(!e)
497     {
498     bg_log(BG_LOG_ERROR, LOG_DOMAIN,
499            "Not exporting empty album");
500     return 0;
501     }
502   out = fopen(name, "w");
503   if(!out)
504     {
505     bg_log(BG_LOG_ERROR, LOG_DOMAIN,
506            "Could not open %s: %s", name, strerror(errno));
507     return 0;
508     }
509 
510   fprintf(out, "#EXTM3U\r\n");
511 
512   while(e)
513     {
514     tmp_string = get_playlist_location(e->location,
515                                        strip_leading, prefix);
516     if(!tmp_string)
517       {
518       e = e->next;
519       continue;
520       }
521 
522     fprintf(out, "#EXTINF:%d,%s\r\n",
523             (int)(e->duration / GAVL_TIME_SCALE),
524             e->name);
525 
526 
527     fprintf(out, "%s\r\n", tmp_string);
528     free(tmp_string);
529     e = e->next;
530     }
531   fclose(out);
532   return 1;
533   }
534 
bg_album_entries_save_pls(bg_album_entry_t * e,const char * name,int strip_leading,const char * prefix)535 int bg_album_entries_save_pls(bg_album_entry_t * e, const char * name,
536                               int strip_leading, const char * prefix)
537   {
538   FILE * out;
539   int count = 1;
540   char * tmp_string;
541 
542   if(!e)
543     {
544     bg_log(BG_LOG_ERROR, LOG_DOMAIN,
545            "Not exporting empty album");
546     return 0;
547     }
548   out = fopen(name, "w");
549   if(!out)
550     {
551     bg_log(BG_LOG_ERROR, LOG_DOMAIN,
552            "Could not open %s: %s", name, strerror(errno));
553     return 0;
554     }
555   fprintf(out, "[Playlist]\r\n");
556 
557   while(e)
558     {
559     tmp_string = get_playlist_location(e->location,
560                                        strip_leading, prefix);
561     if(!tmp_string)
562       {
563       e = e->next;
564       continue;
565       }
566     fprintf(out, "File%d=%s\r\n", count, tmp_string);
567     fprintf(out, "Title%d=%s\r\n",count, e->name);
568     fprintf(out, "Length%d=%d\r\n",count, (int)(e->duration / GAVL_TIME_SCALE));
569     free(tmp_string);
570     e = e->next;
571     count++;
572     }
573   // Footer
574   fprintf(out, "NumberOfEntries=%d\r\n", count-1);
575   fprintf(out, "Version=2\r\n");
576   fclose(out);
577   return 1;
578 
579   }
580 
581 /* Open / close */
582 
open_device(bg_album_t * a)583 static int open_device(bg_album_t * a)
584   {
585   bg_track_info_t * track_info;
586   int i, j;
587   int num_tracks;
588   bg_input_plugin_t * plugin;
589   bg_album_entry_t * new_entry;
590 
591   a->handle = bg_plugin_load(a->com->plugin_reg, a->plugin_info);
592 
593   bg_plugin_lock(a->handle);
594 
595   plugin = (bg_input_plugin_t*)a->handle->plugin;
596 
597   /* Open the plugin */
598 
599   if(!plugin->open(a->handle->priv, a->device))
600     {
601     bg_plugin_unlock(a->handle);
602     return 0;
603     }
604 
605   if(plugin->get_disc_name)
606     {
607     a->disc_name = bg_strdup(a->disc_name,
608                              plugin->get_disc_name(a->handle->priv));
609     if(!a->disc_name || (*a->disc_name == '\0'))
610       a->disc_name = bg_strdup(a->disc_name, TR("Unnamed disc"));
611     }
612 
613   if(plugin->eject_disc)
614     a->flags |= BG_ALBUM_CAN_EJECT;
615 
616   /* Get number of tracks */
617 
618   num_tracks = plugin->get_num_tracks(a->handle->priv);
619 
620   for(i = 0; i < num_tracks; i++)
621     {
622     track_info = plugin->get_track_info(a->handle->priv, i);
623 
624     new_entry = calloc(1, sizeof(*new_entry));
625 
626     new_entry->index = i;
627     new_entry->total_tracks = num_tracks;
628     new_entry->name   = bg_strdup(NULL, track_info->name);
629     new_entry->plugin = bg_strdup(NULL, a->handle->info->name);
630 
631     new_entry->location = bg_strdup(new_entry->location,
632                                       a->device);
633     new_entry->num_audio_streams = track_info->num_audio_streams;
634 
635     new_entry->num_still_streams = 0;
636     new_entry->num_video_streams = 0;
637     for(j = 0; j < track_info->num_video_streams; j++)
638       {
639       if(track_info->video_streams[j].format.framerate_mode ==
640          GAVL_FRAMERATE_STILL)
641         new_entry->num_still_streams++;
642       else
643         new_entry->num_video_streams++;
644       }
645     new_entry->num_subtitle_streams =
646       track_info->num_subtitle_streams;
647     new_entry->duration = track_info->duration;
648 
649     bg_album_insert_entries_before(a, new_entry, NULL);
650     }
651   bg_plugin_unlock(a->handle);
652   return 1;
653   }
654 
bg_album_get_type(bg_album_t * a)655 bg_album_type_t bg_album_get_type(bg_album_t * a)
656   {
657   return a->type;
658   }
659 
660 static bg_album_entry_t *
find_next_with_location(bg_album_t * a,char * filename,bg_album_entry_t * before)661 find_next_with_location(bg_album_t * a, char * filename, bg_album_entry_t * before)
662   {
663   if(!before)
664     before = a->entries;
665   else
666     before = before->next;
667   while(before)
668     {
669     if(!strcmp(before->location, filename))
670       break;
671     before = before->next;
672     }
673   return before;
674   }
675 
sync_dir_add(bg_album_t * a,char * filename,time_t mtime)676 static void sync_dir_add(bg_album_t * a, char * filename, time_t mtime)
677   {
678   bg_album_insert_file_before(a,
679                               filename,
680                               NULL,
681                               0,
682                               NULL,
683                               mtime);
684   }
685 
sync_dir_modify(bg_album_t * a,char * filename,time_t mtime)686 static void sync_dir_modify(bg_album_t * a, char * filename, time_t mtime)
687   {
688   bg_album_delete_with_file(a, filename);
689   sync_dir_add(a, filename, mtime);
690   }
691 
bg_album_inotify(bg_album_t * a,uint8_t * event1)692 int bg_album_inotify(bg_album_t * a, uint8_t * event1)
693   {
694 #ifdef HAVE_INOTIFY
695   char * filename;
696   bg_album_t * child;
697   struct stat stat_buf;
698 
699   if(a->inotify_wd >= 0)
700     {
701     struct inotify_event *event =
702       ( struct inotify_event * ) event1;
703 
704     if(event->wd == a->inotify_wd)
705       {
706       switch(event->mask)
707         {
708         case IN_DELETE:
709         case IN_MOVED_FROM:
710           filename = bg_sprintf("%s/%s", a->watch_dir,
711                                 event->name);
712           bg_log(BG_LOG_INFO, LOG_DOMAIN,
713                  "%s disappeared, updating album",
714                  filename);
715           bg_album_delete_with_file(a, filename);
716           free(filename);
717           break;
718         case IN_CLOSE_WRITE:
719         case IN_MOVED_TO:
720           filename = bg_sprintf("%s/%s", a->watch_dir,
721                                 event->name);
722           bg_log(BG_LOG_INFO, LOG_DOMAIN,
723                  "%s appeared, updating album",
724                  filename);
725           if(stat(filename, &stat_buf))
726             {
727             free(filename);
728             return 1;
729             }
730           sync_dir_add(a, filename, stat_buf.st_mtime);
731           free(filename);
732           break;
733         }
734       return 1;
735       }
736     }
737 
738   /* Not our event, try children */
739   child = a->children;
740   while(child)
741     {
742     if(bg_album_inotify(child, event1))
743       return 1;
744     child = child->next;
745     }
746   return 0;
747 
748 #else
749   return 0;
750 #endif
751   }
752 
753 
sync_with_dir(bg_album_t * a)754 static void sync_with_dir(bg_album_t * a)
755   {
756   //  char * tmp_string;
757   DIR * dir;
758   char filename[FILENAME_MAX];
759   struct dirent * dent_ptr;
760   struct stat stat_buf;
761   bg_album_entry_t * e;
762 
763   struct
764     {
765     struct dirent d;
766     char b[NAME_MAX]; /* Make sure there is enough memory */
767     } dent;
768 
769   dir = opendir(a->watch_dir);
770   if(!dir)
771     return;
772 
773   while(!readdir_r(dir, &dent.d, &dent_ptr))
774     {
775     if(!dent_ptr)
776       break;
777 
778     if(dent.d.d_name[0] == '.') /* Don't import hidden files */
779       continue;
780 
781     sprintf(filename, "%s/%s", a->watch_dir, dent.d.d_name);
782 
783     if(stat(filename, &stat_buf))
784       continue;
785 
786     /* Add directory as subalbum */
787     if(S_ISDIR(stat_buf.st_mode))
788       continue;
789     else if(S_ISREG(stat_buf.st_mode))
790       {
791       e = find_next_with_location(a, filename, NULL);
792 
793       if(e)
794         {
795         while(e)
796           {
797           if(e->mtime != stat_buf.st_mtime)
798             {
799             sync_dir_modify(a, filename, stat_buf.st_mtime);
800             break;
801             }
802           else
803             e->flags |= BG_ALBUM_ENTRY_SYNC;
804           e = find_next_with_location(a, filename, e);
805           }
806         }
807       else
808         sync_dir_add(a, filename, stat_buf.st_mtime);
809       }
810     }
811   closedir(dir);
812 
813   bg_album_delete_unsync(a);
814 
815   }
816 
bg_album_open(bg_album_t * a)817 int bg_album_open(bg_album_t * a)
818   {
819   char * tmp_filename;
820   FILE * testfile;
821   bg_input_plugin_t * plugin;
822 
823   if(a->open_count)
824     {
825     bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Album %s already open", a->name);
826     a->open_count++;
827     return 1;
828     }
829 
830   bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Opening album %s", a->name);
831 
832 
833   a->cfg_section = bg_cfg_section_create(NULL);
834 
835   switch(a->type)
836     {
837     case BG_ALBUM_TYPE_REGULAR:
838     case BG_ALBUM_TYPE_FAVOURITES:
839     case BG_ALBUM_TYPE_INCOMING:
840       if(a->xml_file)
841         {
842         tmp_filename = bg_sprintf("%s/%s", a->com->directory,
843                                   a->xml_file);
844 
845         /* If the file cannot be opened, it was deleted earlier
846            we exit quietly here */
847 
848         if(!(testfile = fopen(tmp_filename,"r")))
849           {
850           free(tmp_filename);
851           break;
852           }
853         fclose(testfile);
854 
855         bg_album_load(a, tmp_filename);
856         free(tmp_filename);
857         }
858       break;
859     case BG_ALBUM_TYPE_TUNER:
860       if(a->xml_file)
861         {
862         tmp_filename = bg_sprintf("%s/%s", a->com->directory,
863                                   a->xml_file);
864 
865         /* If the file cannot be opened, it was deleted earlier
866            we exit quietly here */
867 
868         if(!(testfile = fopen(tmp_filename,"r")))
869           {
870           free(tmp_filename);
871           if(!open_device(a))
872             return 0;
873           break;
874           }
875         fclose(testfile);
876 
877         bg_album_load(a, tmp_filename);
878         free(tmp_filename);
879         a->handle = bg_plugin_load(a->com->plugin_reg, a->plugin_info);
880 
881         bg_plugin_lock(a->handle);
882 
883         plugin = (bg_input_plugin_t*)a->handle->plugin;
884 
885         /* Open the plugin */
886 
887         if(!plugin->open(a->handle->priv, a->device))
888           {
889           bg_plugin_unlock(a->handle);
890           return 0;
891           }
892         bg_plugin_unlock(a->handle);
893         }
894       else
895         if(!open_device(a))
896           return 0;
897       break;
898     case BG_ALBUM_TYPE_REMOVABLE:
899       /* Get infos from the plugin */
900       if(!open_device(a))
901         return 0;
902       break;
903     case BG_ALBUM_TYPE_PLUGIN:
904       return 0; /* Cannot be opened */
905       break;
906     }
907   a->open_count++;
908 
909   if((a->type == BG_ALBUM_TYPE_REGULAR) &&
910      a->watch_dir)
911     {
912     sync_with_dir(a);
913 #ifdef HAVE_INOTIFY
914     a->inotify_wd = inotify_add_watch(a->com->inotify_fd,
915                                       a->watch_dir,
916                                       IN_CLOSE_WRITE | IN_DELETE |
917                                       IN_MOVED_TO | IN_MOVED_FROM);
918 #endif
919     }
920   return 1;
921   }
922 
923 
bg_album_entry_destroy(bg_album_entry_t * entry)924 void bg_album_entry_destroy(bg_album_entry_t * entry)
925   {
926   if(entry->name)
927     free(entry->name);
928   if(entry->location)
929     free(entry->location);
930   if(entry->plugin)
931     free(entry->plugin);
932   free(entry);
933   }
934 
bg_album_entries_destroy(bg_album_entry_t * entries)935 void bg_album_entries_destroy(bg_album_entry_t * entries)
936   {
937   bg_album_entry_t * tmp_entry;
938 
939   while(entries)
940     {
941     tmp_entry = entries->next;
942     bg_album_entry_destroy(entries);
943     entries = tmp_entry;
944     }
945 
946   }
947 
bg_album_entries_count(bg_album_entry_t * e)948 int bg_album_entries_count(bg_album_entry_t * e)
949   {
950   int ret = 0;
951   while(e)
952     {
953     ret++;
954     e = e->next;
955     }
956   return ret;
957   }
958 
959 
bg_album_entry_create()960 bg_album_entry_t * bg_album_entry_create()
961   {
962   bg_album_entry_t * ret;
963   ret = calloc(1, sizeof(*ret));
964   return ret;
965   }
966 
bg_album_close(bg_album_t * a)967 void bg_album_close(bg_album_t *a )
968   {
969   //  char * tmp_filename;
970 
971   a->open_count--;
972 
973   if(a->open_count)
974     {
975     bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Not closing album %s (open_count > 0)", a->name);
976     return;
977     }
978   bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Closing album %s", a->name);
979 
980   /* Tell the tree, if we are the current album */
981 
982   if((a == a->com->current_album) && a->com->set_current_callback)
983     {
984     a->com->set_current_callback(a->com->set_current_callback_data,
985                                  NULL, NULL);
986     }
987   switch(a->type)
988     {
989     case BG_ALBUM_TYPE_REMOVABLE:
990     case BG_ALBUM_TYPE_TUNER:
991       a->flags &= ~BG_ALBUM_CAN_EJECT;
992       bg_plugin_unref(a->handle);
993       a->handle = NULL;
994       if(a->disc_name)
995         {
996         free(a->disc_name);
997         a->disc_name = NULL;
998         }
999       if(a->type == BG_ALBUM_TYPE_TUNER)
1000         bg_album_save(a, NULL);
1001       break;
1002     case BG_ALBUM_TYPE_REGULAR:
1003     case BG_ALBUM_TYPE_INCOMING:
1004     case BG_ALBUM_TYPE_FAVOURITES:
1005       bg_album_save(a, NULL);
1006       break;
1007     case BG_ALBUM_TYPE_PLUGIN:
1008       break;
1009     }
1010 
1011   /* Delete entries */
1012 
1013   bg_album_entries_destroy(a->entries);
1014   a->entries = NULL;
1015 
1016   /* Delete shuffle list */
1017 
1018   delete_shuffle_list(a);
1019 
1020   /* Destroy config data */
1021   if(a->cfg_section)
1022     {
1023     bg_cfg_section_destroy(a->cfg_section);
1024     a->cfg_section = NULL;
1025     }
1026 
1027 #ifdef HAVE_INOTIFY
1028   if(a->inotify_wd >= 0)
1029     {
1030     inotify_rm_watch(a->com->inotify_fd, a->inotify_wd);
1031     a->inotify_wd = -1;
1032     }
1033 #endif
1034 
1035   // if(a->watch_dir)
1036 
1037   }
1038 
bg_album_set_expanded(bg_album_t * a,int expanded)1039 void bg_album_set_expanded(bg_album_t * a, int expanded)
1040   {
1041   if(expanded)
1042     a->flags |= BG_ALBUM_EXPANDED;
1043   else
1044     {
1045     bg_album_t * child;
1046     a->flags &= ~BG_ALBUM_EXPANDED;
1047 
1048     /* Unexpand the children too */
1049     child = a->children;
1050     while(child)
1051       {
1052       bg_album_set_expanded(child, 0);
1053       child = child->next;
1054       }
1055     }
1056   }
1057 
bg_album_get_expanded(bg_album_t * a)1058 int bg_album_get_expanded(bg_album_t * a)
1059   {
1060   if(a->flags & BG_ALBUM_EXPANDED)
1061     return 1;
1062   return 0;
1063   }
1064 
bg_album_is_open(bg_album_t * a)1065 int bg_album_is_open(bg_album_t * a)
1066   {
1067   return (a->open_count) ? 1 : 0;
1068   }
1069 
1070 
bg_album_destroy(bg_album_t * a)1071 void bg_album_destroy(bg_album_t * a)
1072   {
1073   //  char * tmp_filename;
1074   bg_album_t       * tmp_album;
1075 
1076   /* Things to do if an album was open */
1077 
1078   if(a->open_count)
1079     {
1080     bg_album_save(a, NULL);
1081     }
1082   if(a->name)
1083     free(a->name);
1084   if(a->xml_file)
1085     free(a->xml_file);
1086   if(a->device)
1087     free(a->device);
1088 
1089   if(a->disc_name)
1090     free(a->disc_name);
1091   if(a->cfg_section)
1092     bg_cfg_section_destroy(a->cfg_section);
1093 
1094   /* Free entries */
1095 
1096   bg_album_entries_destroy(a->entries);
1097 
1098   /* Free Children */
1099 
1100   while(a->children)
1101     {
1102     tmp_album = a->children->next;
1103     bg_album_destroy(a->children);
1104     a->children = tmp_album;
1105     }
1106 
1107   /* free rest */
1108 
1109   free(a);
1110   }
1111 
bg_album_delete_selected(bg_album_t * album)1112 void bg_album_delete_selected(bg_album_t * album)
1113   {
1114   int num_selected = 0;
1115   bg_album_entry_t * cur;
1116   bg_album_entry_t * cur_next;
1117   bg_album_entry_t * new_entries_end = NULL;
1118   bg_album_entry_t * new_entries;
1119   int index, i;
1120   int * indices = NULL;
1121 
1122   if(!album->entries)
1123     return;
1124 
1125   cur = album->entries;
1126 
1127   num_selected = bg_album_num_selected(album);
1128 
1129   if(!num_selected)
1130     return;
1131 
1132   if(album->delete_callback)
1133     {
1134     indices = malloc((num_selected +1)*sizeof(*indices));
1135     }
1136 
1137   cur = album->entries;
1138   new_entries = NULL;
1139   index = 0;
1140   i = 0;
1141 
1142   while(cur)
1143     {
1144     cur_next = cur->next;
1145 
1146     if(cur->flags & BG_ALBUM_ENTRY_SELECTED)
1147       {
1148       if(cur == album->com->current_entry)
1149         {
1150         album->com->current_entry = NULL;
1151         album->com->current_album = NULL;
1152         }
1153       bg_album_entry_destroy(cur);
1154       if(indices)
1155         indices[i] = index;
1156       i++;
1157       }
1158     else
1159       {
1160       if(!new_entries)
1161         {
1162         new_entries = cur;
1163         new_entries_end = cur;
1164         }
1165       else
1166         {
1167         new_entries_end->next = cur;
1168         new_entries_end = new_entries_end->next;
1169         }
1170       }
1171     cur = cur_next;
1172     index++;
1173     }
1174   if(new_entries)
1175     new_entries_end->next = NULL;
1176   album->entries = new_entries;
1177 
1178   delete_shuffle_list(album);
1179 
1180   if(indices)
1181     {
1182     indices[i] = -1;
1183     album->delete_callback(album, indices, album->delete_callback_data);
1184     free(indices);
1185     }
1186   //  bg_album_changed(album);
1187   }
1188 
bg_album_delete_unsync(bg_album_t * album)1189 void bg_album_delete_unsync(bg_album_t * album)
1190   {
1191   int num_selected = 0;
1192   bg_album_entry_t * cur;
1193   bg_album_entry_t * cur_next;
1194   bg_album_entry_t * new_entries_end = NULL;
1195   bg_album_entry_t * new_entries;
1196   int index, i;
1197   int * indices = NULL;
1198 
1199   if(!album->entries)
1200     return;
1201 
1202   cur = album->entries;
1203 
1204   num_selected = bg_album_num_unsync(album);
1205 
1206   if(!num_selected)
1207     return;
1208 
1209   if(album->delete_callback)
1210     {
1211     indices = malloc((num_selected +1)*sizeof(*indices));
1212     }
1213 
1214   cur = album->entries;
1215   new_entries = NULL;
1216   index = 0;
1217   i = 0;
1218 
1219   while(cur)
1220     {
1221     cur_next = cur->next;
1222 
1223     if(!(cur->flags & BG_ALBUM_ENTRY_SYNC))
1224       {
1225       if(cur == album->com->current_entry)
1226         {
1227         album->com->current_entry = NULL;
1228         album->com->current_album = NULL;
1229         }
1230       bg_album_entry_destroy(cur);
1231       if(indices)
1232         indices[i] = index;
1233       i++;
1234       }
1235     else
1236       {
1237       if(!new_entries)
1238         {
1239         new_entries = cur;
1240         new_entries_end = cur;
1241         }
1242       else
1243         {
1244         new_entries_end->next = cur;
1245         new_entries_end = new_entries_end->next;
1246         }
1247       }
1248     cur = cur_next;
1249     index++;
1250     }
1251   if(new_entries)
1252     new_entries_end->next = NULL;
1253   album->entries = new_entries;
1254 
1255   delete_shuffle_list(album);
1256 
1257   if(indices)
1258     {
1259     indices[i] = -1;
1260     album->delete_callback(album, indices, album->delete_callback_data);
1261     free(indices);
1262     }
1263   //  bg_album_changed(album);
1264   }
1265 
1266 
bg_album_delete_with_file(bg_album_t * album,const char * filename)1267 void bg_album_delete_with_file(bg_album_t * album, const char * filename)
1268   {
1269   bg_album_entry_t * cur;
1270   bg_album_entry_t * cur_next;
1271   bg_album_entry_t * new_entries_end = NULL;
1272   bg_album_entry_t * new_entries;
1273   int index, i;
1274   int * indices = NULL;
1275 
1276   if(!album->entries)
1277     return;
1278 
1279   cur = album->entries;
1280 
1281   cur = album->entries;
1282   new_entries = NULL;
1283   index = 0;
1284   i = 0;
1285 
1286   while(cur)
1287     {
1288     cur_next = cur->next;
1289 
1290     if(!strcmp(cur->location, filename))
1291       {
1292       if(cur == album->com->current_entry)
1293         {
1294         album->com->current_entry = NULL;
1295         album->com->current_album = NULL;
1296         }
1297       bg_album_entry_destroy(cur);
1298       if(album->delete_callback)
1299         {
1300         indices = realloc(indices, (i+1) * sizeof(indices));
1301         indices[i] = index;
1302         }
1303       i++;
1304       }
1305     else
1306       {
1307       if(!new_entries)
1308         {
1309         new_entries = cur;
1310         new_entries_end = cur;
1311         }
1312       else
1313         {
1314         new_entries_end->next = cur;
1315         new_entries_end = new_entries_end->next;
1316         }
1317       }
1318     cur = cur_next;
1319     index++;
1320     }
1321   if(new_entries)
1322     new_entries_end->next = NULL;
1323   album->entries = new_entries;
1324 
1325   delete_shuffle_list(album);
1326 
1327   if(indices)
1328     {
1329     indices = realloc(indices, (i+1) * sizeof(indices));
1330 
1331     indices[i] = -1;
1332     album->delete_callback(album, indices, album->delete_callback_data);
1333     free(indices);
1334     }
1335   }
1336 
1337 
bg_album_select_error_tracks(bg_album_t * album)1338 void bg_album_select_error_tracks(bg_album_t * album)
1339   {
1340   bg_album_entry_t * cur;
1341   cur = album->entries;
1342   while(cur)
1343     {
1344     if(cur->flags & BG_ALBUM_ENTRY_ERROR)
1345       cur->flags |= BG_ALBUM_ENTRY_SELECTED;
1346     else
1347       cur->flags &= ~BG_ALBUM_ENTRY_SELECTED;
1348     cur = cur->next;
1349     }
1350   bg_album_changed(album);
1351   }
1352 
1353 
copy_selected(bg_album_t * album)1354 static bg_album_entry_t * copy_selected(bg_album_t * album)
1355   {
1356   bg_album_entry_t * ret     = NULL;
1357   bg_album_entry_t * ret_end = NULL;
1358   bg_album_entry_t * tmp_entry;
1359 
1360   tmp_entry = album->entries;
1361 
1362   while(tmp_entry)
1363     {
1364     if(tmp_entry->flags & BG_ALBUM_ENTRY_SELECTED)
1365       {
1366       if(ret)
1367         {
1368         ret_end->next = bg_album_entry_copy(album, tmp_entry);
1369         ret_end = ret_end->next;
1370         }
1371       else
1372         {
1373         ret = bg_album_entry_copy(album, tmp_entry);
1374         ret_end = ret;
1375         }
1376       }
1377     tmp_entry = tmp_entry->next;
1378     }
1379   return ret;
1380   }
1381 
extract_selected(bg_album_t * album)1382 static bg_album_entry_t * extract_selected(bg_album_t * album)
1383   {
1384   bg_album_entry_t * selected_end = NULL;
1385   bg_album_entry_t * other_end = NULL;
1386   bg_album_entry_t * tmp_entry;
1387 
1388   bg_album_entry_t * other    = NULL;
1389   bg_album_entry_t * selected = NULL;
1390 
1391   while(album->entries)
1392     {
1393     tmp_entry = album->entries->next;
1394 
1395     if(album->entries->flags & BG_ALBUM_ENTRY_SELECTED)
1396       {
1397       if(!selected)
1398         {
1399         selected = album->entries;
1400         selected_end = selected;
1401         }
1402       else
1403         {
1404         selected_end->next = album->entries;
1405         selected_end = selected_end->next;
1406         }
1407       selected_end->next = NULL;
1408       }
1409     else
1410       {
1411       if(!other)
1412         {
1413         other = album->entries;
1414         other_end = other;
1415         }
1416       else
1417         {
1418         other_end->next = album->entries;
1419         other_end = other_end->next;
1420         }
1421       other_end->next = NULL;
1422       }
1423     album->entries = tmp_entry;
1424     }
1425   album->entries = other;
1426   return selected;
1427   }
1428 
bg_album_move_selected_up(bg_album_t * album)1429 void bg_album_move_selected_up(bg_album_t * album)
1430   {
1431   bg_album_entry_t * selected;
1432   selected = extract_selected(album);
1433 
1434   bg_album_insert_entries_after(album,
1435                                 selected,
1436                                 NULL);
1437   bg_album_changed(album);
1438   }
1439 
bg_album_move_selected_down(bg_album_t * album)1440 void bg_album_move_selected_down(bg_album_t * album)
1441   {
1442   bg_album_entry_t * selected;
1443   selected = extract_selected(album);
1444 
1445   bg_album_insert_entries_before(album,
1446                                  selected,
1447                                  NULL);
1448   bg_album_changed(album);
1449   }
1450 
1451 typedef struct
1452   {
1453   bg_album_entry_t * entry;
1454   char * sort_string;
1455   } sort_entries_struct;
1456 
bg_album_sort_entries(bg_album_t * album)1457 void bg_album_sort_entries(bg_album_t * album)
1458   {
1459   int num_entries;
1460   int i, j;
1461   char * tmp_string;
1462   int sort_string_len;
1463   sort_entries_struct * s_tmp;
1464   int keep_going;
1465 
1466   sort_entries_struct ** s;
1467   bg_album_entry_t * tmp_entry;
1468 
1469   /* 1. Count the entries */
1470 
1471   num_entries = 0;
1472 
1473   tmp_entry = album->entries;
1474 
1475   while(tmp_entry)
1476     {
1477     tmp_entry = tmp_entry->next;
1478     num_entries++;
1479     }
1480 
1481   if(!num_entries)
1482     return;
1483 
1484   /* Set up the album array */
1485 
1486   s = malloc(num_entries * sizeof(*s));
1487 
1488   tmp_entry = album->entries;
1489 
1490   for(i = 0; i < num_entries; i++)
1491     {
1492     s[i] = calloc(1, sizeof(*(s[i])));
1493     s[i]->entry = tmp_entry;
1494 
1495     /* Set up the sort string */
1496 
1497     tmp_string = bg_utf8_to_system(tmp_entry->name,
1498                                    strlen(tmp_entry->name));
1499 
1500     sort_string_len = strxfrm(NULL, tmp_string, 0);
1501     s[i]->sort_string = malloc(sort_string_len+1);
1502     strxfrm(s[i]->sort_string, tmp_string, sort_string_len+1);
1503 
1504     free(tmp_string);
1505 
1506     /* Advance */
1507 
1508     tmp_entry = tmp_entry->next;
1509     }
1510 
1511   /* Now, do a braindead bubblesort algorithm */
1512 
1513   for(i = 0; i < num_entries - 1; i++)
1514     {
1515     keep_going = 0;
1516     for(j = num_entries-1; j > i; j--)
1517       {
1518       if(strcmp(s[j]->sort_string, s[j-1]->sort_string) < 0)
1519         {
1520         s_tmp  = s[j];
1521         s[j]   = s[j-1];
1522         s[j-1] = s_tmp;
1523         keep_going = 1;
1524         }
1525       }
1526     if(!keep_going)
1527       break;
1528     }
1529 
1530   /* Rechain entries */
1531 
1532   album->entries = s[0]->entry;
1533 
1534   for(i = 0; i < num_entries-1; i++)
1535     {
1536     s[i]->entry->next = s[i+1]->entry;
1537     }
1538 
1539   s[num_entries-1]->entry->next = NULL;
1540 
1541   /* Free everything */
1542 
1543   for(i = 0; i < num_entries; i++)
1544     {
1545     free(s[i]->sort_string);
1546     free(s[i]);
1547     }
1548   free(s);
1549   bg_album_changed(album);
1550   }
1551 
1552 typedef struct
1553   {
1554   bg_album_t * child;
1555   char * sort_string;
1556   } sort_children_struct;
1557 
bg_album_sort_children(bg_album_t * album)1558 void bg_album_sort_children(bg_album_t * album)
1559   {
1560   int num_children;
1561   int i, j;
1562   char * tmp_string;
1563   int sort_string_len;
1564   sort_children_struct * s_tmp;
1565   int keep_going;
1566 
1567   sort_children_struct ** s;
1568   bg_album_t * tmp_child;
1569 
1570   /* 1. Count the children */
1571 
1572   num_children = 0;
1573 
1574   tmp_child = album->children;
1575 
1576   while(tmp_child)
1577     {
1578     tmp_child = tmp_child->next;
1579     num_children++;
1580     }
1581 
1582   if(!num_children)
1583     return;
1584 
1585   /* Set up the album array */
1586 
1587   s = malloc(num_children * sizeof(*s));
1588 
1589   tmp_child = album->children;
1590 
1591   for(i = 0; i < num_children; i++)
1592     {
1593     s[i] = calloc(1, sizeof(*(s[i])));
1594     s[i]->child = tmp_child;
1595 
1596     /* Set up the sort string */
1597 
1598     tmp_string = bg_utf8_to_system(tmp_child->name,
1599                                    strlen(tmp_child->name));
1600 
1601     sort_string_len = strxfrm(NULL, tmp_string, 0);
1602     s[i]->sort_string = malloc(sort_string_len+1);
1603     strxfrm(s[i]->sort_string, tmp_string, sort_string_len+1);
1604 
1605     free(tmp_string);
1606 
1607     /* Advance */
1608 
1609     tmp_child = tmp_child->next;
1610     }
1611 
1612   /* Now, do a braindead bubblesort algorithm */
1613 
1614   for(i = 0; i < num_children - 1; i++)
1615     {
1616     keep_going = 0;
1617     for(j = num_children-1; j > i; j--)
1618       {
1619       if(strcmp(s[j]->sort_string, s[j-1]->sort_string) < 0)
1620         {
1621         s_tmp  = s[j];
1622         s[j]   = s[j-1];
1623         s[j-1] = s_tmp;
1624         keep_going = 1;
1625         }
1626       }
1627     if(!keep_going)
1628       break;
1629     }
1630 
1631   /* Rechain children */
1632 
1633   album->children = s[0]->child;
1634 
1635   for(i = 0; i < num_children-1; i++)
1636     {
1637     s[i]->child->next = s[i+1]->child;
1638     }
1639 
1640   s[num_children-1]->child->next = NULL;
1641 
1642   /* Free everything */
1643 
1644   for(i = 0; i < num_children; i++)
1645     {
1646     free(s[i]->sort_string);
1647     free(s[i]);
1648     }
1649   free(s);
1650   }
1651 
1652 /* END */
1653 
bg_album_rename_track(bg_album_t * album,const bg_album_entry_t * entry_c,const char * name)1654 void bg_album_rename_track(bg_album_t * album,
1655                            const bg_album_entry_t * entry_c,
1656                            const char * name)
1657   {
1658   bg_album_entry_t * entry;
1659 
1660   entry = album->entries;
1661 
1662   while(entry)
1663     {
1664     if(entry == entry_c)
1665       break;
1666     entry = entry->next;
1667     }
1668   entry->name = bg_strdup(entry->name, name);
1669 
1670   if(entry->name_w)
1671     {
1672     free(entry->name_w);
1673     entry->name_w = NULL;
1674     entry->len_w = 0;
1675     }
1676   bg_album_entry_changed(album, entry);
1677 
1678   }
1679 
bg_album_rename(bg_album_t * a,const char * name)1680 void bg_album_rename(bg_album_t * a, const char * name)
1681   {
1682   a->name = bg_strdup(a->name, name);
1683 
1684   if(((a->type == BG_ALBUM_TYPE_REMOVABLE) ||
1685       (a->type == BG_ALBUM_TYPE_TUNER)) &&
1686      a->plugin_info)
1687     {
1688     bg_plugin_registry_set_device_name(a->com->plugin_reg,
1689                                        a->plugin_info->name, a->device,
1690                                        name);
1691     }
1692   if(a->name_change_callback)
1693     a->name_change_callback(a, name, a->name_change_callback_data);
1694 
1695   }
1696 
1697 
bg_album_get_current_entry(bg_album_t * a)1698 bg_album_entry_t * bg_album_get_current_entry(bg_album_t * a)
1699   {
1700   return a->com->current_entry;
1701   }
1702 
bg_album_next(bg_album_t * a,int wrap)1703 int bg_album_next(bg_album_t * a, int wrap)
1704   {
1705   if(a->com->current_entry)
1706     {
1707     if(!a->com->current_entry->next)
1708       {
1709       if(wrap)
1710         {
1711         if(a->com->set_current_callback)
1712           a->com->set_current_callback(a->com->set_current_callback_data,
1713                                        a, a->entries);
1714         return 1;
1715         }
1716       else
1717         return 0;
1718       }
1719     else
1720       {
1721       if(a->com->set_current_callback)
1722         a->com->set_current_callback(a->com->set_current_callback_data,
1723                                      a, a->com->current_entry->next);
1724       return 1;
1725       }
1726     }
1727   else
1728     return 0;
1729   }
1730 
1731 
bg_album_get_duration(bg_album_t * a)1732 gavl_time_t bg_album_get_duration(bg_album_t * a)
1733   {
1734   gavl_time_t ret = 0;
1735   bg_album_entry_t * e;
1736   e = a->entries;
1737   while(e)
1738     {
1739     if(e->duration == GAVL_TIME_UNDEFINED)
1740       return GAVL_TIME_UNDEFINED;
1741     else
1742       ret += e->duration;
1743     e = e->next;
1744     }
1745   return ret;
1746   }
1747 
1748 
bg_album_get_index(bg_album_t * a,const bg_album_entry_t * entry)1749 int bg_album_get_index(bg_album_t* a, const bg_album_entry_t * entry)
1750   {
1751   int index = 0;
1752   const bg_album_entry_t * e;
1753   e = a->entries;
1754   while(1)
1755     {
1756     if(e == entry)
1757       return index;
1758 
1759     index++;
1760     e = e->next;
1761 
1762     if(!e)
1763       break;
1764     }
1765   return -1;
1766   }
1767 
1768 
bg_album_previous(bg_album_t * a,int wrap)1769 int bg_album_previous(bg_album_t * a, int wrap)
1770   {
1771   bg_album_entry_t * tmp_entry;
1772 
1773   if(!a->com->current_entry)
1774     return 0;
1775 
1776   if(a->com->current_entry == a->entries)
1777     {
1778     if(!wrap)
1779       return 0;
1780     tmp_entry = a->entries;
1781 
1782     while(tmp_entry->next)
1783       tmp_entry = tmp_entry->next;
1784     if(a->com->set_current_callback)
1785       a->com->set_current_callback(a->com->set_current_callback_data,
1786                                    a, tmp_entry);
1787     return 1;
1788     }
1789   else
1790     {
1791     tmp_entry = a->entries;
1792     while(tmp_entry->next != a->com->current_entry)
1793       tmp_entry = tmp_entry->next;
1794     if(a->com->set_current_callback)
1795       a->com->set_current_callback(a->com->set_current_callback_data,
1796                                    a, tmp_entry);
1797     return 1;
1798     }
1799   }
1800 
bg_album_set_change_callback(bg_album_t * a,void (* change_callback)(bg_album_t * a,void * data),void * change_callback_data)1801 void bg_album_set_change_callback(bg_album_t * a,
1802                                   void (*change_callback)(bg_album_t * a, void * data),
1803                                   void * change_callback_data)
1804   {
1805   a->change_callback      = change_callback;
1806   a->change_callback_data = change_callback_data;
1807   }
1808 
bg_album_set_entry_change_callback(bg_album_t * a,void (* change_callback)(bg_album_t * a,const bg_album_entry_t * e,void * data),void * change_callback_data)1809 void bg_album_set_entry_change_callback(bg_album_t * a,
1810                                         void (*change_callback)(bg_album_t * a,
1811                                                                 const bg_album_entry_t * e,
1812                                                                 void * data),
1813                                         void * change_callback_data)
1814   {
1815   a->entry_change_callback      = change_callback;
1816   a->entry_change_callback_data = change_callback_data;
1817   }
1818 
bg_album_set_current_change_callback(bg_album_t * a,void (* change_callback)(bg_album_t * a,const bg_album_entry_t * e,void * data),void * change_callback_data)1819 void bg_album_set_current_change_callback(bg_album_t * a,
1820                                           void (*change_callback)(bg_album_t * a,
1821                                                                   const bg_album_entry_t * e,
1822                                                                   void * data),
1823                                           void * change_callback_data)
1824   {
1825   a->current_change_callback      = change_callback;
1826   a->current_change_callback_data = change_callback_data;
1827   }
1828 
bg_album_set_delete_callback(bg_album_t * a,void (* delete_callback)(bg_album_t * current_album,int * indices,void * data),void * delete_callback_data)1829 void bg_album_set_delete_callback(bg_album_t * a,
1830                                   void (*delete_callback)(bg_album_t * current_album,
1831                                                           int * indices, void * data),
1832                                   void * delete_callback_data)
1833   {
1834   a->delete_callback      = delete_callback;
1835   a->delete_callback_data = delete_callback_data;
1836   }
1837 
bg_album_set_insert_callback(bg_album_t * a,void (* insert_callback)(bg_album_t * current_album,int start,int num,void * data),void * insert_callback_data)1838 void bg_album_set_insert_callback(bg_album_t * a,
1839                                   void (*insert_callback)(bg_album_t * current_album,
1840                                                           int start, int num, void * data),
1841                                   void * insert_callback_data)
1842   {
1843   a->insert_callback      = insert_callback;
1844   a->insert_callback_data = insert_callback_data;
1845   }
1846 
1847 
1848 
bg_album_set_name_change_callback(bg_album_t * a,void (* name_change_callback)(bg_album_t * a,const char * name,void * data),void * name_change_callback_data)1849 void bg_album_set_name_change_callback(bg_album_t * a,
1850                                        void (*name_change_callback)(bg_album_t * a,
1851                                                                     const char * name,
1852                                                                     void * data),
1853                                        void * name_change_callback_data)
1854   {
1855   a->name_change_callback      = name_change_callback;
1856   a->name_change_callback_data = name_change_callback_data;
1857   }
1858 
bg_album_changed(bg_album_t * a)1859 void bg_album_changed(bg_album_t * a)
1860   {
1861   if(a->change_callback)
1862     a->change_callback(a, a->change_callback_data);
1863   }
1864 
bg_album_current_changed(bg_album_t * a)1865 void bg_album_current_changed(bg_album_t * a)
1866   {
1867   if(a->current_change_callback)
1868     a->current_change_callback(a->com->current_album, a->com->current_entry,
1869                                a->current_change_callback_data);
1870   }
1871 
bg_album_entry_changed(bg_album_t * a,const bg_album_entry_t * e)1872 void bg_album_entry_changed(bg_album_t * a, const bg_album_entry_t * e)
1873   {
1874   if(a->entry_change_callback)
1875     a->entry_change_callback(a->com->current_album, e,
1876                              a->entry_change_callback_data);
1877   }
1878 
bg_album_set_current(bg_album_t * a,const bg_album_entry_t * e)1879 void bg_album_set_current(bg_album_t * a, const bg_album_entry_t * e)
1880   {
1881   bg_album_entry_t * tmp_entry;
1882 
1883   tmp_entry = a->entries;
1884   while(tmp_entry != e)
1885     tmp_entry = tmp_entry->next;
1886 
1887   if(a->com->set_current_callback)
1888     a->com->set_current_callback(a->com->set_current_callback_data,
1889                                  a, tmp_entry);
1890   //  bg_album_current_changed(a);
1891   }
1892 
1893 
bg_album_play(bg_album_t * a)1894 void bg_album_play(bg_album_t * a)
1895   {
1896   if(a->com->play_callback)
1897     a->com->play_callback(a->com->play_callback_data);
1898   }
1899 
bg_album_get_cfg_section(bg_album_t * album)1900 bg_cfg_section_t * bg_album_get_cfg_section(bg_album_t * album)
1901   {
1902   return album->cfg_section;
1903   }
1904 
1905 
bg_album_is_current(bg_album_t * a)1906 int bg_album_is_current(bg_album_t * a)
1907   {
1908   return (a == a->com->current_album) ? 1 : 0;
1909   }
1910 
bg_album_entry_is_current(bg_album_t * a,bg_album_entry_t * e)1911 int bg_album_entry_is_current(bg_album_t * a,
1912                               bg_album_entry_t * e)
1913   {
1914   return ((a == a->com->current_album) &&
1915           (e == a->com->current_entry)) ? 1 : 0;
1916   }
1917 
bg_album_get_plugin_registry(bg_album_t * a)1918 bg_plugin_registry_t * bg_album_get_plugin_registry(bg_album_t * a)
1919   {
1920   return a->com->plugin_reg;
1921   }
1922 
bg_album_get_times(bg_album_t * a,gavl_time_t * duration_before,gavl_time_t * duration_current,gavl_time_t * duration_after)1923 void bg_album_get_times(bg_album_t * a,
1924                         gavl_time_t * duration_before,
1925                         gavl_time_t * duration_current,
1926                         gavl_time_t * duration_after)
1927   {
1928   bg_album_entry_t * e;
1929 
1930   if(a != a->com->current_album)
1931     {
1932     *duration_before = GAVL_TIME_UNDEFINED;
1933     *duration_current = GAVL_TIME_UNDEFINED;
1934     *duration_after = GAVL_TIME_UNDEFINED;
1935     return;
1936     }
1937 
1938   e = a->entries;
1939   *duration_before = 0;
1940   while(e != a->com->current_entry)
1941     {
1942     if(e->duration == GAVL_TIME_UNDEFINED)
1943       {
1944       *duration_before = GAVL_TIME_UNDEFINED;
1945       break;
1946       }
1947     *duration_before += e->duration;
1948     e = e->next;
1949     }
1950 
1951   *duration_current = a->com->current_entry->duration;
1952 
1953   *duration_after = 0;
1954 
1955   e = a->com->current_entry->next;
1956   while(e)
1957     {
1958     if(e->duration == GAVL_TIME_UNDEFINED)
1959       {
1960       *duration_after = GAVL_TIME_UNDEFINED;
1961       break;
1962       }
1963     *duration_after += e->duration;
1964     e = e->next;
1965     }
1966   }
1967 
bg_album_set_error(bg_album_t * a,int err)1968 void bg_album_set_error(bg_album_t * a, int err)
1969   {
1970   if(err)
1971     a->flags |= BG_ALBUM_ERROR;
1972   else
1973     a->flags &= ~BG_ALBUM_ERROR;
1974   }
1975 
bg_album_get_error(bg_album_t * a)1976 int  bg_album_get_error(bg_album_t * a)
1977   {
1978   return !!(a->flags & BG_ALBUM_ERROR);
1979   }
1980 
bg_album_append_child(bg_album_t * parent,bg_album_t * child)1981 void bg_album_append_child(bg_album_t * parent, bg_album_t * child)
1982   {
1983   bg_album_t * album_before;
1984   if(parent->children)
1985     {
1986     album_before = parent->children;
1987     while(album_before->next)
1988       album_before = album_before->next;
1989     album_before->next = child;
1990     }
1991   else
1992     parent->children = child;
1993   }
1994 
add_device(bg_album_t * album,const char * device,const char * name)1995 static void add_device(bg_album_t * album,
1996                        const char * device,
1997                        const char * name)
1998   {
1999   bg_album_t * device_album;
2000   bg_album_type_t type = BG_ALBUM_TYPE_REGULAR;
2001   if(album->plugin_info->flags & BG_PLUGIN_REMOVABLE)
2002     type = BG_ALBUM_TYPE_REMOVABLE;
2003   else if(album->plugin_info->flags & BG_PLUGIN_TUNER)
2004     type = BG_ALBUM_TYPE_TUNER;
2005 
2006   device_album = bg_album_create(album->com, type, album);
2007 
2008   device_album->device = bg_strdup(device_album->device, device);
2009   if(name)
2010     {
2011     device_album->name = bg_strdup(device_album->name,
2012                                    name);
2013     }
2014   else
2015     {
2016     device_album->name = bg_strdup(device_album->name,
2017                                    device);
2018     }
2019 
2020   device_album->plugin_info = album->plugin_info;
2021   bg_album_append_child(album, device_album);
2022   }
2023 
bg_album_add_device(bg_album_t * album,const char * device,const char * name)2024 void bg_album_add_device(bg_album_t * album,
2025                          const char * device,
2026                          const char * name)
2027   {
2028   add_device(album, device, name);
2029   bg_plugin_registry_add_device(album->com->plugin_reg,
2030                                 album->plugin_info->name,
2031                                 device, name);
2032   }
2033 
2034 static bg_album_t *
remove_from_list(bg_album_t * list,bg_album_t * album,int * index)2035 remove_from_list(bg_album_t * list, bg_album_t * album, int * index)
2036   {
2037   bg_album_t * sibling_before;
2038 
2039   *index = 0;
2040 
2041   if(album == list)
2042     return album->next;
2043   else
2044     {
2045     sibling_before = list;
2046     (*index)++;
2047     while(sibling_before->next != album)
2048       {
2049       sibling_before = sibling_before->next;
2050       (*index)++;
2051       }
2052     sibling_before->next = album->next;
2053     return list;
2054     }
2055   }
2056 
bg_album_remove_from_parent(bg_album_t * album)2057 void bg_album_remove_from_parent(bg_album_t * album)
2058   {
2059   int index;
2060   if(!album->parent)
2061     return;
2062 
2063   album->parent->children = remove_from_list(album->parent->children, album, &index);
2064 
2065 
2066   if(album->type == BG_ALBUM_TYPE_REMOVABLE)
2067     {
2068     bg_plugin_registry_remove_device(album->com->plugin_reg,
2069                                      album->plugin_info->name,
2070                                      album->plugin_info->devices[index].device,
2071                                      album->plugin_info->devices[index].name);
2072     }
2073   }
2074 
bg_album_set_devices(bg_album_t * a)2075 void bg_album_set_devices(bg_album_t * a)
2076   {
2077   bg_album_t * tmp_album;
2078   int j;
2079 
2080   /* Delete previous children */
2081   while(a->children)
2082     {
2083     tmp_album = a->children->next;
2084     bg_album_destroy(a->children);
2085     a->children = tmp_album;
2086     }
2087 
2088   if(a->plugin_info->devices && a->plugin_info->devices->device)
2089     {
2090     j = 0;
2091 
2092     while(a->plugin_info->devices[j].device)
2093       {
2094       add_device(a, a->plugin_info->devices[j].device,
2095                  a->plugin_info->devices[j].name);
2096       j++;
2097       } /* Device loop */
2098     }
2099 
2100   }
2101 
bg_album_find_devices(bg_album_t * a)2102 void bg_album_find_devices(bg_album_t * a)
2103   {
2104   bg_plugin_registry_find_devices(a->com->plugin_reg, a->plugin_info->name);
2105   bg_album_set_devices(a);
2106   }
2107 
remove_redirectors(bg_album_t * album,bg_album_entry_t * entries)2108 static bg_album_entry_t * remove_redirectors(bg_album_t * album,
2109                                              bg_album_entry_t * entries)
2110   {
2111   bg_album_entry_t * before;
2112   bg_album_entry_t * e;
2113   bg_album_entry_t * new_entry, * end_entry;
2114   int done = 0;
2115   const bg_plugin_info_t * info;
2116   const char * name;
2117 
2118   done = 1;
2119   e = entries;
2120 
2121   while(e)
2122     {
2123     if(e->flags & BG_ALBUM_ENTRY_REDIRECTOR)
2124       {
2125       /* Load "real" url */
2126       if(e->plugin)
2127         {
2128         info = bg_plugin_find_by_name(album->com->plugin_reg,
2129                                       e->plugin);
2130 
2131         name = info->name;
2132         }
2133       else
2134         name = NULL;
2135 
2136       new_entry = bg_album_load_url(album,
2137                                     e->location,
2138                                     name, 0);
2139       if(new_entry)
2140         {
2141         /* Insert new entries into list */
2142         if(e != entries)
2143           {
2144           before = entries;
2145           while(before->next != e)
2146             before = before->next;
2147           before->next = new_entry;
2148           }
2149         else
2150           {
2151           entries = new_entry;
2152           }
2153         end_entry = new_entry;
2154         while(end_entry->next)
2155           {
2156           /* Set plugin so we don't have to set it next time */
2157           end_entry->plugin = bg_strdup(end_entry->plugin, album->com->load_handle->info->name);
2158           end_entry = end_entry->next;
2159           }
2160         /* Set plugin so we don't have to set it next time */
2161         end_entry->plugin = bg_strdup(end_entry->plugin, album->com->load_handle->info->name);
2162         end_entry->next = e->next;
2163         bg_album_entry_destroy(e);
2164         e = new_entry;
2165         }
2166       else
2167         {
2168         /* Remove e from list */
2169         if(e != entries)
2170           {
2171           before = entries;
2172           while(before->next != e)
2173             before = before->next;
2174           before->next = e->next;
2175           }
2176         else
2177           {
2178           entries = e->next;
2179           before = NULL;
2180           }
2181         bg_album_entry_destroy(e);
2182         e = (before) ? before->next : entries;
2183         }
2184       }
2185     else
2186       {
2187       /* Leave e as it is */
2188       e = e->next;
2189       }
2190     }
2191 
2192   return entries;
2193   }
2194 
is_blacklisted(bg_album_common_t * com,const char * url)2195 static int is_blacklisted(bg_album_common_t * com,
2196                           const char * url)
2197   {
2198   const char * pos;
2199   if(!com->blacklist) // No blacklist
2200     return 0;
2201   if(strncmp(url, "file:", 5) && (*url != '/'))  // Remote file
2202     return 0;
2203 
2204   pos = strrchr(url, '.');
2205   if(pos)
2206     {
2207     pos++;
2208     if(bg_string_match(pos, com->blacklist))
2209       {
2210       bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Not loading %s (blacklisted extension)", url);
2211       return 1;
2212       }
2213     }
2214 
2215   pos = strrchr(url, '/');
2216   if(pos)
2217     {
2218     pos++;
2219     if(bg_string_match(pos, com->blacklist_files))
2220       {
2221       bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Not loading %s (blacklisted filename)", url);
2222       return 1;
2223       }
2224     }
2225   return 0;
2226   }
2227 
bg_album_load_url(bg_album_t * album,char * url,const char * plugin_name,int prefer_edl)2228 bg_album_entry_t * bg_album_load_url(bg_album_t * album,
2229                                      char * url,
2230                                      const char * plugin_name,
2231                                      int prefer_edl)
2232   {
2233   int i, num_entries;
2234 
2235   bg_album_entry_t * new_entry;
2236   bg_album_entry_t * end_entry = NULL;
2237   bg_album_entry_t * ret       = NULL;
2238 
2239   //  char * system_location;
2240 
2241   bg_input_plugin_t * plugin;
2242   bg_track_info_t * track_info;
2243   const bg_plugin_info_t * info;
2244   //  const char * file_plugin_name;
2245 
2246   if(is_blacklisted(album->com, url))
2247     {
2248     return NULL;
2249     }
2250 
2251   bg_log(BG_LOG_INFO, LOG_DOMAIN, "Loading %s", url);
2252 
2253   /* Load the appropriate plugin */
2254 
2255   if(plugin_name)
2256     {
2257     info = bg_plugin_find_by_name(album->com->plugin_reg,
2258                                   plugin_name);
2259     }
2260   else
2261     info = NULL;
2262 
2263   bg_album_common_prepare_callbacks(album->com, NULL);
2264 
2265   if(!bg_input_plugin_load(album->com->plugin_reg,
2266                            url, info,
2267                            &album->com->load_handle,
2268                            &album->com->input_callbacks, prefer_edl))
2269     {
2270     bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Loading %s failed", url);
2271     return NULL;
2272     }
2273   plugin = (bg_input_plugin_t*)(album->com->load_handle->plugin);
2274 
2275   /* Open the track */
2276 
2277   if(!plugin->get_num_tracks)
2278     num_entries = 1;
2279   else
2280     num_entries = plugin->get_num_tracks(album->com->load_handle->priv);
2281 
2282   for(i = 0; i < num_entries; i++)
2283     {
2284     track_info = plugin->get_track_info(album->com->load_handle->priv, i);
2285 
2286     new_entry = bg_album_entry_create();
2287     //    new_entry->location = bg_system_to_utf8(url, strlen(url));
2288     new_entry->location = bg_strdup(new_entry->location, url);
2289     new_entry->index = i;
2290     new_entry->total_tracks = num_entries;
2291 
2292     if(album->com->load_handle->edl)
2293       new_entry->flags |= BG_ALBUM_ENTRY_EDL;
2294 
2295     bg_log(BG_LOG_INFO, LOG_DOMAIN, "Loaded %s (track %d of %d)", url,
2296            new_entry->index+1, new_entry->total_tracks);
2297 
2298     bg_album_common_set_auth_info(album->com, new_entry);
2299     bg_album_update_entry(album, new_entry, track_info, 0, 1);
2300 
2301     new_entry->plugin = bg_strdup(new_entry->plugin, plugin_name);
2302 
2303     if(ret)
2304       {
2305       end_entry->next = new_entry;
2306       end_entry = end_entry->next;
2307       }
2308     else
2309       {
2310       ret = new_entry;
2311       end_entry = ret;
2312       }
2313 
2314     }
2315   plugin->close(album->com->load_handle->priv);
2316 
2317   ret = remove_redirectors(album, ret);
2318   return ret;
2319   }
2320 
bg_album_get_unique_id(bg_album_t * album)2321 int bg_album_get_unique_id(bg_album_t * album)
2322   {
2323   album->com->highest_id++;
2324   return album->com->highest_id;
2325   }
2326 
refresh_entry(bg_album_t * album,bg_album_entry_t * entry,bg_edl_t * edl)2327 static int refresh_entry(bg_album_t * album,
2328                          bg_album_entry_t * entry, bg_edl_t * edl)
2329   {
2330   const bg_plugin_info_t * info;
2331   //  char * system_location;
2332 
2333   bg_input_plugin_t * plugin;
2334   bg_track_info_t * track_info;
2335   int i;
2336   /* Check, which plugin to use */
2337 
2338   if(entry->plugin)
2339     {
2340     info = bg_plugin_find_by_name(album->com->plugin_reg, entry->plugin);
2341     }
2342   else
2343     info = NULL;
2344 
2345   // system_location = bg_utf8_to_system(entry->location,
2346   //                                     strlen(entry->location));
2347   bg_album_common_prepare_callbacks(album->com, entry);
2348   if(!bg_input_plugin_load(album->com->plugin_reg,
2349                            entry->location,
2350                            info,
2351                            &album->com->load_handle, &album->com->input_callbacks,
2352                            !!(entry->flags & BG_ALBUM_ENTRY_EDL)))
2353     {
2354     entry->flags |= BG_ALBUM_ENTRY_ERROR;
2355     bg_album_entry_changed(album, entry);
2356     return 0;
2357     }
2358 
2359   plugin = (bg_input_plugin_t*)(album->com->load_handle->plugin);
2360 
2361   track_info = plugin->get_track_info(album->com->load_handle->priv,
2362                                       entry->index);
2363 
2364   bg_album_common_set_auth_info(album->com, entry);
2365 
2366   if(edl) /* Need timescales */
2367     {
2368     if(plugin->set_track)
2369       plugin->set_track(album->com->load_handle->priv, entry->index);
2370 
2371     if(plugin->set_audio_stream)
2372       {
2373       for(i = 0; i < track_info->num_audio_streams; i++)
2374         plugin->set_audio_stream(album->com->load_handle->priv,
2375                                  i, BG_STREAM_ACTION_DECODE);
2376       }
2377     if(plugin->set_video_stream)
2378       {
2379       for(i = 0; i < track_info->num_video_streams; i++)
2380         plugin->set_video_stream(album->com->load_handle->priv,
2381                                  i, BG_STREAM_ACTION_DECODE);
2382       }
2383     if(plugin->set_subtitle_stream)
2384       {
2385       for(i = 0; i < track_info->num_subtitle_streams; i++)
2386         plugin->set_subtitle_stream(album->com->load_handle->priv,
2387                                     i, BG_STREAM_ACTION_DECODE);
2388       }
2389 
2390     if(plugin->start)
2391       plugin->start(album->com->load_handle->priv);
2392     bg_edl_append_track_info(edl, track_info, entry->location, entry->index,
2393                              entry->total_tracks, entry->name);
2394     }
2395 
2396   bg_album_update_entry(album, entry, track_info, 1, 1);
2397   plugin->close(album->com->load_handle->priv);
2398   bg_album_entry_changed(album, entry);
2399   return 1;
2400   }
2401 
bg_album_refresh_selected(bg_album_t * album)2402 void bg_album_refresh_selected(bg_album_t * album)
2403   {
2404   bg_album_entry_t * cur;
2405   cur = album->entries;
2406   while(cur)
2407     {
2408     if(cur->flags & BG_ALBUM_ENTRY_SELECTED)
2409       refresh_entry(album, cur, NULL);
2410     cur = cur->next;
2411     }
2412   }
2413 
bg_album_selected_to_edl(bg_album_t * album)2414 bg_edl_t * bg_album_selected_to_edl(bg_album_t * album)
2415   {
2416   bg_edl_t * ret;
2417   bg_album_entry_t * cur;
2418 
2419   ret = bg_edl_create();
2420 
2421   cur = album->entries;
2422   while(cur)
2423     {
2424     if(cur->flags & BG_ALBUM_ENTRY_SELECTED)
2425       refresh_entry(album, cur, ret);
2426     cur = cur->next;
2427     }
2428   return ret;
2429   }
2430 
2431 
bg_album_copy_selected_to_favourites(bg_album_t * a)2432 void bg_album_copy_selected_to_favourites(bg_album_t * a)
2433   {
2434   int was_open;
2435   bg_album_entry_t * sel;
2436   sel = copy_selected(a);
2437 
2438   if(!bg_album_is_open(a->com->favourites))
2439     {
2440     bg_album_open(a->com->favourites);
2441     was_open = 0;
2442     }
2443   was_open = 1;
2444 
2445   bg_album_insert_entries_before(a->com->favourites, sel, NULL);
2446 
2447   if(!was_open)
2448     bg_album_close(a->com->favourites);
2449   }
2450 
bg_album_move_selected_to_favourites(bg_album_t * a)2451 void bg_album_move_selected_to_favourites(bg_album_t * a)
2452   {
2453   int was_open;
2454   bg_album_entry_t * sel;
2455   sel = extract_selected(a);
2456 
2457   if(!bg_album_is_open(a->com->favourites))
2458     {
2459     bg_album_open(a->com->favourites);
2460     was_open = 0;
2461     }
2462   was_open = 1;
2463 
2464   bg_album_insert_entries_before(a->com->favourites, sel, NULL);
2465 
2466   if(!was_open)
2467     bg_album_close(a->com->favourites);
2468   }
2469 
bg_album_get_disc_name(bg_album_t * a)2470 const char * bg_album_get_disc_name(bg_album_t * a)
2471   {
2472   return a->disc_name;
2473   }
2474 
bg_album_get_label(bg_album_t * a)2475 char * bg_album_get_label(bg_album_t * a)
2476   {
2477   return a->disc_name ? a->disc_name : a->name;
2478   }
2479 
2480 
bg_album_can_eject(bg_album_t * a)2481 int bg_album_can_eject(bg_album_t * a)
2482   {
2483   /* Leave this disabled until ejecting really works */
2484   return !!(a->flags & BG_ALBUM_CAN_EJECT);
2485   //  return 0;
2486   }
2487 
bg_album_eject(bg_album_t * a)2488 void bg_album_eject(bg_album_t * a)
2489   {
2490   bg_input_plugin_t * plugin;
2491   bg_plugin_handle_t * handle;
2492 
2493   handle = bg_plugin_load(a->com->plugin_reg, a->plugin_info);
2494   plugin = (bg_input_plugin_t*)handle->plugin;
2495 
2496   if(!plugin->eject_disc(a->device))
2497     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Ejecting disc failed");
2498   bg_plugin_unref(handle);
2499   }
2500 
bg_album_selected_to_string(bg_album_t * a)2501 char * bg_album_selected_to_string(bg_album_t * a)
2502   {
2503   char time_string[GAVL_TIME_STRING_LEN];
2504   bg_album_entry_t * entry;
2505   char * ret = NULL;
2506   char * tmp_string;
2507   int index = 1;
2508   entry = a->entries;
2509   while(entry)
2510     {
2511     if(entry->flags & BG_ALBUM_ENTRY_SELECTED)
2512       {
2513       if(ret)
2514         ret = bg_strcat(ret, "\n");
2515       gavl_time_prettyprint(entry->duration, time_string);
2516       tmp_string = bg_sprintf("%d.\t%s\t%s", index, entry->name, time_string);
2517       ret = bg_strcat(ret, tmp_string);
2518       free(tmp_string);
2519       }
2520     entry = entry->next;
2521     index++;
2522     }
2523   return ret;
2524   }
2525 
bg_album_select_entry(bg_album_t * a,int entry)2526 void bg_album_select_entry(bg_album_t * a, int entry)
2527   {
2528   bg_album_entry_t * e;
2529   e = bg_album_get_entry(a, entry);
2530   e->flags |= BG_ALBUM_ENTRY_SELECTED;
2531   }
2532 
bg_album_unselect_entry(bg_album_t * a,int entry)2533 void bg_album_unselect_entry(bg_album_t * a, int entry)
2534   {
2535   bg_album_entry_t * e;
2536   e = bg_album_get_entry(a, entry);
2537   e->flags &= ~BG_ALBUM_ENTRY_SELECTED;
2538   }
2539 
bg_album_unselect_all(bg_album_t * a)2540 void bg_album_unselect_all(bg_album_t * a)
2541   {
2542   bg_album_entry_t * e;
2543   e = a->entries;
2544   while(e)
2545     {
2546     e->flags &= ~BG_ALBUM_ENTRY_SELECTED;
2547     e = e->next;
2548     }
2549   }
2550 
bg_album_num_selected(bg_album_t * a)2551 int bg_album_num_selected(bg_album_t * a)
2552   {
2553   bg_album_entry_t * e;
2554   int ret = 0;
2555   e = a->entries;
2556   while(e)
2557     {
2558     if(e->flags & BG_ALBUM_ENTRY_SELECTED)
2559       ret++;
2560     e = e->next;
2561     }
2562   return ret;
2563   }
2564 
bg_album_num_unsync(bg_album_t * a)2565 int bg_album_num_unsync(bg_album_t * a)
2566   {
2567   bg_album_entry_t * e;
2568   int ret = 0;
2569   e = a->entries;
2570   while(e)
2571     {
2572     if(!(e->flags & BG_ALBUM_ENTRY_SYNC))
2573       ret++;
2574     e = e->next;
2575     }
2576   return ret;
2577   }
2578 
bg_album_entry_is_selected(bg_album_t * a,int entry)2579 int bg_album_entry_is_selected(bg_album_t * a, int entry)
2580   {
2581   bg_album_entry_t * e;
2582   e = bg_album_get_entry(a, entry);
2583   return !!(e->flags & BG_ALBUM_ENTRY_SELECTED);
2584   }
2585 
2586 
bg_album_toggle_select_entry(bg_album_t * a,int entry)2587 void bg_album_toggle_select_entry(bg_album_t * a, int entry)
2588   {
2589   bg_album_entry_t * e;
2590   e = bg_album_get_entry(a, entry);
2591   if(e->flags & BG_ALBUM_ENTRY_SELECTED)
2592     e->flags &= ~BG_ALBUM_ENTRY_SELECTED;
2593   else
2594     e->flags |= BG_ALBUM_ENTRY_SELECTED;
2595   }
2596 
bg_album_select_entries(bg_album_t * a,int start,int end)2597 void bg_album_select_entries(bg_album_t * a, int start, int end)
2598   {
2599   int i;
2600   int swp;
2601   bg_album_entry_t * e;
2602 
2603   if(end < start)
2604     {
2605     swp = end;
2606     end = start;
2607     start = swp;
2608     }
2609 
2610   e = bg_album_get_entry(a, start);
2611   for(i = start; i <= end; i++)
2612     {
2613     if(!e)
2614       {
2615       bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Invalid selection range given");
2616       return;
2617       }
2618     e->flags |= BG_ALBUM_ENTRY_SELECTED;
2619     e = e->next;
2620     }
2621   }
2622 
2623 /*********************************
2624  * Seek support
2625  *********************************/
2626 
2627 struct bg_album_seek_data_s
2628   {
2629   char * str;
2630   int ignore;
2631   int exact;
2632 
2633   int changed;
2634 
2635   struct
2636     {
2637     wchar_t * str;
2638     int len;
2639     int matched;
2640     } * substrings;
2641   int num_substrings;
2642   int substrings_alloc;
2643 
2644   int (*match_func)(const wchar_t *s1, const wchar_t *s2, size_t n);
2645 
2646   bg_charset_converter_t * cnv;
2647   };
2648 
bg_album_seek_data_create()2649 bg_album_seek_data_t * bg_album_seek_data_create()
2650   {
2651   bg_album_seek_data_t * ret;
2652   ret = calloc(1, sizeof(*ret));
2653   ret->cnv = bg_charset_converter_create("UTF-8", "WCHAR_T");
2654   return ret;
2655   }
2656 
bg_album_seek_data_set_string(bg_album_seek_data_t * d,const char * str)2657 void bg_album_seek_data_set_string(bg_album_seek_data_t * d, const char * str)
2658   {
2659   if(d->str && !strcmp(d->str, str))
2660     return;
2661   d->str = bg_strdup(d->str, str);
2662   d->changed = 1;
2663   }
2664 
bg_album_seek_data_ignore_case(bg_album_seek_data_t * d,int ignore)2665 void bg_album_seek_data_ignore_case(bg_album_seek_data_t * d, int ignore)
2666   {
2667   if(d->ignore == ignore)
2668     return;
2669   d->ignore = ignore;
2670   d->changed = 1;
2671   }
2672 
bg_album_seek_data_exact_string(bg_album_seek_data_t * d,int exact)2673 void bg_album_seek_data_exact_string(bg_album_seek_data_t * d, int exact)
2674   {
2675   if(d->exact == exact)
2676     return;
2677   d->exact = exact;
2678   d->changed = 1;
2679   }
2680 
match_string_ignore(const wchar_t * s1,const wchar_t * s2,size_t n)2681 static int match_string_ignore(const wchar_t *s1, const wchar_t *s2, size_t n)
2682   {
2683   int i;
2684   for(i = 0; i < n; i++)
2685     {
2686     if(!(*s1) || !(*s2) || (towlower(*s1) != towlower(*s2)))
2687       return 0;
2688     s1++;
2689     s2++;
2690     }
2691   return 1;
2692   }
2693 
match_string(const wchar_t * s1,const wchar_t * s2,size_t n)2694 static int match_string(const wchar_t *s1, const wchar_t *s2, size_t n)
2695   {
2696   int i;
2697   for(i = 0; i < n; i++)
2698     {
2699     if(!(*s1) || !(*s2) || (*s1 != *s2))
2700       return 0;
2701     s1++;
2702     s2++;
2703     }
2704   return 1;
2705   }
2706 
update_seek_data(bg_album_seek_data_t * d)2707 static void update_seek_data(bg_album_seek_data_t * d)
2708   {
2709   int i;
2710   char ** substrings;
2711   char ** substrings_d = NULL;
2712 
2713   char * substrings_s[2];
2714 
2715 
2716   if(d->exact)
2717     {
2718     d->num_substrings = 1;
2719     substrings_s[0] = d->str;
2720     substrings_s[1] = NULL;
2721     substrings = substrings_s;
2722     }
2723   else
2724     {
2725     substrings_d = bg_strbreak(d->str, ' ');
2726     d->num_substrings = 0;
2727     while(substrings_d[d->num_substrings])
2728       d->num_substrings++;
2729     substrings = substrings_d;
2730     }
2731 
2732   if(d->num_substrings > d->substrings_alloc)
2733     {
2734     d->substrings = realloc(d->substrings,
2735                             d->num_substrings * sizeof(*d->substrings));
2736     memset(d->substrings + d->substrings_alloc,
2737            0, sizeof(*d->substrings) * (d->num_substrings - d->substrings_alloc));
2738     d->substrings_alloc = d->num_substrings;
2739     }
2740 
2741   for(i = 0; i < d->num_substrings; i++)
2742     {
2743     if(d->substrings[i].str)
2744       free(d->substrings[i].str);
2745     d->substrings[i].str =
2746       (wchar_t*)bg_convert_string(d->cnv,
2747                                   substrings[i], -1,
2748                                   &d->substrings[i].len);
2749     d->substrings[i].len /= sizeof(wchar_t);
2750     }
2751 
2752   if(d->ignore)
2753     d->match_func = match_string_ignore;
2754   else
2755     d->match_func = match_string;
2756 
2757   if(substrings_d)
2758     bg_strbreak_free(substrings_d);
2759   d->changed = 0;
2760   }
2761 
entry_matches(bg_album_entry_t * entry,bg_album_seek_data_t * d)2762 static int entry_matches(bg_album_entry_t * entry, bg_album_seek_data_t * d)
2763   {
2764   int i, j, keep_going;
2765   wchar_t * ptr;
2766   if(d->changed)
2767     update_seek_data(d);
2768 
2769   if(!entry->name_w)
2770     {
2771     entry->name_w = (wchar_t*)bg_convert_string(d->cnv,
2772                                                 entry->name, -1,
2773                                                 &entry->len_w);
2774     entry->len_w /= sizeof(wchar_t);
2775     }
2776 
2777   ptr = entry->name_w;
2778 
2779   for(j = 0; j < d->num_substrings; j++)
2780     d->substrings[j].matched = 0;
2781 
2782   keep_going = 1;
2783 
2784   for(i = 0; i < entry->len_w-1; i++)
2785     {
2786     keep_going = 0;
2787     for(j = 0; j < d->num_substrings; j++)
2788       {
2789       if(!d->substrings[j].matched)
2790         d->substrings[j].matched = d->match_func(d->substrings[j].str,
2791                                                  ptr, d->substrings[j].len);
2792 
2793       if(!d->substrings[j].matched)
2794         keep_going = 1;
2795       }
2796     if(!keep_going)
2797       break;
2798     ptr++;
2799     }
2800   if(keep_going)
2801     return 0;
2802   return 1;
2803   }
2804 
2805 
bg_album_seek_entry_after(bg_album_t * a,bg_album_entry_t * e,bg_album_seek_data_t * d)2806 bg_album_entry_t * bg_album_seek_entry_after(bg_album_t * a,
2807                                              bg_album_entry_t * e,
2808                                              bg_album_seek_data_t * d)
2809   {
2810   if(!e)
2811     e = a->entries;
2812   else
2813     e = e->next;
2814 
2815   while(e)
2816     {
2817     if(entry_matches(e, d))
2818       return e;
2819     e = e->next;
2820     }
2821   return e;
2822   }
2823 
bg_album_seek_entry_before(bg_album_t * a,bg_album_entry_t * e,bg_album_seek_data_t * d)2824 bg_album_entry_t * bg_album_seek_entry_before(bg_album_t * a,
2825                                               bg_album_entry_t * e,
2826                                               bg_album_seek_data_t * d)
2827   {
2828   bg_album_entry_t * cur;
2829 
2830   bg_album_entry_t * match = NULL;
2831 
2832   if(!e)
2833     {
2834     e = a->entries;
2835     while(e->next)
2836       e = e->next;
2837     if(entry_matches(e, d))
2838       return e;
2839     }
2840 
2841   cur = a->entries;
2842   while(1)
2843     {
2844     if(cur == e)
2845       return match;
2846 
2847     if(entry_matches(cur, d))
2848       {
2849       if(cur->next == e)
2850         return cur;
2851       match = cur;
2852       }
2853     cur = cur->next;
2854     if(!cur)
2855       break;
2856     }
2857   return NULL;
2858   }
2859 
bg_album_seek_data_destroy(bg_album_seek_data_t * d)2860 void bg_album_seek_data_destroy(bg_album_seek_data_t * d)
2861   {
2862   int i;
2863   bg_charset_converter_destroy(d->cnv);
2864 
2865   if(d->str)
2866     free(d->str);
2867 
2868   for(i = 0; i < d->substrings_alloc; i++)
2869     {
2870     if(d->substrings[i].str)
2871       free(d->substrings[i].str);
2872     }
2873   if(d->substrings)
2874     free(d->substrings);
2875 
2876   free(d);
2877   }
2878 
bg_album_seek_data_changed(bg_album_seek_data_t * d)2879 int bg_album_seek_data_changed(bg_album_seek_data_t * d)
2880   {
2881   return d->changed;
2882   }
2883 
bg_album_entry_copy(bg_album_t * a,bg_album_entry_t * e)2884 bg_album_entry_t * bg_album_entry_copy(bg_album_t * a, bg_album_entry_t * e)
2885   {
2886   bg_album_entry_t * ret;
2887   /* Also sets unique ID */
2888   ret = bg_album_entry_create();
2889 
2890   ret->name = bg_strdup(ret->name, e->name);
2891   ret->location = bg_strdup(ret->location, e->location);
2892   ret->plugin = bg_strdup(ret->plugin, e->plugin);
2893   ret->duration = e->duration;
2894 
2895   ret->num_audio_streams = e->num_audio_streams;
2896   ret->num_still_streams = e->num_still_streams;
2897   ret->num_video_streams = e->num_video_streams;
2898   ret->num_subtitle_streams = e->num_subtitle_streams;
2899 
2900   /*
2901    *  Track index for multi track files/plugins
2902    */
2903 
2904   ret->index = e->index;
2905   ret->total_tracks = e->total_tracks;
2906 
2907   /* Authentication data */
2908 
2909   ret->username = bg_strdup(ret->username, e->username);
2910   ret->password = bg_strdup(ret->password, e->password);
2911 
2912   ret->flags = e->flags;
2913   /* Clear selected bit */
2914   ret->flags &= ~(BG_ALBUM_ENTRY_SELECTED);
2915 
2916   /* The wchar stuff will be rebuilt on demand */
2917 
2918   return ret;
2919   }
2920 
bg_album_set_watch_dir(bg_album_t * a,const char * dir)2921 void bg_album_set_watch_dir(bg_album_t * a,
2922                             const char * dir)
2923   {
2924   char * pos;
2925   a->watch_dir = bg_strdup(a->watch_dir, dir);
2926   pos = a->watch_dir + strlen(a->watch_dir) - 1;
2927   if(*pos == '\n')
2928     *pos = '\0';
2929   }
2930