Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
playlist-new.c
Go to the documentation of this file.
1 /*
2  * playlist-new.c
3  * Copyright 2009-2012 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #include <glib.h>
27 
28 #include <libaudcore/audstrings.h>
29 #include <libaudcore/hook.h>
30 #include <libaudcore/tuple.h>
31 
32 #include "config.h"
33 #include "i18n.h"
34 #include "misc.h"
35 #include "playback.h"
36 #include "playlist.h"
37 #include "plugins.h"
38 #include "util.h"
39 
41 
42 #define SCAN_THREADS 2
43 #define STATE_FILE "playlist-state"
44 
45 #define ENTER pthread_mutex_lock (& mutex)
46 #define LEAVE pthread_mutex_unlock (& mutex)
47 
48 #define LEAVE_RET_VOID do { \
49  pthread_mutex_unlock (& mutex); \
50  return; \
51 } while (0)
52 
53 #define LEAVE_RET(ret) do { \
54  pthread_mutex_unlock (& mutex); \
55  return ret; \
56 } while (0)
57 
58 #define DECLARE_PLAYLIST \
59  Playlist * playlist
60 
61 #define DECLARE_PLAYLIST_ENTRY \
62  Playlist * playlist; \
63  Entry * entry
64 
65 #define LOOKUP_PLAYLIST do { \
66  if (! (playlist = lookup_playlist (playlist_num))) \
67  LEAVE_RET_VOID; \
68 } while (0)
69 
70 #define LOOKUP_PLAYLIST_RET(ret) do { \
71  if (! (playlist = lookup_playlist (playlist_num))) \
72  LEAVE_RET(ret); \
73 } while (0)
74 
75 #define LOOKUP_PLAYLIST_ENTRY do { \
76  LOOKUP_PLAYLIST; \
77  if (! (entry = lookup_entry (playlist, entry_num))) \
78  LEAVE_RET_VOID; \
79 } while (0)
80 
81 #define LOOKUP_PLAYLIST_ENTRY_RET(ret) do { \
82  LOOKUP_PLAYLIST_RET(ret); \
83  if (! (entry = lookup_entry (playlist, entry_num))) \
84  LEAVE_RET(ret); \
85 } while (0)
86 
87 typedef struct {
88  int level, before, after;
89 } Update;
90 
91 typedef struct {
92  int number;
93  char * filename;
95  Tuple * tuple;
96  char * formatted, * title, * artist, * album;
97  int length;
103  int start, end;
104 } Entry;
105 
106 typedef struct {
107  int number, unique_id;
108  char * filename, * title;
110  Index * entries;
114  GList * queued;
115  int64_t total_length, selected_length;
116  bool_t scanning, scan_ending;
117  Update next_update, last_update;
118  int resume_state, resume_time;
119 } Playlist;
120 
121 static const char * const default_title = N_("New Playlist");
122 static const char * const temp_title = N_("Now Playing");
123 
124 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
125 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
126 
127 /* The unique ID table contains pointers to Playlist for ID's in use and NULL
128  * for "dead" (previously used and therefore unavailable) ID's. */
129 static GHashTable * unique_id_table = NULL;
130 static int next_unique_id = 1000;
131 
132 static Index * playlists = NULL;
135 
136 static int update_source = 0, update_level;
137 
138 typedef struct {
141 } ScanItem;
142 
143 static pthread_t scan_threads[SCAN_THREADS];
146 static GQueue scan_queue = G_QUEUE_INIT;
148 
149 static void * scanner (void * unused);
150 static void scan_trigger (void);
151 
152 static char * title_format;
153 
154 static char * title_from_tuple (Tuple * tuple)
155 {
156  if (! title_format)
157  title_format = get_string (NULL, "generic_title_format");
158 
159  return tuple_format_title (tuple, title_format);
160 }
161 
162 static void entry_set_tuple_real (Entry * entry, Tuple * tuple)
163 {
164  /* Hack: We cannot refresh segmented entries (since their info is read from
165  * the cue sheet when it is first loaded), so leave them alone. -jlindgren */
166  if (entry->segmented && entry->tuple)
167  {
168  if (tuple)
169  tuple_unref (tuple);
170  return;
171  }
172 
173  if (entry->tuple)
174  tuple_unref (entry->tuple);
175  entry->tuple = tuple;
176 
177  str_unref (entry->formatted);
178  str_unref (entry->title);
179  str_unref (entry->artist);
180  str_unref (entry->album);
181 
182  describe_song (entry->filename, tuple, & entry->title, & entry->artist, & entry->album);
183 
184  if (! tuple)
185  {
186  entry->formatted = NULL;
187  entry->length = 0;
188  entry->segmented = FALSE;
189  entry->start = 0;
190  entry->end = -1;
191  }
192  else
193  {
194  entry->formatted = title_from_tuple (tuple);
195  entry->length = tuple_get_int (tuple, FIELD_LENGTH, NULL);
196  if (entry->length < 0)
197  entry->length = 0;
198 
200  {
201  entry->segmented = TRUE;
202  entry->start = tuple_get_int (tuple, FIELD_SEGMENT_START, NULL);
203 
205  entry->end = tuple_get_int (tuple, FIELD_SEGMENT_END, NULL);
206  else
207  entry->end = -1;
208  }
209  else
210  entry->segmented = FALSE;
211  }
212 }
213 
214 static void entry_set_tuple (Playlist * playlist, Entry * entry, Tuple * tuple)
215 {
216  if (entry->tuple)
217  {
218  playlist->total_length -= entry->length;
219  if (entry->selected)
220  playlist->selected_length -= entry->length;
221  }
222 
223  entry_set_tuple_real (entry, tuple);
224 
225  if (tuple)
226  {
227  playlist->total_length += entry->length;
228  if (entry->selected)
229  playlist->selected_length += entry->length;
230  }
231 }
232 
234 {
235  entry_set_tuple (playlist, entry, tuple_new_from_filename (entry->filename));
236  entry->failed = TRUE;
237 }
238 
240 {
241  GList * next;
242  for (GList * node = scan_queue.head; node; node = next)
243  {
244  ScanItem * item = node->data;
245  next = node->next;
246 
247  if (item->entry == entry)
248  {
249  g_queue_delete_link (& scan_queue, node);
250  g_slice_free (ScanItem, item);
251  }
252  }
253 
254  for (int i = 0; i < SCAN_THREADS; i ++)
255  {
256  if (scan_items[i] && scan_items[i]->entry == entry)
257  {
258  g_slice_free (ScanItem, scan_items[i]);
259  scan_items[i] = NULL;
260  }
261  }
262 }
263 
264 static Entry * entry_new (char * filename, Tuple * tuple, PluginHandle * decoder)
265 {
266  Entry * entry = g_slice_new (Entry);
267 
268  entry->filename = filename;
269  entry->decoder = decoder;
270  entry->tuple = NULL;
271  entry->formatted = NULL;
272  entry->title = NULL;
273  entry->artist = NULL;
274  entry->album = NULL;
275  entry->failed = FALSE;
276  entry->number = -1;
277  entry->selected = FALSE;
278  entry->shuffle_num = 0;
279  entry->queued = FALSE;
280  entry->segmented = FALSE;
281  entry->start = 0;
282  entry->end = -1;
283 
284  entry_set_tuple_real (entry, tuple);
285  return entry;
286 }
287 
288 static void entry_free (Entry * entry)
289 {
290  entry_cancel_scan (entry);
291 
292  str_unref (entry->filename);
293  if (entry->tuple)
294  tuple_unref (entry->tuple);
295 
296  str_unref (entry->formatted);
297  str_unref (entry->title);
298  str_unref (entry->artist);
299  str_unref (entry->album);
300  g_slice_free (Entry, entry);
301 }
302 
303 static int new_unique_id (int preferred)
304 {
305  if (preferred >= 0 && ! g_hash_table_lookup_extended (unique_id_table,
306  GINT_TO_POINTER (preferred), NULL, NULL))
307  return preferred;
308 
309  while (g_hash_table_lookup_extended (unique_id_table,
310  GINT_TO_POINTER (next_unique_id), NULL, NULL))
311  next_unique_id ++;
312 
313  return next_unique_id ++;
314 }
315 
316 static Playlist * playlist_new (int id)
317 {
318  Playlist * playlist = g_slice_new (Playlist);
319 
320  playlist->number = -1;
321  playlist->unique_id = new_unique_id (id);
322  playlist->filename = NULL;
323  playlist->title = str_get (_(default_title));
324  playlist->modified = TRUE;
325  playlist->entries = index_new();
326  playlist->position = NULL;
327  playlist->selected_count = 0;
328  playlist->last_shuffle_num = 0;
329  playlist->queued = NULL;
330  playlist->total_length = 0;
331  playlist->selected_length = 0;
332  playlist->scanning = FALSE;
333  playlist->scan_ending = FALSE;
334  playlist->resume_state = RESUME_STOP;
335  playlist->resume_time = 0;
336 
337  memset (& playlist->last_update, 0, sizeof (Update));
338  memset (& playlist->next_update, 0, sizeof (Update));
339 
340  g_hash_table_insert (unique_id_table, GINT_TO_POINTER (playlist->unique_id), playlist);
341  return playlist;
342 }
343 
345 {
346  g_hash_table_insert (unique_id_table, GINT_TO_POINTER (playlist->unique_id), NULL);
347 
348  str_unref (playlist->filename);
349  str_unref (playlist->title);
350 
351  for (int count = 0; count < index_count (playlist->entries); count ++)
352  entry_free (index_get (playlist->entries, count));
353 
354  index_free (playlist->entries);
355  g_list_free (playlist->queued);
356  g_slice_free (Playlist, playlist);
357 }
358 
359 static void number_playlists (int at, int length)
360 {
361  for (int count = 0; count < length; count ++)
362  {
363  Playlist * playlist = index_get (playlists, at + count);
364  playlist->number = at + count;
365  }
366 }
367 
368 static Playlist * lookup_playlist (int playlist_num)
369 {
370  return (playlists && playlist_num >= 0 && playlist_num < index_count
371  (playlists)) ? index_get (playlists, playlist_num) : NULL;
372 }
373 
374 static void number_entries (Playlist * playlist, int at, int length)
375 {
376  for (int count = 0; count < length; count ++)
377  {
378  Entry * entry = index_get (playlist->entries, at + count);
379  entry->number = at + count;
380  }
381 }
382 
383 static Entry * lookup_entry (Playlist * playlist, int entry_num)
384 {
385  return (entry_num >= 0 && entry_num < index_count (playlist->entries)) ?
386  index_get (playlist->entries, entry_num) : NULL;
387 }
388 
389 static bool_t update (void * unused)
390 {
391  ENTER;
392 
393  for (int i = 0; i < index_count (playlists); i ++)
394  {
395  Playlist * p = index_get (playlists, i);
396  memcpy (& p->last_update, & p->next_update, sizeof (Update));
397  memset (& p->next_update, 0, sizeof (Update));
398  }
399 
400  int level = update_level;
401  update_level = 0;
402 
403  if (update_source)
404  {
405  g_source_remove (update_source);
406  update_source = 0;
407  }
408 
409  LEAVE;
410 
411  hook_call ("playlist update", GINT_TO_POINTER (level));
412  return FALSE;
413 }
414 
415 static void queue_update (int level, int list, int at, int count)
416 {
417  Playlist * p = lookup_playlist (list);
418 
419  if (p)
420  {
421  if (level >= PLAYLIST_UPDATE_METADATA)
422  {
423  p->modified = TRUE;
424 
425  if (! get_bool (NULL, "metadata_on_play"))
426  {
427  p->scanning = TRUE;
428  p->scan_ending = FALSE;
429  scan_trigger ();
430  }
431  }
432 
433  if (p->next_update.level)
434  {
435  p->next_update.level = MAX (p->next_update.level, level);
436  p->next_update.before = MIN (p->next_update.before, at);
438  index_count (p->entries) - at - count);
439  }
440  else
441  {
442  p->next_update.level = level;
443  p->next_update.before = at;
444  p->next_update.after = index_count (p->entries) - at - count;
445  }
446  }
447 
448  update_level = MAX (update_level, level);
449 
450  if (! update_source)
451  update_source = g_idle_add_full (G_PRIORITY_HIGH, update, NULL, NULL);
452 }
453 
455 {
456  ENTER;
457  bool_t pending = update_level ? TRUE : FALSE;
458  LEAVE_RET (pending);
459 }
460 
461 int playlist_updated_range (int playlist_num, int * at, int * count)
462 {
463  ENTER;
466 
467  Update * u = & playlist->last_update;
468 
469  int level = u->level;
470  * at = u->before;
471  * count = index_count (playlist->entries) - u->before - u->after;
472 
473  LEAVE_RET (level);
474 }
475 
477 {
478  ENTER;
481 
482  bool_t scanning = playlist->scanning || playlist->scan_ending;
483 
484  LEAVE_RET (scanning);
485 }
486 
488 {
489  for (GList * node = scan_queue.head; node; node = node->next)
490  {
491  ScanItem * item = node->data;
492  if (item->entry == entry)
493  return TRUE;
494  }
495 
496  for (int i = 0; i < SCAN_THREADS; i ++)
497  {
498  if (scan_items[i] && scan_items[i]->entry == entry)
499  return TRUE;
500  }
501 
502  return FALSE;
503 }
504 
506 {
507  if (entry_scan_is_queued (entry))
508  return;
509 
510  ScanItem * item = g_slice_new (ScanItem);
511  item->playlist = playlist;
512  item->entry = entry;
513  g_queue_push_tail (& scan_queue, item);
514 
515  pthread_cond_broadcast (& cond);
516 }
517 
518 static void check_scan_complete (Playlist * p)
519 {
520  if (! p->scan_ending)
521  return;
522 
523  for (GList * node = scan_queue.head; node; node = node->next)
524  {
525  ScanItem * item = node->data;
526  if (item->playlist == p)
527  return;
528  }
529 
530  for (int i = 0; i < SCAN_THREADS; i ++)
531  {
532  if (scan_items[i] && scan_items[i]->playlist == p)
533  return;
534  }
535 
536  p->scan_ending = FALSE;
537 
538  event_queue_cancel ("playlist scan complete", NULL);
539  event_queue ("playlist scan complete", NULL);
540 }
541 
543 {
544  ScanItem * item = g_queue_pop_head (& scan_queue);
545  if (item)
546  return item;
547 
549  {
551 
552  if (playlist->scanning)
553  {
554  while (scan_row < index_count (playlist->entries))
555  {
556  Entry * entry = index_get (playlist->entries, scan_row);
557 
558  if (! entry->tuple && ! entry_scan_is_queued (entry))
559  {
560  item = g_slice_new (ScanItem);
561  item->playlist = playlist;
562  item->entry = entry;
563  return item;
564  }
565 
566  scan_row ++;
567  }
568 
569  playlist->scanning = FALSE;
570  playlist->scan_ending = TRUE;
571  check_scan_complete (playlist);
572  }
573 
574  scan_playlist ++;
575  scan_row = 0;
576  }
577 
578  return NULL;
579 }
580 
581 static void * scanner (void * data)
582 {
583  ENTER;
584 
585  int i = GPOINTER_TO_INT (data);
586 
587  while (! scan_quit)
588  {
589  if (! scan_items[i])
590  scan_items[i] = entry_find_to_scan ();
591 
592  if (! scan_items[i])
593  {
594  pthread_cond_wait (& cond, & mutex);
595  continue;
596  }
597 
598  Playlist * playlist = scan_items[i]->playlist;
599  Entry * entry = scan_items[i]->entry;
600  char * filename = str_ref (entry->filename);
601  PluginHandle * decoder = entry->decoder;
602  bool_t need_tuple = entry->tuple ? FALSE : TRUE;
603 
604  LEAVE;
605 
606  if (! decoder)
607  decoder = file_find_decoder (filename, FALSE);
608 
609  Tuple * tuple = (need_tuple && decoder) ? file_read_tuple (filename, decoder) : NULL;
610 
611  ENTER;
612 
613  str_unref (filename);
614 
615  if (! scan_items[i]) /* scan canceled */
616  {
617  if (tuple)
618  tuple_unref (tuple);
619  continue;
620  }
621 
622  entry->decoder = decoder;
623 
624  if (tuple)
625  {
626  entry_set_tuple (playlist, entry, tuple);
627  queue_update (PLAYLIST_UPDATE_METADATA, playlist->number, entry->number, 1);
628  }
629  else if (need_tuple || ! decoder)
630  {
631  entry_set_failed (playlist, entry);
632  queue_update (PLAYLIST_UPDATE_METADATA, playlist->number, entry->number, 1);
633  }
634 
635  g_slice_free (ScanItem, scan_items[i]);
636  scan_items[i] = NULL;
637 
638  pthread_cond_broadcast (& cond);
639  check_scan_complete (playlist);
640  }
641 
642  LEAVE_RET (NULL);
643 }
644 
645 static void scan_trigger (void)
646 {
647  scan_playlist = 0;
648  scan_row = 0;
649  pthread_cond_broadcast (& cond);
650 }
651 
652 /* mutex may be unlocked during the call */
653 static Entry * get_entry (int playlist_num, int entry_num,
654  bool_t need_decoder, bool_t need_tuple)
655 {
656  while (1)
657  {
658  Playlist * playlist = lookup_playlist (playlist_num);
659  Entry * entry = playlist ? lookup_entry (playlist, entry_num) : NULL;
660 
661  if (! entry || entry->failed)
662  return entry;
663 
664  if ((need_decoder && ! entry->decoder) || (need_tuple && ! entry->tuple))
665  {
666  entry_queue_scan (playlist, entry);
667  pthread_cond_wait (& cond, & mutex);
668  continue;
669  }
670 
671  return entry;
672  }
673 }
674 
675 /* mutex may be unlocked during the call */
676 static Entry * get_playback_entry (bool_t need_decoder, bool_t need_tuple)
677 {
678  while (1)
679  {
680  Entry * entry = playing_playlist ? playing_playlist->position : NULL;
681 
682  if (! entry || entry->failed)
683  return entry;
684 
685  if ((need_decoder && ! entry->decoder) || (need_tuple && ! entry->tuple))
686  {
687  entry_queue_scan (playing_playlist, entry);
688  pthread_cond_wait (& cond, & mutex);
689  continue;
690  }
691 
692  return entry;
693  }
694 }
695 
696 static void get_resume_state (int * state, int * time)
697 {
698  if (playback_get_playing ())
699  {
701  * time = playback_get_time ();
702  }
703  else
704  {
705  * state = RESUME_STOP;
706  * time = 0;
707  }
708 }
709 
710 static void resume (int state, int time)
711 {
712  if (state == RESUME_PLAY || state == RESUME_PAUSE)
713  playback_play (time, state == RESUME_PAUSE);
714 }
715 
716 void playlist_init (void)
717 {
718  srand (time (NULL));
719 
720  ENTER;
721 
722  unique_id_table = g_hash_table_new (g_direct_hash, g_direct_equal);
723  playlists = index_new ();
724 
725  update_level = 0;
726 
727  scan_quit = FALSE;
728  scan_playlist = scan_row = 0;
729 
730  for (int i = 0; i < SCAN_THREADS; i ++)
731  pthread_create (& scan_threads[i], NULL, scanner, GINT_TO_POINTER (i));
732 
733  LEAVE;
734 }
735 
736 void playlist_end (void)
737 {
738  ENTER;
739 
740  scan_quit = TRUE;
741  pthread_cond_broadcast (& cond);
742 
743  LEAVE;
744 
745  for (int i = 0; i < SCAN_THREADS; i ++)
746  pthread_join (scan_threads[i], NULL);
747 
748  ENTER;
749 
750  if (update_source)
751  {
752  g_source_remove (update_source);
753  update_source = 0;
754  }
755 
756  active_playlist = playing_playlist = NULL;
757 
758  for (int i = 0; i < index_count (playlists); i ++)
760 
762  playlists = NULL;
763 
764  g_hash_table_destroy (unique_id_table);
766 
767  g_free (title_format);
768  title_format = NULL;
769 
770  LEAVE;
771 }
772 
773 int playlist_count (void)
774 {
775  ENTER;
776  int count = index_count (playlists);
777  LEAVE_RET (count);
778 }
779 
780 void playlist_insert_with_id (int at, int id)
781 {
782  ENTER;
783 
784  if (at < 0 || at > index_count (playlists))
785  at = index_count (playlists);
786 
787  index_insert (playlists, at, playlist_new (id));
789 
791  LEAVE;
792 }
793 
794 void playlist_insert (int at)
795 {
796  playlist_insert_with_id (at, -1);
797 }
798 
799 void playlist_reorder (int from, int to, int count)
800 {
801  ENTER;
802  if (from < 0 || from + count > index_count (playlists) || to < 0 || to +
803  count > index_count (playlists) || count < 0)
805 
806  Index * displaced = index_new ();
807 
808  if (to < from)
809  index_copy_append (playlists, to, displaced, from - to);
810  else
811  index_copy_append (playlists, from + count, displaced, to - from);
812 
813  index_move (playlists, from, to, count);
814 
815  if (to < from)
816  {
817  index_copy_set (displaced, 0, playlists, to + count, from - to);
818  number_playlists (to, from + count - to);
819  }
820  else
821  {
822  index_copy_set (displaced, 0, playlists, from, to - from);
823  number_playlists (from, to + count - from);
824  }
825 
826  index_free (displaced);
827 
829  LEAVE;
830 }
831 
832 void playlist_delete (int playlist_num)
833 {
834  /* stop playback before locking playlists */
835  if (playback_get_playing () && playlist_num == playlist_get_playing ())
836  playback_stop ();
837 
838  ENTER;
841 
842  index_delete (playlists, playlist_num, 1);
844 
845  if (! index_count (playlists))
847 
848  number_playlists (playlist_num, index_count (playlists) - playlist_num);
849 
850  if (playlist == active_playlist)
851  active_playlist = index_get (playlists, MIN (playlist_num, index_count (playlists) - 1));
852  if (playlist == playing_playlist)
853  playing_playlist = NULL;
854 
856  LEAVE;
857 }
858 
859 int playlist_get_unique_id (int playlist_num)
860 {
861  ENTER;
863  LOOKUP_PLAYLIST_RET (-1);
864 
865  int unique_id = playlist->unique_id;
866 
867  LEAVE_RET (unique_id);
868 }
869 
871 {
872  ENTER;
873 
874  Playlist * p = g_hash_table_lookup (unique_id_table, GINT_TO_POINTER (id));
875  int num = p ? p->number : -1;
876 
877  LEAVE_RET (num);
878 }
879 
880 void playlist_set_filename (int playlist_num, const char * filename)
881 {
882  ENTER;
885 
886  str_unref (playlist->filename);
887  playlist->filename = str_get (filename);
888  playlist->modified = TRUE;
889 
891  LEAVE;
892 }
893 
894 char * playlist_get_filename (int playlist_num)
895 {
896  ENTER;
899 
900  char * filename = str_ref (playlist->filename);
901 
902  LEAVE_RET (filename);
903 }
904 
905 void playlist_set_title (int playlist_num, const char * title)
906 {
907  ENTER;
910 
911  str_unref (playlist->title);
912  playlist->title = str_get (title);
913  playlist->modified = TRUE;
914 
916  LEAVE;
917 }
918 
919 char * playlist_get_title (int playlist_num)
920 {
921  ENTER;
924 
925  char * title = str_ref (playlist->title);
926 
927  LEAVE_RET (title);
928 }
929 
930 void playlist_set_modified (int playlist_num, bool_t modified)
931 {
932  ENTER;
935 
936  playlist->modified = modified;
937 
938  LEAVE;
939 }
940 
941 bool_t playlist_get_modified (int playlist_num)
942 {
943  ENTER;
946 
947  bool_t modified = playlist->modified;
948 
949  LEAVE_RET (modified);
950 }
951 
952 void playlist_set_active (int playlist_num)
953 {
954  ENTER;
957 
958  bool_t changed = FALSE;
959 
960  if (playlist != active_playlist)
961  {
962  changed = TRUE;
963  active_playlist = playlist;
964  }
965 
966  LEAVE;
967 
968  if (changed)
969  hook_call ("playlist activate", NULL);
970 }
971 
973 {
974  ENTER;
975  int list = active_playlist ? active_playlist->number : -1;
976  LEAVE_RET (list);
977 }
978 
979 void playlist_set_playing (int playlist_num)
980 {
981  if (playlist_num == playlist_get_playing ())
982  return;
983 
984  /* get playback state and stop playback before locking playlists */
985  int state, time;
986  get_resume_state (& state, & time);
987 
988  if (state != RESUME_STOP)
989  playback_stop ();
990 
991  ENTER;
992 
993  if (playing_playlist)
994  {
995  playing_playlist->resume_state = state;
996  playing_playlist->resume_time = time;
997  }
998 
1000 
1001  if (playlist_num < 0)
1002  playlist = NULL;
1003  else
1005 
1006  playing_playlist = playlist;
1007 
1008  if (playlist)
1009  {
1010  state = playlist->resume_state;
1011  time = playlist->resume_time;
1012  }
1013  else
1014  {
1015  state = RESUME_STOP;
1016  time = 0;
1017  }
1018 
1019  LEAVE;
1020 
1021  hook_call ("playlist set playing", NULL);
1022 
1023  /* resume playback after unlocking playlists */
1024  resume (state, time);
1025 }
1026 
1028 {
1029  ENTER;
1030  int list = playing_playlist ? playing_playlist->number: -1;
1031  LEAVE_RET (list);
1032 }
1033 
1035 {
1036  int list = playlist_get_active ();
1037  char * title = playlist_get_title (list);
1038 
1039  if (strcmp (title, _(default_title)) || playlist_entry_count (list) > 0)
1040  {
1041  list = playlist_count ();
1042  playlist_insert (list);
1043  }
1044 
1045  str_unref (title);
1046  return list;
1047 }
1048 
1050 {
1051  int list, count = playlist_count ();
1052  bool_t found = FALSE;
1053 
1054  for (list = 0; list < count; list ++)
1055  {
1056  char * title = playlist_get_title (list);
1057  found = ! strcmp (title, _(temp_title));
1058  str_unref (title);
1059 
1060  if (found)
1061  break;
1062  }
1063 
1064  if (! found)
1065  {
1066  list = playlist_get_blank ();
1067  playlist_set_title (list, _(temp_title));
1068  }
1069 
1070  return list;
1071 }
1072 
1073 static void set_position (Playlist * playlist, Entry * entry, bool_t update_shuffle)
1074 {
1075  playlist->position = entry;
1076  playlist->resume_state = RESUME_STOP;
1077  playlist->resume_time = 0;
1078 
1079  /* move entry to top of shuffle list */
1080  if (entry && update_shuffle)
1081  entry->shuffle_num = ++ playlist->last_shuffle_num;
1082 }
1083 
1084 int playlist_entry_count (int playlist_num)
1085 {
1086  ENTER;
1088  LOOKUP_PLAYLIST_RET (0);
1089 
1090  int count = index_count (playlist->entries);
1091 
1092  LEAVE_RET (count);
1093 }
1094 
1095 void playlist_entry_insert_batch_raw (int playlist_num, int at,
1096  Index * filenames, Index * tuples, Index * decoders)
1097 {
1098  ENTER;
1101 
1102  int entries = index_count (playlist->entries);
1103 
1104  if (at < 0 || at > entries)
1105  at = entries;
1106 
1107  int number = index_count (filenames);
1108 
1109  Index * add = index_new ();
1110  index_allocate (add, number);
1111 
1112  for (int i = 0; i < number; i ++)
1113  {
1114  char * filename = index_get (filenames, i);
1115  Tuple * tuple = tuples ? index_get (tuples, i) : NULL;
1116  PluginHandle * decoder = decoders ? index_get (decoders, i) : NULL;
1117  index_append (add, entry_new (filename, tuple, decoder));
1118  }
1119 
1120  index_free (filenames);
1121  if (decoders)
1122  index_free (decoders);
1123  if (tuples)
1124  index_free (tuples);
1125 
1126  number = index_count (add);
1127  index_merge_insert (playlist->entries, at, add);
1128  index_free (add);
1129 
1130  number_entries (playlist, at, entries + number - at);
1131 
1132  for (int count = 0; count < number; count ++)
1133  {
1134  Entry * entry = index_get (playlist->entries, at + count);
1135  playlist->total_length += entry->length;
1136  }
1137 
1138  queue_update (PLAYLIST_UPDATE_STRUCTURE, playlist->number, at, number);
1139  LEAVE;
1140 }
1141 
1142 void playlist_entry_delete (int playlist_num, int at, int number)
1143 {
1144  /* stop playback before locking playlists */
1145  if (playback_get_playing () && playlist_num == playlist_get_playing () &&
1146  playlist_get_position (playlist_num) >= at && playlist_get_position
1147  (playlist_num) < at + number)
1148  playback_stop ();
1149 
1150  ENTER;
1153 
1154  int entries = index_count (playlist->entries);
1155 
1156  if (at < 0 || at > entries)
1157  at = entries;
1158  if (number < 0 || number > entries - at)
1159  number = entries - at;
1160 
1161  if (playlist->position && playlist->position->number >= at &&
1162  playlist->position->number < at + number)
1164 
1165  for (int count = 0; count < number; count ++)
1166  {
1167  Entry * entry = index_get (playlist->entries, at + count);
1168 
1169  if (entry->queued)
1170  playlist->queued = g_list_remove (playlist->queued, entry);
1171 
1172  if (entry->selected)
1173  {
1174  playlist->selected_count --;
1175  playlist->selected_length -= entry->length;
1176  }
1177 
1178  playlist->total_length -= entry->length;
1179  entry_free (entry);
1180  }
1181 
1182  index_delete (playlist->entries, at, number);
1183  number_entries (playlist, at, entries - at - number);
1184 
1186  LEAVE;
1187 }
1188 
1189 char * playlist_entry_get_filename (int playlist_num, int entry_num)
1190 {
1191  ENTER;
1194 
1195  char * filename = str_ref (entry->filename);
1196 
1197  LEAVE_RET (filename);
1198 }
1199 
1200 PluginHandle * playlist_entry_get_decoder (int playlist_num, int entry_num, bool_t fast)
1201 {
1202  ENTER;
1203 
1204  Entry * entry = get_entry (playlist_num, entry_num, ! fast, FALSE);
1205  PluginHandle * decoder = entry ? entry->decoder : NULL;
1206 
1207  LEAVE_RET (decoder);
1208 }
1209 
1210 Tuple * playlist_entry_get_tuple (int playlist_num, int entry_num, bool_t fast)
1211 {
1212  ENTER;
1213 
1214  Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
1215  Tuple * tuple = entry ? entry->tuple : NULL;
1216 
1217  if (tuple)
1218  tuple_ref (tuple);
1219 
1220  LEAVE_RET (tuple);
1221 }
1222 
1223 char * playlist_entry_get_title (int playlist_num, int entry_num, bool_t fast)
1224 {
1225  ENTER;
1226 
1227  Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
1228  char * title = entry ? str_ref (entry->formatted ? entry->formatted : entry->title) : NULL;
1229 
1230  LEAVE_RET (title);
1231 }
1232 
1233 void playlist_entry_describe (int playlist_num, int entry_num,
1234  char * * title, char * * artist, char * * album, bool_t fast)
1235 {
1236  ENTER;
1237 
1238  Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
1239 
1240  if (title)
1241  * title = (entry && entry->title) ? str_ref (entry->title) : NULL;
1242  if (artist)
1243  * artist = (entry && entry->artist) ? str_ref (entry->artist) : NULL;
1244  if (album)
1245  * album = (entry && entry->album) ? str_ref (entry->album) : NULL;
1246 
1247  LEAVE;
1248 }
1249 
1250 int playlist_entry_get_length (int playlist_num, int entry_num, bool_t fast)
1251 {
1252  ENTER;
1253 
1254  Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
1255  int length = entry ? entry->length : 0;
1256 
1257  LEAVE_RET (length);
1258 }
1259 
1260 void playlist_set_position (int playlist_num, int entry_num)
1261 {
1262  /* stop playback before locking playlists */
1263  if (playback_get_playing () && playlist_num == playlist_get_playing ())
1264  playback_stop ();
1265 
1266  ENTER;
1268 
1269  if (entry_num == -1)
1270  {
1272  entry = NULL;
1273  }
1274  else
1276 
1278  LEAVE;
1279 
1280  hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
1281 }
1282 
1283 int playlist_get_position (int playlist_num)
1284 {
1285  ENTER;
1287  LOOKUP_PLAYLIST_RET (-1);
1288 
1289  int position = playlist->position ? playlist->position->number : -1;
1290 
1291  LEAVE_RET (position);
1292 }
1293 
1294 void playlist_entry_set_selected (int playlist_num, int entry_num,
1295  bool_t selected)
1296 {
1297  ENTER;
1300 
1301  if (entry->selected == selected)
1303 
1304  entry->selected = selected;
1305 
1306  if (selected)
1307  {
1308  playlist->selected_count++;
1309  playlist->selected_length += entry->length;
1310  }
1311  else
1312  {
1313  playlist->selected_count--;
1314  playlist->selected_length -= entry->length;
1315  }
1316 
1317  queue_update (PLAYLIST_UPDATE_SELECTION, playlist->number, entry_num, 1);
1318  LEAVE;
1319 }
1320 
1321 bool_t playlist_entry_get_selected (int playlist_num, int entry_num)
1322 {
1323  ENTER;
1326 
1327  bool_t selected = entry->selected;
1328 
1329  LEAVE_RET (selected);
1330 }
1331 
1332 int playlist_selected_count (int playlist_num)
1333 {
1334  ENTER;
1336  LOOKUP_PLAYLIST_RET (0);
1337 
1338  int selected_count = playlist->selected_count;
1339 
1340  LEAVE_RET (selected_count);
1341 }
1342 
1343 void playlist_select_all (int playlist_num, bool_t selected)
1344 {
1345  ENTER;
1348 
1349  int entries = index_count (playlist->entries);
1350  int first = entries, last = 0;
1351 
1352  for (int count = 0; count < entries; count ++)
1353  {
1354  Entry * entry = index_get (playlist->entries, count);
1355 
1356  if ((selected && ! entry->selected) || (entry->selected && ! selected))
1357  {
1358  entry->selected = selected;
1359  first = MIN (first, entry->number);
1360  last = entry->number;
1361  }
1362  }
1363 
1364  if (selected)
1365  {
1366  playlist->selected_count = entries;
1367  playlist->selected_length = playlist->total_length;
1368  }
1369  else
1370  {
1371  playlist->selected_count = 0;
1372  playlist->selected_length = 0;
1373  }
1374 
1375  if (first < entries)
1376  queue_update (PLAYLIST_UPDATE_SELECTION, playlist->number, first, last + 1 - first);
1377 
1378  LEAVE;
1379 }
1380 
1381 int playlist_shift (int playlist_num, int entry_num, int distance)
1382 {
1383  ENTER;
1386 
1387  if (! entry->selected || ! distance)
1388  LEAVE_RET (0);
1389 
1390  int entries = index_count (playlist->entries);
1391  int shift = 0, center, top, bottom;
1392 
1393  if (distance < 0)
1394  {
1395  for (center = entry_num; center > 0 && shift > distance; )
1396  {
1397  entry = index_get (playlist->entries, -- center);
1398  if (! entry->selected)
1399  shift --;
1400  }
1401  }
1402  else
1403  {
1404  for (center = entry_num + 1; center < entries && shift < distance; )
1405  {
1406  entry = index_get (playlist->entries, center ++);
1407  if (! entry->selected)
1408  shift ++;
1409  }
1410  }
1411 
1412  top = bottom = center;
1413 
1414  for (int i = 0; i < top; i ++)
1415  {
1416  entry = index_get (playlist->entries, i);
1417  if (entry->selected)
1418  top = i;
1419  }
1420 
1421  for (int i = entries; i > bottom; i --)
1422  {
1423  entry = index_get (playlist->entries, i - 1);
1424  if (entry->selected)
1425  bottom = i;
1426  }
1427 
1428  Index * temp = index_new ();
1429 
1430  for (int i = top; i < center; i ++)
1431  {
1432  entry = index_get (playlist->entries, i);
1433  if (! entry->selected)
1434  index_append (temp, entry);
1435  }
1436 
1437  for (int i = top; i < bottom; i ++)
1438  {
1439  entry = index_get (playlist->entries, i);
1440  if (entry->selected)
1441  index_append (temp, entry);
1442  }
1443 
1444  for (int i = center; i < bottom; i ++)
1445  {
1446  entry = index_get (playlist->entries, i);
1447  if (! entry->selected)
1448  index_append (temp, entry);
1449  }
1450 
1451  index_copy_set (temp, 0, playlist->entries, top, bottom - top);
1452 
1453  number_entries (playlist, top, bottom - top);
1454  queue_update (PLAYLIST_UPDATE_STRUCTURE, playlist->number, top, bottom - top);
1455 
1456  LEAVE_RET (shift);
1457 }
1458 
1459 void playlist_delete_selected (int playlist_num)
1460 {
1461  /* stop playback before locking playlists */
1462  if (playback_get_playing () && playlist_num == playlist_get_playing () &&
1463  playlist_get_position (playlist_num) >= 0 && playlist_entry_get_selected
1464  (playlist_num, playlist_get_position (playlist_num)))
1465  playback_stop ();
1466 
1467  ENTER;
1470 
1471  if (! playlist->selected_count)
1473 
1474  int entries = index_count (playlist->entries);
1475 
1476  Index * others = index_new ();
1477  index_allocate (others, entries - playlist->selected_count);
1478 
1479  if (playlist->position && playlist->position->selected)
1481 
1482  int before = 0, after = 0;
1483  bool_t found = FALSE;
1484 
1485  for (int count = 0; count < entries; count++)
1486  {
1487  Entry * entry = index_get (playlist->entries, count);
1488 
1489  if (entry->selected)
1490  {
1491  if (entry->queued)
1492  playlist->queued = g_list_remove (playlist->queued, entry);
1493 
1494  playlist->total_length -= entry->length;
1495  entry_free (entry);
1496 
1497  found = TRUE;
1498  after = 0;
1499  }
1500  else
1501  {
1502  index_append (others, entry);
1503 
1504  if (found)
1505  after ++;
1506  else
1507  before ++;
1508  }
1509  }
1510 
1511  index_free (playlist->entries);
1512  playlist->entries = others;
1513 
1514  playlist->selected_count = 0;
1515  playlist->selected_length = 0;
1516 
1517  number_entries (playlist, before, index_count (playlist->entries) - before);
1519  index_count (playlist->entries) - after - before);
1520  LEAVE;
1521 }
1522 
1523 void playlist_reverse (int playlist_num)
1524 {
1525  ENTER;
1528 
1529  int entries = index_count (playlist->entries);
1530 
1531  Index * reversed = index_new ();
1532  index_allocate (reversed, entries);
1533 
1534  for (int count = entries; count --; )
1535  index_append (reversed, index_get (playlist->entries, count));
1536 
1537  index_free (playlist->entries);
1538  playlist->entries = reversed;
1539 
1540  number_entries (playlist, 0, entries);
1541  queue_update (PLAYLIST_UPDATE_STRUCTURE, playlist->number, 0, entries);
1542  LEAVE;
1543 }
1544 
1545 void playlist_randomize (int playlist_num)
1546 {
1547  ENTER;
1550 
1551  int entries = index_count (playlist->entries);
1552 
1553  for (int i = 0; i < entries; i ++)
1554  {
1555  int j = i + rand () % (entries - i);
1556 
1557  struct entry * entry = index_get (playlist->entries, j);
1558  index_set (playlist->entries, j, index_get (playlist->entries, i));
1559  index_set (playlist->entries, i, entry);
1560  }
1561 
1562  number_entries (playlist, 0, entries);
1563  queue_update (PLAYLIST_UPDATE_STRUCTURE, playlist->number, 0, entries);
1564  LEAVE;
1565 }
1566 
1567 enum {
1571 
1572 typedef int (* CompareFunc) (const void * a, const void * b);
1573 
1574 typedef struct {
1575  int type;
1577 } CompareData;
1578 
1579 static int compare_cb (const void * _a, const void * _b, void * _data)
1580 {
1581  const Entry * a = _a, * b = _b;
1582  CompareData * data = _data;
1583 
1584  int diff = 0;
1585 
1586  if (data->type == COMPARE_TYPE_FILENAME)
1587  diff = data->func (a->filename, b->filename);
1588  else if (data->type == COMPARE_TYPE_TUPLE)
1589  diff = data->func (a->tuple, b->tuple);
1590  else if (data->type == COMPARE_TYPE_TITLE)
1591  diff = data->func (a->formatted ? a->formatted : a->filename,
1592  b->formatted ? b->formatted : b->filename);
1593 
1594  if (diff)
1595  return diff;
1596 
1597  /* preserve order of "equal" entries */
1598  return a->number - b->number;
1599 }
1600 
1601 static void sort (Playlist * playlist, CompareData * data)
1602 {
1603  index_sort_with_data (playlist->entries, compare_cb, data);
1604  number_entries (playlist, 0, index_count (playlist->entries));
1605 
1606  queue_update (PLAYLIST_UPDATE_STRUCTURE, playlist->number, 0, index_count (playlist->entries));
1607 }
1608 
1610 {
1611  int entries = index_count (playlist->entries);
1612 
1613  Index * selected = index_new ();
1614  index_allocate (selected, playlist->selected_count);
1615 
1616  for (int count = 0; count < entries; count++)
1617  {
1618  Entry * entry = index_get (playlist->entries, count);
1619  if (entry->selected)
1620  index_append (selected, entry);
1621  }
1622 
1623  index_sort_with_data (selected, compare_cb, data);
1624 
1625  int count2 = 0;
1626  for (int count = 0; count < entries; count++)
1627  {
1628  Entry * entry = index_get (playlist->entries, count);
1629  if (entry->selected)
1630  index_set (playlist->entries, count, index_get (selected, count2 ++));
1631  }
1632 
1633  index_free (selected);
1634 
1635  number_entries (playlist, 0, entries);
1636  queue_update (PLAYLIST_UPDATE_STRUCTURE, playlist->number, 0, entries);
1637 }
1638 
1640 {
1641  int entries = index_count (playlist->entries);
1642  for (int count = 0; count < entries; count ++)
1643  {
1644  Entry * entry = index_get (playlist->entries, count);
1645  if (selected && ! entry->selected)
1646  continue;
1647 
1648  if (! entry->tuple)
1649  {
1650  interface_show_error (_("The playlist cannot be sorted because "
1651  "metadata scanning is still in progress (or has been disabled)."));
1652  return FALSE;
1653  }
1654  }
1655 
1656  return TRUE;
1657 }
1658 
1659 void playlist_sort_by_filename (int playlist_num, int (* compare)
1660  (const char * a, const char * b))
1661 {
1662  ENTER;
1665 
1666  CompareData data = {COMPARE_TYPE_FILENAME, (CompareFunc) compare};
1667  sort (playlist, & data);
1668 
1669  LEAVE;
1670 }
1671 
1672 void playlist_sort_by_tuple (int playlist_num, int (* compare)
1673  (const Tuple * a, const Tuple * b))
1674 {
1675  ENTER;
1678 
1679  CompareData data = {COMPARE_TYPE_TUPLE, (CompareFunc) compare};
1681  sort (playlist, & data);
1682 
1683  LEAVE;
1684 }
1685 
1686 void playlist_sort_by_title (int playlist_num, int (* compare) (const char *
1687  a, const char * b))
1688 {
1689  ENTER;
1692 
1693  CompareData data = {COMPARE_TYPE_TITLE, (CompareFunc) compare};
1695  sort (playlist, & data);
1696 
1697  LEAVE;
1698 }
1699 
1700 void playlist_sort_selected_by_filename (int playlist_num, int (* compare)
1701  (const char * a, const char * b))
1702 {
1703  ENTER;
1706 
1707  CompareData data = {COMPARE_TYPE_FILENAME, (CompareFunc) compare};
1708  sort_selected (playlist, & data);
1709 
1710  LEAVE;
1711 }
1712 
1713 void playlist_sort_selected_by_tuple (int playlist_num, int (* compare)
1714  (const Tuple * a, const Tuple * b))
1715 {
1716  ENTER;
1719 
1720  CompareData data = {COMPARE_TYPE_TUPLE, (CompareFunc) compare};
1722  sort_selected (playlist, & data);
1723 
1724  LEAVE;
1725 }
1726 
1727 void playlist_sort_selected_by_title (int playlist_num, int (* compare)
1728  (const char * a, const char * b))
1729 {
1730  ENTER;
1733 
1734  CompareData data = {COMPARE_TYPE_TITLE, (CompareFunc) compare};
1736  sort_selected (playlist, & data);
1737 
1738  LEAVE;
1739 }
1740 
1742 {
1743  ENTER;
1744 
1745  g_free (title_format);
1746  title_format = NULL;
1747 
1748  for (int playlist_num = 0; playlist_num < index_count (playlists);
1749  playlist_num ++)
1750  {
1751  Playlist * playlist = index_get (playlists, playlist_num);
1752  int entries = index_count (playlist->entries);
1753 
1754  for (int count = 0; count < entries; count++)
1755  {
1756  Entry * entry = index_get (playlist->entries, count);
1757  str_unref (entry->formatted);
1758  entry->formatted = entry->tuple ? title_from_tuple (entry->tuple) : NULL;
1759  }
1760 
1761  queue_update (PLAYLIST_UPDATE_METADATA, playlist_num, 0, entries);
1762  }
1763 
1764  LEAVE;
1765 }
1766 
1768 {
1769  ENTER;
1770 
1771  for (int i = 0; i < index_count (playlists); i ++)
1772  {
1773  Playlist * p = index_get (playlists, i);
1774  p->scanning = TRUE;
1775  }
1776 
1777  scan_trigger ();
1778 
1779  LEAVE;
1780 }
1781 
1782 static void playlist_rescan_real (int playlist_num, bool_t selected)
1783 {
1784  ENTER;
1787 
1788  int entries = index_count (playlist->entries);
1789 
1790  for (int count = 0; count < entries; count ++)
1791  {
1792  Entry * entry = index_get (playlist->entries, count);
1793  if (! selected || entry->selected)
1794  {
1795  entry_set_tuple (playlist, entry, NULL);
1796  entry->failed = FALSE;
1797  }
1798  }
1799 
1800  queue_update (PLAYLIST_UPDATE_METADATA, playlist->number, 0, entries);
1801  LEAVE;
1802 }
1803 
1804 void playlist_rescan (int playlist_num)
1805 {
1806  playlist_rescan_real (playlist_num, FALSE);
1807 }
1808 
1809 void playlist_rescan_selected (int playlist_num)
1810 {
1811  playlist_rescan_real (playlist_num, TRUE);
1812 }
1813 
1814 void playlist_rescan_file (const char * filename)
1815 {
1816  ENTER;
1817 
1818  int num_playlists = index_count (playlists);
1819 
1820  for (int playlist_num = 0; playlist_num < num_playlists; playlist_num ++)
1821  {
1822  Playlist * playlist = index_get (playlists, playlist_num);
1823  int num_entries = index_count (playlist->entries);
1824 
1825  for (int entry_num = 0; entry_num < num_entries; entry_num ++)
1826  {
1827  Entry * entry = index_get (playlist->entries, entry_num);
1828 
1829  if (! strcmp (entry->filename, filename))
1830  {
1831  entry_set_tuple (playlist, entry, NULL);
1832  entry->failed = FALSE;
1833 
1834  queue_update (PLAYLIST_UPDATE_METADATA, playlist_num, entry_num, 1);
1835  }
1836  }
1837  }
1838 
1839  LEAVE;
1840 }
1841 
1842 int64_t playlist_get_total_length (int playlist_num)
1843 {
1844  ENTER;
1846  LOOKUP_PLAYLIST_RET (0);
1847 
1848  int64_t length = playlist->total_length;
1849 
1850  LEAVE_RET (length);
1851 }
1852 
1853 int64_t playlist_get_selected_length (int playlist_num)
1854 {
1855  ENTER;
1857  LOOKUP_PLAYLIST_RET (0);
1858 
1859  int64_t length = playlist->selected_length;
1860 
1861  LEAVE_RET (length);
1862 }
1863 
1864 int playlist_queue_count (int playlist_num)
1865 {
1866  ENTER;
1868  LOOKUP_PLAYLIST_RET (0);
1869 
1870  int count = g_list_length (playlist->queued);
1871 
1872  LEAVE_RET (count);
1873 }
1874 
1875 void playlist_queue_insert (int playlist_num, int at, int entry_num)
1876 {
1877  ENTER;
1880 
1881  if (entry->queued)
1883 
1884  if (at < 0)
1885  playlist->queued = g_list_append (playlist->queued, entry);
1886  else
1887  playlist->queued = g_list_insert (playlist->queued, entry, at);
1888 
1889  entry->queued = TRUE;
1890 
1891  queue_update (PLAYLIST_UPDATE_SELECTION, playlist->number, entry_num, 1);
1892  LEAVE;
1893 }
1894 
1895 void playlist_queue_insert_selected (int playlist_num, int at)
1896 {
1897  ENTER;
1900 
1901  int entries = index_count(playlist->entries);
1902  int first = entries, last = 0;
1903 
1904  for (int count = 0; count < entries; count++)
1905  {
1906  Entry * entry = index_get (playlist->entries, count);
1907 
1908  if (! entry->selected || entry->queued)
1909  continue;
1910 
1911  if (at < 0)
1912  playlist->queued = g_list_append (playlist->queued, entry);
1913  else
1914  playlist->queued = g_list_insert (playlist->queued, entry, at++);
1915 
1916  entry->queued = TRUE;
1917  first = MIN (first, entry->number);
1918  last = entry->number;
1919  }
1920 
1921  if (first < entries)
1922  queue_update (PLAYLIST_UPDATE_SELECTION, playlist->number, first, last + 1 - first);
1923 
1924  LEAVE;
1925 }
1926 
1927 int playlist_queue_get_entry (int playlist_num, int at)
1928 {
1929  ENTER;
1931  LOOKUP_PLAYLIST_RET (-1);
1932 
1933  GList * node = g_list_nth (playlist->queued, at);
1934  int entry_num = node ? ((Entry *) node->data)->number : -1;
1935 
1936  LEAVE_RET (entry_num);
1937 }
1938 
1939 int playlist_queue_find_entry (int playlist_num, int entry_num)
1940 {
1941  ENTER;
1944 
1945  int pos = entry->queued ? g_list_index (playlist->queued, entry) : -1;
1946 
1947  LEAVE_RET (pos);
1948 }
1949 
1950 void playlist_queue_delete (int playlist_num, int at, int number)
1951 {
1952  ENTER;
1955 
1956  int entries = index_count (playlist->entries);
1957  int first = entries, last = 0;
1958 
1959  if (at == 0)
1960  {
1961  while (playlist->queued && number --)
1962  {
1963  Entry * entry = playlist->queued->data;
1964  entry->queued = FALSE;
1965  first = MIN (first, entry->number);
1966  last = entry->number;
1967 
1968  playlist->queued = g_list_delete_link (playlist->queued, playlist->queued);
1969  }
1970  }
1971  else
1972  {
1973  GList * anchor = g_list_nth (playlist->queued, at - 1);
1974  if (! anchor)
1975  goto DONE;
1976 
1977  while (anchor->next && number --)
1978  {
1979  Entry * entry = anchor->next->data;
1980  entry->queued = FALSE;
1981  first = MIN (first, entry->number);
1982  last = entry->number;
1983 
1984  playlist->queued = g_list_delete_link (playlist->queued, anchor->next);
1985  }
1986  }
1987 
1988 DONE:
1989  if (first < entries)
1990  queue_update (PLAYLIST_UPDATE_SELECTION, playlist->number, first, last + 1 - first);
1991 
1992  LEAVE;
1993 }
1994 
1995 void playlist_queue_delete_selected (int playlist_num)
1996 {
1997  ENTER;
2000 
2001  int entries = index_count (playlist->entries);
2002  int first = entries, last = 0;
2003 
2004  for (GList * node = playlist->queued; node; )
2005  {
2006  GList * next = node->next;
2007  Entry * entry = node->data;
2008 
2009  if (entry->selected)
2010  {
2011  entry->queued = FALSE;
2012  playlist->queued = g_list_delete_link (playlist->queued, node);
2013  first = MIN (first, entry->number);
2014  last = entry->number;
2015  }
2016 
2017  node = next;
2018  }
2019 
2020  if (first < entries)
2021  queue_update (PLAYLIST_UPDATE_SELECTION, playlist->number, first, last + 1 - first);
2022 
2023  LEAVE;
2024 }
2025 
2027 {
2028  int entries = index_count (playlist->entries);
2029  Entry * found = NULL;
2030 
2031  for (int count = 0; count < entries; count ++)
2032  {
2033  Entry * entry = index_get (playlist->entries, count);
2034 
2035  if (entry->shuffle_num && (! playlist->position ||
2036  entry->shuffle_num < playlist->position->shuffle_num) && (! found
2037  || entry->shuffle_num > found->shuffle_num))
2038  found = entry;
2039  }
2040 
2041  if (! found)
2042  return FALSE;
2043 
2044  set_position (playlist, found, FALSE);
2045  return TRUE;
2046 }
2047 
2048 bool_t playlist_prev_song (int playlist_num)
2049 {
2050  /* stop playback before locking playlists */
2051  if (playback_get_playing () && playlist_num == playlist_get_playing ())
2052  playback_stop ();
2053 
2054  ENTER;
2057 
2058  if (get_bool (NULL, "shuffle"))
2059  {
2060  if (! shuffle_prev (playlist))
2061  LEAVE_RET (FALSE);
2062  }
2063  else
2064  {
2065  if (! playlist->position || playlist->position->number == 0)
2066  LEAVE_RET (FALSE);
2067 
2069  playlist->position->number - 1), TRUE);
2070  }
2071 
2072  LEAVE;
2073 
2074  hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
2075  return TRUE;
2076 }
2077 
2079 {
2080  int entries = index_count (playlist->entries), choice = 0, count;
2081  Entry * found = NULL;
2082 
2083  for (count = 0; count < entries; count ++)
2084  {
2085  Entry * entry = index_get (playlist->entries, count);
2086 
2087  if (! entry->shuffle_num)
2088  choice ++;
2089  else if (playlist->position && entry->shuffle_num >
2090  playlist->position->shuffle_num && (! found || entry->shuffle_num
2091  < found->shuffle_num))
2092  found = entry;
2093  }
2094 
2095  if (found)
2096  {
2097  set_position (playlist, found, FALSE);
2098  return TRUE;
2099  }
2100 
2101  if (! choice)
2102  return FALSE;
2103 
2104  choice = rand () % choice;
2105 
2106  for (count = 0; ; count ++)
2107  {
2108  Entry * entry = index_get (playlist->entries, count);
2109 
2110  if (! entry->shuffle_num)
2111  {
2112  if (! choice)
2113  {
2114  set_position (playlist, entry, TRUE);
2115  return TRUE;
2116  }
2117 
2118  choice --;
2119  }
2120  }
2121 }
2122 
2124 {
2125  int entries = index_count (playlist->entries);
2126 
2127  playlist->last_shuffle_num = 0;
2128 
2129  for (int count = 0; count < entries; count ++)
2130  {
2131  Entry * entry = index_get (playlist->entries, count);
2132  entry->shuffle_num = 0;
2133  }
2134 }
2135 
2136 bool_t playlist_next_song (int playlist_num, bool_t repeat)
2137 {
2138  /* stop playback before locking playlists */
2139  if (playback_get_playing () && playlist_num == playlist_get_playing ())
2140  playback_stop ();
2141 
2142  ENTER;
2145 
2146  int entries = index_count(playlist->entries);
2147 
2148  if (! entries)
2149  LEAVE_RET (FALSE);
2150 
2151  if (playlist->queued)
2152  {
2153  set_position (playlist, playlist->queued->data, TRUE);
2154  playlist->queued = g_list_remove (playlist->queued, playlist->position);
2155  playlist->position->queued = FALSE;
2156  }
2157  else if (get_bool (NULL, "shuffle"))
2158  {
2159  if (! shuffle_next (playlist))
2160  {
2161  if (! repeat)
2162  LEAVE_RET (FALSE);
2163 
2165 
2166  if (! shuffle_next (playlist))
2167  LEAVE_RET (FALSE);
2168  }
2169  }
2170  else
2171  {
2172  if (! playlist->position)
2173  set_position (playlist, index_get (playlist->entries, 0), TRUE);
2174  else if (playlist->position->number == entries - 1)
2175  {
2176  if (! repeat)
2177  LEAVE_RET (FALSE);
2178 
2179  set_position (playlist, index_get (playlist->entries, 0), TRUE);
2180  }
2181  else
2183  playlist->position->number + 1), TRUE);
2184  }
2185 
2186  LEAVE;
2187 
2188  hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
2189  return TRUE;
2190 }
2191 
2193 {
2194  ENTER;
2195 
2197  int entry_num = entry ? entry->number : -1;
2198 
2199  LEAVE_RET (entry_num);
2200 }
2201 
2203 {
2204  ENTER;
2205 
2207  PluginHandle * decoder = entry ? entry->decoder : NULL;
2208 
2209  LEAVE_RET (decoder);
2210 }
2211 
2213 {
2214  ENTER;
2215 
2217  Tuple * tuple = entry ? entry->tuple : NULL;
2218 
2219  if (tuple)
2220  tuple_ref (tuple);
2221 
2222  LEAVE_RET (tuple);
2223 }
2224 
2226 {
2227  ENTER;
2228 
2230  char * title = entry ? str_ref (entry->formatted ? entry->formatted : entry->title) : NULL;
2231 
2232  LEAVE_RET (title);
2233 }
2234 
2236 {
2237  ENTER;
2238 
2240  int length = entry ? entry->length : 0;
2241 
2242  LEAVE_RET (length);
2243 }
2244 
2245 void playback_entry_set_tuple (Tuple * tuple)
2246 {
2247  ENTER;
2248  if (! playing_playlist || ! playing_playlist->position)
2250 
2251  Entry * entry = playing_playlist->position;
2252  entry_cancel_scan (entry);
2253  entry_set_tuple (playing_playlist, entry, tuple);
2254 
2255  queue_update (PLAYLIST_UPDATE_METADATA, playing_playlist->number, entry->number, 1);
2256  LEAVE;
2257 }
2258 
2260 {
2261  ENTER;
2262  if (! playing_playlist || ! playing_playlist->position)
2263  LEAVE_RET (0);
2264 
2265  int start = playing_playlist->position->start;
2266  LEAVE_RET (start);
2267 }
2268 
2270 {
2271  ENTER;
2272  if (! playing_playlist || ! playing_playlist->position)
2273  LEAVE_RET (-1);
2274 
2275  int end = playing_playlist->position->end;
2276  LEAVE_RET (end);
2277 }
2278 
2280 {
2281  /* get playback state before locking playlists */
2282  int state, time;
2283  get_resume_state (& state, & time);
2284 
2285  ENTER;
2286 
2287  char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
2288  FILE * handle = fopen (path, "w");
2289  g_free (path);
2290  if (! handle)
2292 
2293  fprintf (handle, "active %d\n", active_playlist ? active_playlist->number : -1);
2294  fprintf (handle, "playing %d\n", playing_playlist ? playing_playlist->number : -1);
2295 
2296  for (int playlist_num = 0; playlist_num < index_count (playlists);
2297  playlist_num ++)
2298  {
2299  Playlist * playlist = index_get (playlists, playlist_num);
2300 
2301  fprintf (handle, "playlist %d\n", playlist_num);
2302 
2303  if (playlist->filename)
2304  fprintf (handle, "filename %s\n", playlist->filename);
2305 
2306  fprintf (handle, "position %d\n", playlist->position ? playlist->position->number : -1);
2307 
2308  if (playlist == playing_playlist)
2309  {
2310  playlist->resume_state = state;
2311  playlist->resume_time = time;
2312  }
2313 
2314  fprintf (handle, "resume-state %d\n", playlist->resume_state);
2315  fprintf (handle, "resume-time %d\n", playlist->resume_time);
2316  }
2317 
2318  fclose (handle);
2319  LEAVE;
2320 }
2321 
2322 static char parse_key[512];
2323 static char * parse_value;
2324 
2325 static void parse_next (FILE * handle)
2326 {
2327  parse_value = NULL;
2328 
2329  if (! fgets (parse_key, sizeof parse_key, handle))
2330  return;
2331 
2332  char * space = strchr (parse_key, ' ');
2333  if (! space)
2334  return;
2335 
2336  * space = 0;
2337  parse_value = space + 1;
2338 
2339  char * newline = strchr (parse_value, '\n');
2340  if (newline)
2341  * newline = 0;
2342 }
2343 
2344 static bool_t parse_integer (const char * key, int * value)
2345 {
2346  return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value, "%d", value) == 1);
2347 }
2348 
2349 static char * parse_string (const char * key)
2350 {
2351  return (parse_value && ! strcmp (parse_key, key)) ? str_get (parse_value) : NULL;
2352 }
2353 
2355 {
2356  ENTER;
2357  int playlist_num;
2358 
2359  char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
2360  FILE * handle = fopen (path, "r");
2361  g_free (path);
2362  if (! handle)
2364 
2365  parse_next (handle);
2366 
2367  if (parse_integer ("active", & playlist_num))
2368  {
2369  if (! (active_playlist = lookup_playlist (playlist_num)))
2370  active_playlist = index_get (playlists, 0);
2371  parse_next (handle);
2372  }
2373 
2374  if (parse_integer ("playing", & playlist_num))
2375  {
2376  playing_playlist = lookup_playlist (playlist_num);
2377  parse_next (handle);
2378  }
2379 
2380  while (parse_integer ("playlist", & playlist_num) && playlist_num >= 0 &&
2381  playlist_num < index_count (playlists))
2382  {
2383  Playlist * playlist = index_get (playlists, playlist_num);
2384  int entries = index_count (playlist->entries), position;
2385  char * s;
2386 
2387  parse_next (handle);
2388 
2389  if ((s = parse_string ("filename")))
2390  {
2391  str_unref (playlist->filename);
2392  playlist->filename = s;
2393  parse_next (handle);
2394  }
2395 
2396  if (parse_integer ("position", & position))
2397  parse_next (handle);
2398 
2399  if (position >= 0 && position < entries)
2400  set_position (playlist, index_get (playlist->entries, position), TRUE);
2401 
2402  if (parse_integer ("resume-state", & playlist->resume_state))
2403  parse_next (handle);
2404  if (parse_integer ("resume-time", & playlist->resume_time))
2405  parse_next (handle);
2406  }
2407 
2408  fclose (handle);
2409 
2410  /* clear updates queued during init sequence */
2411 
2412  for (int i = 0; i < index_count (playlists); i ++)
2413  {
2414  Playlist * p = index_get (playlists, i);
2415  memset (& p->last_update, 0, sizeof (Update));
2416  memset (& p->next_update, 0, sizeof (Update));
2417  }
2418 
2419  update_level = 0;
2420 
2421  if (update_source)
2422  {
2423  g_source_remove (update_source);
2424  update_source = 0;
2425  }
2426 
2427  LEAVE;
2428 }
2429 
2430 void playlist_resume (void)
2431 {
2432  int state = RESUME_STOP, time = 0;
2433 
2434  ENTER;
2435 
2436  if (playing_playlist)
2437  {
2438  state = playing_playlist->resume_state;
2439  time = playing_playlist->resume_time;
2440  }
2441 
2442  LEAVE;
2443 
2444  /* resume playback after unlocking playlists */
2445  if (state == RESUME_PLAY && ! get_bool (NULL, "resume_playback_on_startup"))
2446  state = RESUME_PAUSE;
2447 
2448  resume (state, time);
2449 }