1 /*
2 * Copyright 2008-2013 Various Authors
3 * Copyright 2004-2006 Timo Hirvonen
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "lib.h"
20 #include "editable.h"
21 #include "track_info.h"
22 #include "options.h"
23 #include "xmalloc.h"
24 #include "rbtree.h"
25 #include "debug.h"
26 #include "utils.h"
27 #include "ui_curses.h" /* cur_view */
28
29 #include <pthread.h>
30 #include <string.h>
31
32 struct editable lib_editable;
33 static struct editable_shared lib_editable_shared;
34
35 struct tree_track *lib_cur_track = NULL;
36 unsigned int play_sorted = 0;
37 enum aaa_mode aaa_mode = AAA_MODE_ALL;
38 /* used in ui_curses.c for status display */
39 char *lib_live_filter = NULL;
40
41 struct rb_root lib_shuffle_root;
42 static struct expr *filter = NULL;
43 static struct expr *add_filter = NULL;
44 static int remove_from_hash = 1;
45
46 static struct expr *live_filter_expr = NULL;
47 static struct track_info *cur_track_ti = NULL;
48 static struct track_info *sel_track_ti = NULL;
49
artist_sort_name(const struct artist * a)50 const char *artist_sort_name(const struct artist *a)
51 {
52 if (a->sort_name)
53 return a->sort_name;
54
55 if (smart_artist_sort && a->auto_sort_name)
56 return a->auto_sort_name;
57
58 return a->name;
59 }
60
sorted_track_to_iter(struct tree_track * track,struct iter * iter)61 static inline void sorted_track_to_iter(struct tree_track *track, struct iter *iter)
62 {
63 iter->data0 = &lib_editable.head;
64 iter->data1 = track;
65 iter->data2 = NULL;
66 }
67
all_wins_changed(void)68 static void all_wins_changed(void)
69 {
70 lib_tree_win->changed = 1;
71 lib_track_win->changed = 1;
72 lib_editable.shared->win->changed = 1;
73 }
74
shuffle_add(struct tree_track * track)75 static void shuffle_add(struct tree_track *track)
76 {
77 shuffle_list_add(&track->shuffle_track, &lib_shuffle_root);
78 }
79
views_add_track(struct track_info * ti)80 static void views_add_track(struct track_info *ti)
81 {
82 struct tree_track *track = xnew(struct tree_track, 1);
83
84 /* NOTE: does not ref ti */
85 simple_track_init((struct simple_track *)track, ti);
86
87 /* both the hash table and views have refs */
88 track_info_ref(ti);
89
90 tree_add_track(track);
91 shuffle_add(track);
92 editable_add(&lib_editable, (struct simple_track *)track);
93 }
94
95 struct fh_entry {
96 struct fh_entry *next;
97
98 /* ref count is increased when added to this hash */
99 struct track_info *ti;
100 };
101
102 #define FH_SIZE (1024)
103 static struct fh_entry *ti_hash[FH_SIZE] = { NULL, };
104
hash_insert(struct track_info * ti)105 static int hash_insert(struct track_info *ti)
106 {
107 const char *filename = ti->filename;
108 unsigned int pos = hash_str(filename) % FH_SIZE;
109 struct fh_entry **entryp;
110 struct fh_entry *e;
111
112 entryp = &ti_hash[pos];
113 e = *entryp;
114 while (e) {
115 if (strcmp(e->ti->filename, filename) == 0) {
116 /* found, don't insert */
117 return 0;
118 }
119 e = e->next;
120 }
121
122 e = xnew(struct fh_entry, 1);
123 track_info_ref(ti);
124 e->ti = ti;
125 e->next = *entryp;
126 *entryp = e;
127 return 1;
128 }
129
hash_remove(struct track_info * ti)130 static void hash_remove(struct track_info *ti)
131 {
132 const char *filename = ti->filename;
133 unsigned int pos = hash_str(filename) % FH_SIZE;
134 struct fh_entry **entryp;
135
136 entryp = &ti_hash[pos];
137 while (1) {
138 struct fh_entry *e = *entryp;
139
140 BUG_ON(e == NULL);
141 if (strcmp(e->ti->filename, filename) == 0) {
142 *entryp = e->next;
143 track_info_unref(e->ti);
144 free(e);
145 break;
146 }
147 entryp = &e->next;
148 }
149 }
150
is_filtered(struct track_info * ti)151 static int is_filtered(struct track_info *ti)
152 {
153 if (live_filter_expr && !expr_eval(live_filter_expr, ti))
154 return 1;
155 if (!live_filter_expr && lib_live_filter && !track_info_matches(ti, lib_live_filter, TI_MATCH_ALL))
156 return 1;
157 if (filter && !expr_eval(filter, ti))
158 return 1;
159 return 0;
160 }
161
lib_add_track(struct track_info * ti,void * opaque)162 void lib_add_track(struct track_info *ti, void *opaque)
163 {
164 if (add_filter && !expr_eval(add_filter, ti)) {
165 /* filter any files excluded by lib_add_filter */
166 return;
167 }
168
169 if (!hash_insert(ti)) {
170 /* duplicate files not allowed */
171 return;
172 }
173
174 if (!is_filtered(ti))
175 views_add_track(ti);
176 }
177
album_first_track(const struct album * album)178 static struct tree_track *album_first_track(const struct album *album)
179 {
180 return to_tree_track(rb_first(&album->track_root));
181 }
182
artist_first_track(const struct artist * artist)183 static struct tree_track *artist_first_track(const struct artist *artist)
184 {
185 return album_first_track(to_album(rb_first(&artist->album_root)));
186 }
187
normal_get_first(void)188 static struct tree_track *normal_get_first(void)
189 {
190 return artist_first_track(to_artist(rb_first(&lib_artist_root)));
191 }
192
album_last_track(const struct album * album)193 static struct tree_track *album_last_track(const struct album *album)
194 {
195 return to_tree_track(rb_last(&album->track_root));
196 }
197
artist_last_track(const struct artist * artist)198 static struct tree_track *artist_last_track(const struct artist *artist)
199 {
200 return album_last_track(to_album(rb_last(&artist->album_root)));
201 }
202
aaa_mode_filter(const struct simple_track * track)203 static int aaa_mode_filter(const struct simple_track *track)
204 {
205 const struct album *album = ((struct tree_track *)track)->album;
206
207 if (aaa_mode == AAA_MODE_ALBUM)
208 return CUR_ALBUM == album;
209
210 if (aaa_mode == AAA_MODE_ARTIST)
211 return CUR_ARTIST == album->artist;
212
213 /* AAA_MODE_ALL */
214 return 1;
215 }
216
217 /* set next/prev (tree) {{{ */
218
normal_get_next(void)219 static struct tree_track *normal_get_next(void)
220 {
221 if (lib_cur_track == NULL)
222 return normal_get_first();
223
224 /* not last track of the album? */
225 if (rb_next(&lib_cur_track->tree_node)) {
226 /* next track of the current album */
227 return to_tree_track(rb_next(&lib_cur_track->tree_node));
228 }
229
230 if (aaa_mode == AAA_MODE_ALBUM) {
231 if (!repeat)
232 return NULL;
233 /* first track of the current album */
234 return album_first_track(CUR_ALBUM);
235 }
236
237 /* not last album of the artist? */
238 if (rb_next(&CUR_ALBUM->tree_node) != NULL) {
239 /* first track of the next album */
240 return album_first_track(to_album(rb_next(&CUR_ALBUM->tree_node)));
241 }
242
243 if (aaa_mode == AAA_MODE_ARTIST) {
244 if (!repeat)
245 return NULL;
246 /* first track of the first album of the current artist */
247 return artist_first_track(CUR_ARTIST);
248 }
249
250 /* not last artist of the library? */
251 if (rb_next(&CUR_ARTIST->tree_node) != NULL) {
252 /* first track of the next artist */
253 return artist_first_track(to_artist(rb_next(&CUR_ARTIST->tree_node)));
254 }
255
256 if (!repeat)
257 return NULL;
258
259 /* first track */
260 return normal_get_first();
261 }
262
normal_get_prev(void)263 static struct tree_track *normal_get_prev(void)
264 {
265 if (lib_cur_track == NULL)
266 return normal_get_first();
267
268 /* not first track of the album? */
269 if (rb_prev(&lib_cur_track->tree_node)) {
270 /* prev track of the album */
271 return to_tree_track(rb_prev(&lib_cur_track->tree_node));
272 }
273
274 if (aaa_mode == AAA_MODE_ALBUM) {
275 if (!repeat)
276 return NULL;
277 /* last track of the album */
278 return album_last_track(CUR_ALBUM);
279 }
280
281 /* not first album of the artist? */
282 if (rb_prev(&CUR_ALBUM->tree_node) != NULL) {
283 /* last track of the prev album of the artist */
284 return album_last_track(to_album(rb_prev(&CUR_ALBUM->tree_node)));
285 }
286
287 if (aaa_mode == AAA_MODE_ARTIST) {
288 if (!repeat)
289 return NULL;
290 /* last track of the last album of the artist */
291 return album_last_track(to_album(rb_last(&CUR_ARTIST->album_root)));
292 }
293
294 /* not first artist of the library? */
295 if (rb_prev(&CUR_ARTIST->tree_node) != NULL) {
296 /* last track of the last album of the prev artist */
297 return artist_last_track(to_artist(rb_prev(&CUR_ARTIST->tree_node)));
298 }
299
300 if (!repeat)
301 return NULL;
302
303 /* last track */
304 return artist_last_track(to_artist(rb_last(&lib_artist_root)));
305 }
306
307 /* set next/prev (tree) }}} */
308
lib_reshuffle(void)309 void lib_reshuffle(void)
310 {
311 shuffle_list_reshuffle(&lib_shuffle_root);
312 }
313
free_lib_track(struct editable * e,struct list_head * item)314 static void free_lib_track(struct editable *e, struct list_head *item)
315 {
316 struct tree_track *track = (struct tree_track *)to_simple_track(item);
317 struct track_info *ti = tree_track_info(track);
318
319 if (track == lib_cur_track)
320 lib_cur_track = NULL;
321
322 if (remove_from_hash)
323 hash_remove(ti);
324
325 rb_erase(&track->shuffle_track.tree_node, &lib_shuffle_root);
326 tree_remove(track);
327
328 track_info_unref(ti);
329 free(track);
330 }
331
lib_init(void)332 void lib_init(void)
333 {
334 editable_shared_init(&lib_editable_shared, free_lib_track);
335 editable_init(&lib_editable, &lib_editable_shared, 1);
336 tree_init();
337 srand(time(NULL));
338 }
339
lib_set_track(struct tree_track * track)340 struct track_info *lib_set_track(struct tree_track *track)
341 {
342 struct track_info *ti = NULL;
343
344 if (track) {
345 lib_cur_track = track;
346 ti = tree_track_info(track);
347 track_info_ref(ti);
348 if (follow) {
349 tree_sel_current(auto_expand_albums_follow);
350 sorted_sel_current();
351 }
352 all_wins_changed();
353 }
354 return ti;
355 }
356
lib_goto_next(void)357 struct track_info *lib_goto_next(void)
358 {
359 struct tree_track *track;
360
361 if (rb_root_empty(&lib_artist_root)) {
362 BUG_ON(lib_cur_track != NULL);
363 return NULL;
364 }
365 if (shuffle) {
366 track = (struct tree_track *)shuffle_list_get_next(&lib_shuffle_root,
367 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
368 } else if (play_sorted) {
369 track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
370 (struct simple_track *)lib_cur_track, aaa_mode_filter);
371 } else {
372 track = normal_get_next();
373 }
374 return lib_set_track(track);
375 }
376
lib_goto_prev(void)377 struct track_info *lib_goto_prev(void)
378 {
379 struct tree_track *track;
380
381 if (rb_root_empty(&lib_artist_root)) {
382 BUG_ON(lib_cur_track != NULL);
383 return NULL;
384 }
385 if (shuffle) {
386 track = (struct tree_track *)shuffle_list_get_prev(&lib_shuffle_root,
387 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
388 } else if (play_sorted) {
389 track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
390 (struct simple_track *)lib_cur_track, aaa_mode_filter);
391 } else {
392 track = normal_get_prev();
393 }
394 return lib_set_track(track);
395 }
396
sorted_get_selected(void)397 static struct tree_track *sorted_get_selected(void)
398 {
399 struct iter sel;
400
401 if (list_empty(&lib_editable.head))
402 return NULL;
403
404 window_get_sel(lib_editable.shared->win, &sel);
405 return iter_to_sorted_track(&sel);
406 }
407
sorted_activate_selected(void)408 struct track_info *sorted_activate_selected(void)
409 {
410 return lib_set_track(sorted_get_selected());
411 }
412
hash_add_to_views(void)413 static void hash_add_to_views(void)
414 {
415 int i;
416 for (i = 0; i < FH_SIZE; i++) {
417 struct fh_entry *e;
418
419 e = ti_hash[i];
420 while (e) {
421 struct track_info *ti = e->ti;
422
423 if (!is_filtered(ti))
424 views_add_track(ti);
425 e = e->next;
426 }
427 }
428 }
429
lib_find_track(struct track_info * ti)430 struct tree_track *lib_find_track(struct track_info *ti)
431 {
432 struct simple_track *track;
433
434 list_for_each_entry(track, &lib_editable.head, node) {
435 if (strcmp(track->info->filename, ti->filename) == 0) {
436 struct tree_track *tt = (struct tree_track *)track;
437 return tt;
438 }
439 }
440 return NULL;
441 }
442
lib_store_cur_track(struct track_info * ti)443 void lib_store_cur_track(struct track_info *ti)
444 {
445 if (cur_track_ti)
446 track_info_unref(cur_track_ti);
447 cur_track_ti = ti;
448 track_info_ref(cur_track_ti);
449 }
450
lib_get_cur_stored_track(void)451 struct track_info *lib_get_cur_stored_track(void)
452 {
453 if (cur_track_ti && lib_find_track(cur_track_ti))
454 return cur_track_ti;
455 return NULL;
456 }
457
restore_cur_track(struct track_info * ti)458 static void restore_cur_track(struct track_info *ti)
459 {
460 struct tree_track *tt = lib_find_track(ti);
461 if (tt)
462 lib_cur_track = tt;
463 }
464
is_filtered_cb(void * data,struct track_info * ti)465 static int is_filtered_cb(void *data, struct track_info *ti)
466 {
467 return is_filtered(ti);
468 }
469
do_lib_filter(int clear_before)470 static void do_lib_filter(int clear_before)
471 {
472 /* try to save cur_track */
473 if (lib_cur_track)
474 lib_store_cur_track(tree_track_info(lib_cur_track));
475
476 if (clear_before)
477 d_print("filter results could grow, clear tracks and re-add (slow)\n");
478
479 remove_from_hash = 0;
480 if (clear_before) {
481 editable_clear(&lib_editable);
482 hash_add_to_views();
483 } else
484 editable_remove_matching_tracks(&lib_editable, is_filtered_cb, NULL);
485 remove_from_hash = 1;
486
487 window_changed(lib_editable.shared->win);
488 window_goto_top(lib_editable.shared->win);
489 lib_cur_win = lib_tree_win;
490 window_goto_top(lib_tree_win);
491
492 /* restore cur_track */
493 if (cur_track_ti && !lib_cur_track)
494 restore_cur_track(cur_track_ti);
495 }
496
unset_live_filter(void)497 static void unset_live_filter(void)
498 {
499 free(lib_live_filter);
500 lib_live_filter = NULL;
501 free(live_filter_expr);
502 live_filter_expr = NULL;
503 }
504
lib_set_filter(struct expr * expr)505 void lib_set_filter(struct expr *expr)
506 {
507 int clear_before = lib_live_filter || filter;
508 unset_live_filter();
509 if (filter)
510 expr_free(filter);
511 filter = expr;
512 do_lib_filter(clear_before);
513 }
514
lib_set_add_filter(struct expr * expr)515 void lib_set_add_filter(struct expr *expr)
516 {
517 if (add_filter)
518 expr_free(add_filter);
519 add_filter = expr;
520 }
521
get_sel_track(void)522 static struct tree_track *get_sel_track(void)
523 {
524 switch (cur_view) {
525 case TREE_VIEW:
526 return tree_get_selected();
527 case SORTED_VIEW:
528 return sorted_get_selected();
529 }
530 return NULL;
531 }
532
set_sel_track(struct tree_track * tt)533 static void set_sel_track(struct tree_track *tt)
534 {
535 struct iter iter;
536
537 switch (cur_view) {
538 case TREE_VIEW:
539 tree_sel_track(tt, auto_expand_albums_selcur);
540 break;
541 case SORTED_VIEW:
542 sorted_track_to_iter(tt, &iter);
543 window_set_sel(lib_editable.shared->win, &iter);
544 break;
545 }
546 }
547
store_sel_track(void)548 static void store_sel_track(void)
549 {
550 struct tree_track *tt = get_sel_track();
551 if (tt) {
552 sel_track_ti = tree_track_info(tt);
553 track_info_ref(sel_track_ti);
554 }
555 }
556
restore_sel_track(void)557 static void restore_sel_track(void)
558 {
559 if (sel_track_ti) {
560 struct tree_track *tt = lib_find_track(sel_track_ti);
561 if (tt) {
562 set_sel_track(tt);
563 track_info_unref(sel_track_ti);
564 sel_track_ti = NULL;
565 }
566 }
567 }
568
569 /* determine if filter results could grow, in which case all tracks must be cleared and re-added */
do_clear_before(const char * str,struct expr * expr)570 static int do_clear_before(const char *str, struct expr *expr)
571 {
572 if (!lib_live_filter)
573 return 0;
574 if (!str)
575 return 1;
576 if ((!expr && live_filter_expr) || (expr && !live_filter_expr))
577 return 1;
578 if (!expr || expr_is_harmless(expr))
579 return !strstr(str, lib_live_filter);
580 return 1;
581 }
582
lib_set_live_filter(const char * str)583 void lib_set_live_filter(const char *str)
584 {
585 int clear_before;
586 struct expr *expr = NULL;
587
588 if (strcmp0(str, lib_live_filter) == 0)
589 return;
590
591 if (str && expr_is_short(str)) {
592 expr = expr_parse(str);
593 if (!expr)
594 return;
595 }
596
597 clear_before = do_clear_before(str, expr);
598
599 if (!str)
600 store_sel_track();
601
602 unset_live_filter();
603 lib_live_filter = str ? xstrdup(str) : NULL;
604 live_filter_expr = expr;
605 do_lib_filter(clear_before);
606
607 if (expr) {
608 unsigned int match_type = expr_get_match_type(expr);
609 if (match_type & TI_MATCH_ALBUM)
610 tree_expand_all();
611 if (match_type & TI_MATCH_TITLE)
612 tree_sel_first();
613 } else if (str)
614 tree_expand_matching(str);
615
616 if (!str)
617 restore_sel_track();
618 }
619
lib_remove(struct track_info * ti)620 int lib_remove(struct track_info *ti)
621 {
622 struct simple_track *track;
623
624 list_for_each_entry(track, &lib_editable.head, node) {
625 if (track->info == ti) {
626 editable_remove_track(&lib_editable, track);
627 return 1;
628 }
629 }
630 return 0;
631 }
632
lib_clear_store(void)633 void lib_clear_store(void)
634 {
635 int i;
636
637 for (i = 0; i < FH_SIZE; i++) {
638 struct fh_entry *e, *next;
639
640 e = ti_hash[i];
641 while (e) {
642 next = e->next;
643 track_info_unref(e->ti);
644 free(e);
645 e = next;
646 }
647 ti_hash[i] = NULL;
648 }
649 }
650
sorted_sel_current(void)651 void sorted_sel_current(void)
652 {
653 if (lib_cur_track) {
654 struct iter iter;
655
656 sorted_track_to_iter(lib_cur_track, &iter);
657 window_set_sel(lib_editable.shared->win, &iter);
658 }
659 }
660
ti_cmp(const void * a,const void * b)661 static int ti_cmp(const void *a, const void *b)
662 {
663 const struct track_info *ai = *(const struct track_info **)a;
664 const struct track_info *bi = *(const struct track_info **)b;
665
666 return track_info_cmp(ai, bi, lib_editable.shared->sort_keys);
667 }
668
do_lib_for_each(int (* cb)(void * data,struct track_info * ti),void * data,int filtered)669 static int do_lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data, int filtered)
670 {
671 int i, rc = 0, count = 0, size = 1024;
672 struct track_info **tis;
673
674 tis = xnew(struct track_info *, size);
675
676 /* collect all track_infos */
677 for (i = 0; i < FH_SIZE; i++) {
678 struct fh_entry *e;
679
680 e = ti_hash[i];
681 while (e) {
682 if (count == size) {
683 size *= 2;
684 tis = xrenew(struct track_info *, tis, size);
685 }
686 if (!filtered || !filter || expr_eval(filter, e->ti))
687 tis[count++] = e->ti;
688 e = e->next;
689 }
690 }
691
692 /* sort to speed up playlist loading */
693 qsort(tis, count, sizeof(struct track_info *), ti_cmp);
694 for (i = 0; i < count; i++) {
695 rc = cb(data, tis[i]);
696 if (rc)
697 break;
698 }
699
700 free(tis);
701 return rc;
702 }
703
lib_for_each(int (* cb)(void * data,struct track_info * ti),void * data,void * opaque)704 int lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data,
705 void *opaque)
706 {
707 return do_lib_for_each(cb, data, 0);
708 }
709
lib_for_each_filtered(int (* cb)(void * data,struct track_info * ti),void * data,void * opaque)710 int lib_for_each_filtered(int (*cb)(void *data, struct track_info *ti),
711 void *data, void *opaque)
712 {
713 return do_lib_for_each(cb, data, 1);
714 }
715