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