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