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