00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <pthread.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <time.h>
00027
00028 #include <glib.h>
00029
00030 #include <libaudcore/audstrings.h>
00031 #include <libaudcore/hook.h>
00032 #include <libaudcore/tuple.h>
00033
00034 #include "config.h"
00035 #include "i18n.h"
00036 #include "misc.h"
00037 #include "playback.h"
00038 #include "playlist.h"
00039 #include "plugins.h"
00040 #include "util.h"
00041
00042 enum {RESUME_STOP, RESUME_PLAY, RESUME_PAUSE};
00043
00044 #define SCAN_THREADS 2
00045 #define STATE_FILE "playlist-state"
00046
00047 #define ENTER pthread_mutex_lock (& mutex)
00048 #define LEAVE pthread_mutex_unlock (& mutex)
00049
00050 #define LEAVE_RET_VOID do { \
00051 pthread_mutex_unlock (& mutex); \
00052 return; \
00053 } while (0)
00054
00055 #define LEAVE_RET(ret) do { \
00056 pthread_mutex_unlock (& mutex); \
00057 return ret; \
00058 } while (0)
00059
00060 #define DECLARE_PLAYLIST \
00061 Playlist * playlist
00062
00063 #define DECLARE_PLAYLIST_ENTRY \
00064 Playlist * playlist; \
00065 Entry * entry
00066
00067 #define LOOKUP_PLAYLIST do { \
00068 if (! (playlist = lookup_playlist (playlist_num))) \
00069 LEAVE_RET_VOID; \
00070 } while (0)
00071
00072 #define LOOKUP_PLAYLIST_RET(ret) do { \
00073 if (! (playlist = lookup_playlist (playlist_num))) \
00074 LEAVE_RET(ret); \
00075 } while (0)
00076
00077 #define LOOKUP_PLAYLIST_ENTRY do { \
00078 LOOKUP_PLAYLIST; \
00079 if (! (entry = lookup_entry (playlist, entry_num))) \
00080 LEAVE_RET_VOID; \
00081 } while (0)
00082
00083 #define LOOKUP_PLAYLIST_ENTRY_RET(ret) do { \
00084 LOOKUP_PLAYLIST_RET(ret); \
00085 if (! (entry = lookup_entry (playlist, entry_num))) \
00086 LEAVE_RET(ret); \
00087 } while (0)
00088
00089 #define SELECTION_HAS_CHANGED(p, a, c) \
00090 queue_update (PLAYLIST_UPDATE_SELECTION, p, a, c)
00091
00092 #define METADATA_HAS_CHANGED(p, a, c) \
00093 queue_update (PLAYLIST_UPDATE_METADATA, p, a, c)
00094
00095 #define PLAYLIST_HAS_CHANGED(p, a, c) \
00096 queue_update (PLAYLIST_UPDATE_STRUCTURE, p, a, c)
00097
00098 typedef struct {
00099 int level, before, after;
00100 } Update;
00101
00102 typedef struct {
00103 int number;
00104 char * filename;
00105 PluginHandle * decoder;
00106 Tuple * tuple;
00107 char * formatted, * title, * artist, * album;
00108 int length;
00109 bool_t failed;
00110 bool_t selected;
00111 int shuffle_num;
00112 bool_t queued;
00113 bool_t segmented;
00114 int start, end;
00115 } Entry;
00116
00117 typedef struct {
00118 int number, unique_id;
00119 char * filename, * title;
00120 bool_t modified;
00121 Index * entries;
00122 Entry * position;
00123 int selected_count;
00124 int last_shuffle_num;
00125 GList * queued;
00126 int64_t total_length, selected_length;
00127 bool_t scanning, scan_ending;
00128 Update next_update, last_update;
00129 } Playlist;
00130
00131 static const char * const default_title = N_("New Playlist");
00132 static const char * const temp_title = N_("Now Playing");
00133
00134 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
00135 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
00136
00137
00138
00139 static GHashTable * unique_id_table = NULL;
00140 static int next_unique_id = 1000;
00141
00142 static Index * playlists = NULL;
00143 static Playlist * active_playlist = NULL;
00144 static Playlist * playing_playlist = NULL;
00145
00146 static int update_source = 0, update_level;
00147 static int resume_state, resume_time;
00148
00149 typedef struct {
00150 Playlist * playlist;
00151 Entry * entry;
00152 } ScanItem;
00153
00154 static pthread_t scan_threads[SCAN_THREADS];
00155 static bool_t scan_quit;
00156 static int scan_playlist, scan_row;
00157 static GQueue scan_queue = G_QUEUE_INIT;
00158 static ScanItem * scan_items[SCAN_THREADS];
00159
00160 static void * scanner (void * unused);
00161 static void scan_trigger (void);
00162
00163 static char * title_format;
00164
00165 static char * title_from_tuple (Tuple * tuple)
00166 {
00167 if (! title_format)
00168 title_format = get_string (NULL, "generic_title_format");
00169
00170 return tuple_format_title (tuple, title_format);
00171 }
00172
00173 static void entry_set_tuple_real (Entry * entry, Tuple * tuple)
00174 {
00175
00176
00177 if (entry->segmented && entry->tuple)
00178 {
00179 if (tuple)
00180 tuple_unref (tuple);
00181 return;
00182 }
00183
00184 if (entry->tuple)
00185 tuple_unref (entry->tuple);
00186 entry->tuple = tuple;
00187
00188 str_unref (entry->formatted);
00189 str_unref (entry->title);
00190 str_unref (entry->artist);
00191 str_unref (entry->album);
00192
00193 describe_song (entry->filename, tuple, & entry->title, & entry->artist, & entry->album);
00194
00195 if (! tuple)
00196 {
00197 entry->formatted = NULL;
00198 entry->length = 0;
00199 entry->segmented = FALSE;
00200 entry->start = 0;
00201 entry->end = -1;
00202 }
00203 else
00204 {
00205 entry->formatted = title_from_tuple (tuple);
00206 entry->length = tuple_get_int (tuple, FIELD_LENGTH, NULL);
00207 if (entry->length < 0)
00208 entry->length = 0;
00209
00210 if (tuple_get_value_type (tuple, FIELD_SEGMENT_START, NULL) == TUPLE_INT)
00211 {
00212 entry->segmented = TRUE;
00213 entry->start = tuple_get_int (tuple, FIELD_SEGMENT_START, NULL);
00214
00215 if (tuple_get_value_type (tuple, FIELD_SEGMENT_END, NULL) ==
00216 TUPLE_INT)
00217 entry->end = tuple_get_int (tuple, FIELD_SEGMENT_END, NULL);
00218 else
00219 entry->end = -1;
00220 }
00221 else
00222 entry->segmented = FALSE;
00223 }
00224 }
00225
00226 static void entry_set_tuple (Playlist * playlist, Entry * entry, Tuple * tuple)
00227 {
00228 if (entry->tuple)
00229 {
00230 playlist->total_length -= entry->length;
00231 if (entry->selected)
00232 playlist->selected_length -= entry->length;
00233 }
00234
00235 entry_set_tuple_real (entry, tuple);
00236
00237 if (tuple)
00238 {
00239 playlist->total_length += entry->length;
00240 if (entry->selected)
00241 playlist->selected_length += entry->length;
00242 }
00243 }
00244
00245 static void entry_set_failed (Playlist * playlist, Entry * entry)
00246 {
00247 entry_set_tuple (playlist, entry, tuple_new_from_filename (entry->filename));
00248 entry->failed = TRUE;
00249 }
00250
00251 static void entry_cancel_scan (Entry * entry)
00252 {
00253 GList * next;
00254 for (GList * node = scan_queue.head; node; node = next)
00255 {
00256 ScanItem * item = node->data;
00257 next = node->next;
00258
00259 if (item->entry == entry)
00260 {
00261 g_queue_delete_link (& scan_queue, node);
00262 g_slice_free (ScanItem, item);
00263 }
00264 }
00265
00266 for (int i = 0; i < SCAN_THREADS; i ++)
00267 {
00268 if (scan_items[i] && scan_items[i]->entry == entry)
00269 {
00270 g_slice_free (ScanItem, scan_items[i]);
00271 scan_items[i] = NULL;
00272 }
00273 }
00274 }
00275
00276 static Entry * entry_new (char * filename, Tuple * tuple,
00277 PluginHandle * decoder)
00278 {
00279 Entry * entry = g_slice_new (Entry);
00280
00281 entry->filename = filename;
00282 entry->decoder = decoder;
00283 entry->tuple = NULL;
00284 entry->formatted = NULL;
00285 entry->title = NULL;
00286 entry->artist = NULL;
00287 entry->album = NULL;
00288 entry->failed = FALSE;
00289 entry->number = -1;
00290 entry->selected = FALSE;
00291 entry->shuffle_num = 0;
00292 entry->queued = FALSE;
00293 entry->segmented = FALSE;
00294 entry->start = 0;
00295 entry->end = -1;
00296
00297 entry_set_tuple_real (entry, tuple);
00298 return entry;
00299 }
00300
00301 static void entry_free (Entry * entry)
00302 {
00303 entry_cancel_scan (entry);
00304
00305 str_unref (entry->filename);
00306 if (entry->tuple)
00307 tuple_unref (entry->tuple);
00308
00309 str_unref (entry->formatted);
00310 str_unref (entry->title);
00311 str_unref (entry->artist);
00312 str_unref (entry->album);
00313 g_slice_free (Entry, entry);
00314 }
00315
00316 static int new_unique_id (int preferred)
00317 {
00318 if (preferred >= 0 && ! g_hash_table_lookup_extended (unique_id_table,
00319 GINT_TO_POINTER (preferred), NULL, NULL))
00320 return preferred;
00321
00322 while (g_hash_table_lookup_extended (unique_id_table,
00323 GINT_TO_POINTER (next_unique_id), NULL, NULL))
00324 next_unique_id ++;
00325
00326 return next_unique_id ++;
00327 }
00328
00329 static Playlist * playlist_new (int id)
00330 {
00331 Playlist * playlist = g_slice_new (Playlist);
00332
00333 playlist->number = -1;
00334 playlist->unique_id = new_unique_id (id);
00335 playlist->filename = NULL;
00336 playlist->title = str_get (_(default_title));
00337 playlist->modified = TRUE;
00338 playlist->entries = index_new();
00339 playlist->position = NULL;
00340 playlist->selected_count = 0;
00341 playlist->last_shuffle_num = 0;
00342 playlist->queued = NULL;
00343 playlist->total_length = 0;
00344 playlist->selected_length = 0;
00345 playlist->scanning = FALSE;
00346 playlist->scan_ending = FALSE;
00347
00348 memset (& playlist->last_update, 0, sizeof (Update));
00349 memset (& playlist->next_update, 0, sizeof (Update));
00350
00351 g_hash_table_insert (unique_id_table, GINT_TO_POINTER (playlist->unique_id), playlist);
00352 return playlist;
00353 }
00354
00355 static void playlist_free (Playlist * playlist)
00356 {
00357 g_hash_table_insert (unique_id_table, GINT_TO_POINTER (playlist->unique_id), NULL);
00358
00359 str_unref (playlist->filename);
00360 str_unref (playlist->title);
00361
00362 for (int count = 0; count < index_count (playlist->entries); count ++)
00363 entry_free (index_get (playlist->entries, count));
00364
00365 index_free (playlist->entries);
00366 g_list_free (playlist->queued);
00367 g_slice_free (Playlist, playlist);
00368 }
00369
00370 static void number_playlists (int at, int length)
00371 {
00372 for (int count = 0; count < length; count ++)
00373 {
00374 Playlist * playlist = index_get (playlists, at + count);
00375 playlist->number = at + count;
00376 }
00377 }
00378
00379 static Playlist * lookup_playlist (int playlist_num)
00380 {
00381 return (playlists && playlist_num >= 0 && playlist_num < index_count
00382 (playlists)) ? index_get (playlists, playlist_num) : NULL;
00383 }
00384
00385 static void number_entries (Playlist * playlist, int at, int length)
00386 {
00387 for (int count = 0; count < length; count ++)
00388 {
00389 Entry * entry = index_get (playlist->entries, at + count);
00390 entry->number = at + count;
00391 }
00392 }
00393
00394 static Entry * lookup_entry (Playlist * playlist, int entry_num)
00395 {
00396 return (entry_num >= 0 && entry_num < index_count (playlist->entries)) ?
00397 index_get (playlist->entries, entry_num) : NULL;
00398 }
00399
00400 static bool_t update (void * unused)
00401 {
00402 ENTER;
00403
00404 for (int i = 0; i < index_count (playlists); i ++)
00405 {
00406 Playlist * p = index_get (playlists, i);
00407 memcpy (& p->last_update, & p->next_update, sizeof (Update));
00408 memset (& p->next_update, 0, sizeof (Update));
00409 }
00410
00411 int level = update_level;
00412 update_level = 0;
00413
00414 if (update_source)
00415 {
00416 g_source_remove (update_source);
00417 update_source = 0;
00418 }
00419
00420 LEAVE;
00421
00422 hook_call ("playlist update", GINT_TO_POINTER (level));
00423 return FALSE;
00424 }
00425
00426 static void queue_update (int level, int list, int at, int count)
00427 {
00428 Playlist * p = lookup_playlist (list);
00429
00430 if (p)
00431 {
00432 if (level >= PLAYLIST_UPDATE_METADATA)
00433 {
00434 p->modified = TRUE;
00435
00436 if (! get_bool (NULL, "metadata_on_play"))
00437 {
00438 p->scanning = TRUE;
00439 p->scan_ending = FALSE;
00440 scan_trigger ();
00441 }
00442 }
00443
00444 if (p->next_update.level)
00445 {
00446 p->next_update.level = MAX (p->next_update.level, level);
00447 p->next_update.before = MIN (p->next_update.before, at);
00448 p->next_update.after = MIN (p->next_update.after,
00449 index_count (p->entries) - at - count);
00450 }
00451 else
00452 {
00453 p->next_update.level = level;
00454 p->next_update.before = at;
00455 p->next_update.after = index_count (p->entries) - at - count;
00456 }
00457 }
00458
00459 update_level = MAX (update_level, level);
00460
00461 if (! update_source)
00462 update_source = g_idle_add_full (G_PRIORITY_HIGH, update, NULL, NULL);
00463 }
00464
00465 bool_t playlist_update_pending (void)
00466 {
00467 ENTER;
00468 bool_t pending = update_level ? TRUE : FALSE;
00469 LEAVE_RET (pending);
00470 }
00471
00472 int playlist_updated_range (int playlist_num, int * at, int * count)
00473 {
00474 ENTER;
00475 DECLARE_PLAYLIST;
00476 LOOKUP_PLAYLIST_RET (0);
00477
00478 Update * u = & playlist->last_update;
00479
00480 int level = u->level;
00481 * at = u->before;
00482 * count = index_count (playlist->entries) - u->before - u->after;
00483
00484 LEAVE_RET (level);
00485 }
00486
00487 bool_t playlist_scan_in_progress (int playlist_num)
00488 {
00489 ENTER;
00490 DECLARE_PLAYLIST;
00491 LOOKUP_PLAYLIST_RET (FALSE);
00492
00493 bool_t scanning = playlist->scanning || playlist->scan_ending;
00494
00495 LEAVE_RET (scanning);
00496 }
00497
00498 static bool_t entry_scan_is_queued (Entry * entry)
00499 {
00500 for (GList * node = scan_queue.head; node; node = node->next)
00501 {
00502 ScanItem * item = node->data;
00503 if (item->entry == entry)
00504 return TRUE;
00505 }
00506
00507 for (int i = 0; i < SCAN_THREADS; i ++)
00508 {
00509 if (scan_items[i] && scan_items[i]->entry == entry)
00510 return TRUE;
00511 }
00512
00513 return FALSE;
00514 }
00515
00516 static void entry_queue_scan (Playlist * playlist, Entry * entry)
00517 {
00518 if (entry_scan_is_queued (entry))
00519 return;
00520
00521 ScanItem * item = g_slice_new (ScanItem);
00522 item->playlist = playlist;
00523 item->entry = entry;
00524 g_queue_push_tail (& scan_queue, item);
00525
00526 pthread_cond_broadcast (& cond);
00527 }
00528
00529 static void check_scan_complete (Playlist * p)
00530 {
00531 if (! p->scan_ending)
00532 return;
00533
00534 for (GList * node = scan_queue.head; node; node = node->next)
00535 {
00536 ScanItem * item = node->data;
00537 if (item->playlist == p)
00538 return;
00539 }
00540
00541 for (int i = 0; i < SCAN_THREADS; i ++)
00542 {
00543 if (scan_items[i] && scan_items[i]->playlist == p)
00544 return;
00545 }
00546
00547 p->scan_ending = FALSE;
00548
00549 event_queue_cancel ("playlist scan complete", NULL);
00550 event_queue ("playlist scan complete", NULL);
00551 }
00552
00553 static ScanItem * entry_find_to_scan (void)
00554 {
00555 ScanItem * item = g_queue_pop_head (& scan_queue);
00556 if (item)
00557 return item;
00558
00559 while (scan_playlist < index_count (playlists))
00560 {
00561 Playlist * playlist = index_get (playlists, scan_playlist);
00562
00563 if (playlist->scanning)
00564 {
00565 while (scan_row < index_count (playlist->entries))
00566 {
00567 Entry * entry = index_get (playlist->entries, scan_row);
00568
00569 if (! entry->tuple && ! entry_scan_is_queued (entry))
00570 {
00571 item = g_slice_new (ScanItem);
00572 item->playlist = playlist;
00573 item->entry = entry;
00574 return item;
00575 }
00576
00577 scan_row ++;
00578 }
00579
00580 playlist->scanning = FALSE;
00581 playlist->scan_ending = TRUE;
00582 check_scan_complete (playlist);
00583 }
00584
00585 scan_playlist ++;
00586 scan_row = 0;
00587 }
00588
00589 return NULL;
00590 }
00591
00592 static void * scanner (void * data)
00593 {
00594 ENTER;
00595
00596 int i = GPOINTER_TO_INT (data);
00597
00598 while (! scan_quit)
00599 {
00600 if (! scan_items[i])
00601 scan_items[i] = entry_find_to_scan ();
00602
00603 if (! scan_items[i])
00604 {
00605 pthread_cond_wait (& cond, & mutex);
00606 continue;
00607 }
00608
00609 Playlist * playlist = scan_items[i]->playlist;
00610 Entry * entry = scan_items[i]->entry;
00611 char * filename = str_ref (entry->filename);
00612 PluginHandle * decoder = entry->decoder;
00613 bool_t need_tuple = entry->tuple ? FALSE : TRUE;
00614
00615 LEAVE;
00616
00617 if (! decoder)
00618 decoder = file_find_decoder (filename, FALSE);
00619
00620 Tuple * tuple = (need_tuple && decoder) ? file_read_tuple (filename, decoder) : NULL;
00621
00622 ENTER;
00623
00624 str_unref (filename);
00625
00626 if (! scan_items[i])
00627 {
00628 if (tuple)
00629 tuple_unref (tuple);
00630 continue;
00631 }
00632
00633 entry->decoder = decoder;
00634
00635 if (tuple)
00636 {
00637 entry_set_tuple (playlist, entry, tuple);
00638 queue_update (PLAYLIST_UPDATE_METADATA, playlist->number, entry->number, 1);
00639 }
00640 else if (need_tuple || ! decoder)
00641 {
00642 entry_set_failed (playlist, entry);
00643 queue_update (PLAYLIST_UPDATE_METADATA, playlist->number, entry->number, 1);
00644 }
00645
00646 g_slice_free (ScanItem, scan_items[i]);
00647 scan_items[i] = NULL;
00648
00649 pthread_cond_broadcast (& cond);
00650 check_scan_complete (playlist);
00651 }
00652
00653 LEAVE_RET (NULL);
00654 }
00655
00656 static void scan_trigger (void)
00657 {
00658 scan_playlist = 0;
00659 scan_row = 0;
00660 pthread_cond_broadcast (& cond);
00661 }
00662
00663
00664 static Entry * get_entry (int playlist_num, int entry_num,
00665 bool_t need_decoder, bool_t need_tuple)
00666 {
00667 while (1)
00668 {
00669 Playlist * playlist = lookup_playlist (playlist_num);
00670 Entry * entry = playlist ? lookup_entry (playlist, entry_num) : NULL;
00671
00672 if (! entry || entry->failed)
00673 return entry;
00674
00675 if ((need_decoder && ! entry->decoder) || (need_tuple && ! entry->tuple))
00676 {
00677 entry_queue_scan (playlist, entry);
00678 pthread_cond_wait (& cond, & mutex);
00679 continue;
00680 }
00681
00682 return entry;
00683 }
00684 }
00685
00686
00687 static Entry * get_playback_entry (bool_t need_decoder, bool_t need_tuple)
00688 {
00689 while (1)
00690 {
00691 Entry * entry = playing_playlist ? playing_playlist->position : NULL;
00692
00693 if (! entry || entry->failed)
00694 return entry;
00695
00696 if ((need_decoder && ! entry->decoder) || (need_tuple && ! entry->tuple))
00697 {
00698 entry_queue_scan (playing_playlist, entry);
00699 pthread_cond_wait (& cond, & mutex);
00700 continue;
00701 }
00702
00703 return entry;
00704 }
00705 }
00706
00707 void playlist_init (void)
00708 {
00709 srand (time (NULL));
00710
00711 ENTER;
00712
00713 unique_id_table = g_hash_table_new (g_direct_hash, g_direct_equal);
00714 playlists = index_new ();
00715
00716 update_level = 0;
00717
00718 scan_quit = FALSE;
00719 scan_playlist = scan_row = 0;
00720
00721 for (int i = 0; i < SCAN_THREADS; i ++)
00722 pthread_create (& scan_threads[i], NULL, scanner, GINT_TO_POINTER (i));
00723
00724 LEAVE;
00725 }
00726
00727 void playlist_end (void)
00728 {
00729 ENTER;
00730
00731 scan_quit = TRUE;
00732 pthread_cond_broadcast (& cond);
00733
00734 LEAVE;
00735
00736 for (int i = 0; i < SCAN_THREADS; i ++)
00737 pthread_join (scan_threads[i], NULL);
00738
00739 ENTER;
00740
00741 if (update_source)
00742 {
00743 g_source_remove (update_source);
00744 update_source = 0;
00745 }
00746
00747 active_playlist = playing_playlist = NULL;
00748
00749 for (int i = 0; i < index_count (playlists); i ++)
00750 playlist_free (index_get (playlists, i));
00751
00752 index_free (playlists);
00753 playlists = NULL;
00754
00755 g_hash_table_destroy (unique_id_table);
00756 unique_id_table = NULL;
00757
00758 g_free (title_format);
00759 title_format = NULL;
00760
00761 LEAVE;
00762 }
00763
00764 int playlist_count (void)
00765 {
00766 ENTER;
00767 int count = index_count (playlists);
00768 LEAVE_RET (count);
00769 }
00770
00771 void playlist_insert_with_id (int at, int id)
00772 {
00773 ENTER;
00774
00775 if (at < 0 || at > index_count (playlists))
00776 at = index_count (playlists);
00777
00778 index_insert (playlists, at, playlist_new (id));
00779 number_playlists (at, index_count (playlists) - at);
00780
00781 PLAYLIST_HAS_CHANGED (-1, 0, 0);
00782 LEAVE;
00783 }
00784
00785 void playlist_insert (int at)
00786 {
00787 playlist_insert_with_id (at, -1);
00788 }
00789
00790 void playlist_reorder (int from, int to, int count)
00791 {
00792 ENTER;
00793 if (from < 0 || from + count > index_count (playlists) || to < 0 || to +
00794 count > index_count (playlists) || count < 0)
00795 LEAVE_RET_VOID;
00796
00797 Index * displaced = index_new ();
00798
00799 if (to < from)
00800 index_copy_append (playlists, to, displaced, from - to);
00801 else
00802 index_copy_append (playlists, from + count, displaced, to - from);
00803
00804 index_move (playlists, from, to, count);
00805
00806 if (to < from)
00807 {
00808 index_copy_set (displaced, 0, playlists, to + count, from - to);
00809 number_playlists (to, from + count - to);
00810 }
00811 else
00812 {
00813 index_copy_set (displaced, 0, playlists, from, to - from);
00814 number_playlists (from, to + count - from);
00815 }
00816
00817 index_free (displaced);
00818
00819 PLAYLIST_HAS_CHANGED (-1, 0, 0);
00820 LEAVE;
00821 }
00822
00823 void playlist_delete (int playlist_num)
00824 {
00825 if (playback_get_playing () && playlist_num == playlist_get_playing ())
00826 playback_stop ();
00827
00828 ENTER;
00829 DECLARE_PLAYLIST;
00830 LOOKUP_PLAYLIST;
00831
00832 index_delete (playlists, playlist_num, 1);
00833 playlist_free (playlist);
00834
00835 if (! index_count (playlists))
00836 index_insert (playlists, 0, playlist_new (-1));
00837
00838 number_playlists (playlist_num, index_count (playlists) - playlist_num);
00839
00840 if (playlist == active_playlist)
00841 active_playlist = index_get (playlists, MIN (playlist_num, index_count
00842 (playlists) - 1));
00843 if (playlist == playing_playlist)
00844 playing_playlist = NULL;
00845
00846 PLAYLIST_HAS_CHANGED (-1, 0, 0);
00847 LEAVE;
00848 }
00849
00850 int playlist_get_unique_id (int playlist_num)
00851 {
00852 ENTER;
00853 DECLARE_PLAYLIST;
00854 LOOKUP_PLAYLIST_RET (-1);
00855
00856 int unique_id = playlist->unique_id;
00857
00858 LEAVE_RET (unique_id);
00859 }
00860
00861 int playlist_by_unique_id (int id)
00862 {
00863 ENTER;
00864
00865 Playlist * p = g_hash_table_lookup (unique_id_table, GINT_TO_POINTER (id));
00866 int num = p ? p->number : -1;
00867
00868 LEAVE_RET (num);
00869 }
00870
00871 void playlist_set_filename (int playlist_num, const char * filename)
00872 {
00873 ENTER;
00874 DECLARE_PLAYLIST;
00875 LOOKUP_PLAYLIST;
00876
00877 str_unref (playlist->filename);
00878 playlist->filename = str_get (filename);
00879 playlist->modified = TRUE;
00880
00881 METADATA_HAS_CHANGED (-1, 0, 0);
00882 LEAVE;
00883 }
00884
00885 char * playlist_get_filename (int playlist_num)
00886 {
00887 ENTER;
00888 DECLARE_PLAYLIST;
00889 LOOKUP_PLAYLIST_RET (NULL);
00890
00891 char * filename = str_ref (playlist->filename);
00892
00893 LEAVE_RET (filename);
00894 }
00895
00896 void playlist_set_title (int playlist_num, const char * title)
00897 {
00898 ENTER;
00899 DECLARE_PLAYLIST;
00900 LOOKUP_PLAYLIST;
00901
00902 str_unref (playlist->title);
00903 playlist->title = str_get (title);
00904 playlist->modified = TRUE;
00905
00906 METADATA_HAS_CHANGED (-1, 0, 0);
00907 LEAVE;
00908 }
00909
00910 char * playlist_get_title (int playlist_num)
00911 {
00912 ENTER;
00913 DECLARE_PLAYLIST;
00914 LOOKUP_PLAYLIST_RET (NULL);
00915
00916 char * title = str_ref (playlist->title);
00917
00918 LEAVE_RET (title);
00919 }
00920
00921 void playlist_set_modified (int playlist_num, bool_t modified)
00922 {
00923 ENTER;
00924 DECLARE_PLAYLIST;
00925 LOOKUP_PLAYLIST;
00926
00927 playlist->modified = modified;
00928
00929 LEAVE;
00930 }
00931
00932 bool_t playlist_get_modified (int playlist_num)
00933 {
00934 ENTER;
00935 DECLARE_PLAYLIST;
00936 LOOKUP_PLAYLIST_RET (FALSE);
00937
00938 bool_t modified = playlist->modified;
00939
00940 LEAVE_RET (modified);
00941 }
00942
00943 void playlist_set_active (int playlist_num)
00944 {
00945 ENTER;
00946 DECLARE_PLAYLIST;
00947 LOOKUP_PLAYLIST;
00948
00949 bool_t changed = FALSE;
00950
00951 if (playlist != active_playlist)
00952 {
00953 changed = TRUE;
00954 active_playlist = playlist;
00955 }
00956
00957 LEAVE;
00958
00959 if (changed)
00960 hook_call ("playlist activate", NULL);
00961 }
00962
00963 int playlist_get_active (void)
00964 {
00965 ENTER;
00966 int list = active_playlist ? active_playlist->number : -1;
00967 LEAVE_RET (list);
00968 }
00969
00970 void playlist_set_playing (int playlist_num)
00971 {
00972 if (playback_get_playing ())
00973 playback_stop ();
00974
00975 ENTER;
00976 DECLARE_PLAYLIST;
00977
00978 if (playlist_num < 0)
00979 playlist = NULL;
00980 else
00981 LOOKUP_PLAYLIST;
00982
00983 playing_playlist = playlist;
00984
00985 LEAVE;
00986
00987 hook_call ("playlist set playing", NULL);
00988 }
00989
00990 int playlist_get_playing (void)
00991 {
00992 ENTER;
00993 int list = playing_playlist ? playing_playlist->number: -1;
00994 LEAVE_RET (list);
00995 }
00996
00997 int playlist_get_blank (void)
00998 {
00999 int list = playlist_get_active ();
01000 char * title = playlist_get_title (list);
01001
01002 if (strcmp (title, _(default_title)) || playlist_entry_count (list) > 0)
01003 {
01004 list = playlist_count ();
01005 playlist_insert (list);
01006 }
01007
01008 str_unref (title);
01009 return list;
01010 }
01011
01012 int playlist_get_temporary (void)
01013 {
01014 int list, count = playlist_count ();
01015 bool_t found = FALSE;
01016
01017 for (list = 0; list < count; list ++)
01018 {
01019 char * title = playlist_get_title (list);
01020 found = ! strcmp (title, _(temp_title));
01021 str_unref (title);
01022
01023 if (found)
01024 break;
01025 }
01026
01027 if (! found)
01028 {
01029 list = playlist_get_blank ();
01030 playlist_set_title (list, _(temp_title));
01031 }
01032
01033 return list;
01034 }
01035
01036
01037
01038 static void set_position (Playlist * playlist, Entry * entry)
01039 {
01040 if (entry == playlist->position)
01041 return;
01042
01043 playlist->position = entry;
01044
01045 if (! entry)
01046 return;
01047
01048 if (! entry->shuffle_num || entry->shuffle_num != playlist->last_shuffle_num)
01049 {
01050 playlist->last_shuffle_num ++;
01051 entry->shuffle_num = playlist->last_shuffle_num;
01052 }
01053 }
01054
01055 int playlist_entry_count (int playlist_num)
01056 {
01057 ENTER;
01058 DECLARE_PLAYLIST;
01059 LOOKUP_PLAYLIST_RET (0);
01060
01061 int count = index_count (playlist->entries);
01062
01063 LEAVE_RET (count);
01064 }
01065
01066 void playlist_entry_insert_batch_raw (int playlist_num, int at,
01067 Index * filenames, Index * tuples, Index * decoders)
01068 {
01069 ENTER;
01070 DECLARE_PLAYLIST;
01071 LOOKUP_PLAYLIST;
01072
01073 int entries = index_count (playlist->entries);
01074
01075 if (at < 0 || at > entries)
01076 at = entries;
01077
01078 int number = index_count (filenames);
01079
01080 Index * add = index_new ();
01081 index_allocate (add, number);
01082
01083 for (int i = 0; i < number; i ++)
01084 {
01085 char * filename = index_get (filenames, i);
01086 Tuple * tuple = tuples ? index_get (tuples, i) : NULL;
01087 PluginHandle * decoder = decoders ? index_get (decoders, i) : NULL;
01088 index_append (add, entry_new (filename, tuple, decoder));
01089 }
01090
01091 index_free (filenames);
01092 if (decoders)
01093 index_free (decoders);
01094 if (tuples)
01095 index_free (tuples);
01096
01097 number = index_count (add);
01098 index_merge_insert (playlist->entries, at, add);
01099 index_free (add);
01100
01101 number_entries (playlist, at, entries + number - at);
01102
01103 for (int count = 0; count < number; count ++)
01104 {
01105 Entry * entry = index_get (playlist->entries, at + count);
01106 playlist->total_length += entry->length;
01107 }
01108
01109 PLAYLIST_HAS_CHANGED (playlist->number, at, number);
01110 LEAVE;
01111 }
01112
01113 void playlist_entry_delete (int playlist_num, int at, int number)
01114 {
01115 if (playback_get_playing () && playlist_num == playlist_get_playing () &&
01116 playlist_get_position (playlist_num) >= at && playlist_get_position
01117 (playlist_num) < at + number)
01118 playback_stop ();
01119
01120 ENTER;
01121 DECLARE_PLAYLIST;
01122 LOOKUP_PLAYLIST;
01123
01124 int entries = index_count (playlist->entries);
01125
01126 if (at < 0 || at > entries)
01127 at = entries;
01128 if (number < 0 || number > entries - at)
01129 number = entries - at;
01130
01131 if (playlist->position && playlist->position->number >= at &&
01132 playlist->position->number < at + number)
01133 set_position (playlist, NULL);
01134
01135 for (int count = 0; count < number; count ++)
01136 {
01137 Entry * entry = index_get (playlist->entries, at + count);
01138
01139 if (entry->queued)
01140 playlist->queued = g_list_remove (playlist->queued, entry);
01141
01142 if (entry->selected)
01143 {
01144 playlist->selected_count --;
01145 playlist->selected_length -= entry->length;
01146 }
01147
01148 playlist->total_length -= entry->length;
01149 entry_free (entry);
01150 }
01151
01152 index_delete (playlist->entries, at, number);
01153 number_entries (playlist, at, entries - at - number);
01154
01155 PLAYLIST_HAS_CHANGED (playlist->number, at, 0);
01156 LEAVE;
01157 }
01158
01159 char * playlist_entry_get_filename (int playlist_num, int entry_num)
01160 {
01161 ENTER;
01162 DECLARE_PLAYLIST_ENTRY;
01163 LOOKUP_PLAYLIST_ENTRY_RET (NULL);
01164
01165 char * filename = str_ref (entry->filename);
01166
01167 LEAVE_RET (filename);
01168 }
01169
01170 PluginHandle * playlist_entry_get_decoder (int playlist_num, int entry_num, bool_t fast)
01171 {
01172 ENTER;
01173
01174 Entry * entry = get_entry (playlist_num, entry_num, ! fast, FALSE);
01175 PluginHandle * decoder = entry ? entry->decoder : NULL;
01176
01177 LEAVE_RET (decoder);
01178 }
01179
01180 Tuple * playlist_entry_get_tuple (int playlist_num, int entry_num, bool_t fast)
01181 {
01182 ENTER;
01183
01184 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01185 Tuple * tuple = entry ? entry->tuple : NULL;
01186
01187 if (tuple)
01188 tuple_ref (tuple);
01189
01190 LEAVE_RET (tuple);
01191 }
01192
01193 char * playlist_entry_get_title (int playlist_num, int entry_num, bool_t fast)
01194 {
01195 ENTER;
01196
01197 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01198 char * title = entry ? str_ref (entry->formatted ? entry->formatted : entry->filename) : NULL;
01199
01200 LEAVE_RET (title);
01201 }
01202
01203 void playlist_entry_describe (int playlist_num, int entry_num,
01204 char * * title, char * * artist, char * * album, bool_t fast)
01205 {
01206 ENTER;
01207
01208 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01209 * title = (entry && entry->title) ? str_ref (entry->title) : NULL;
01210 * artist = (entry && entry->artist) ? str_ref (entry->artist) : NULL;
01211 * album = (entry && entry->album) ? str_ref (entry->album) : NULL;
01212
01213 LEAVE;
01214 }
01215
01216 int playlist_entry_get_length (int playlist_num, int entry_num, bool_t fast)
01217 {
01218 ENTER;
01219
01220 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01221 int length = entry ? entry->length : 0;
01222
01223 LEAVE_RET (length);
01224 }
01225
01226 void playlist_set_position (int playlist_num, int entry_num)
01227 {
01228 if (playback_get_playing () && playlist_num == playlist_get_playing ())
01229 playback_stop ();
01230
01231 ENTER;
01232 DECLARE_PLAYLIST_ENTRY;
01233
01234 if (entry_num == -1)
01235 {
01236 LOOKUP_PLAYLIST;
01237 entry = NULL;
01238 }
01239 else
01240 LOOKUP_PLAYLIST_ENTRY;
01241
01242 set_position (playlist, entry);
01243 LEAVE;
01244
01245 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
01246 }
01247
01248 int playlist_get_position (int playlist_num)
01249 {
01250 ENTER;
01251 DECLARE_PLAYLIST;
01252 LOOKUP_PLAYLIST_RET (-1);
01253
01254 int position = playlist->position ? playlist->position->number : -1;
01255
01256 LEAVE_RET (position);
01257 }
01258
01259 void playlist_entry_set_selected (int playlist_num, int entry_num,
01260 bool_t selected)
01261 {
01262 ENTER;
01263 DECLARE_PLAYLIST_ENTRY;
01264 LOOKUP_PLAYLIST_ENTRY;
01265
01266 if (entry->selected == selected)
01267 LEAVE_RET_VOID;
01268
01269 entry->selected = selected;
01270
01271 if (selected)
01272 {
01273 playlist->selected_count++;
01274 playlist->selected_length += entry->length;
01275 }
01276 else
01277 {
01278 playlist->selected_count--;
01279 playlist->selected_length -= entry->length;
01280 }
01281
01282 SELECTION_HAS_CHANGED (playlist->number, entry_num, 1);
01283 LEAVE;
01284 }
01285
01286 bool_t playlist_entry_get_selected (int playlist_num, int entry_num)
01287 {
01288 ENTER;
01289 DECLARE_PLAYLIST_ENTRY;
01290 LOOKUP_PLAYLIST_ENTRY_RET (FALSE);
01291
01292 bool_t selected = entry->selected;
01293
01294 LEAVE_RET (selected);
01295 }
01296
01297 int playlist_selected_count (int playlist_num)
01298 {
01299 ENTER;
01300 DECLARE_PLAYLIST;
01301 LOOKUP_PLAYLIST_RET (0);
01302
01303 int selected_count = playlist->selected_count;
01304
01305 LEAVE_RET (selected_count);
01306 }
01307
01308 void playlist_select_all (int playlist_num, bool_t selected)
01309 {
01310 ENTER;
01311 DECLARE_PLAYLIST;
01312 LOOKUP_PLAYLIST;
01313
01314 int entries = index_count (playlist->entries);
01315 int first = entries, last = 0;
01316
01317 for (int count = 0; count < entries; count ++)
01318 {
01319 Entry * entry = index_get (playlist->entries, count);
01320
01321 if ((selected && ! entry->selected) || (entry->selected && ! selected))
01322 {
01323 entry->selected = selected;
01324 first = MIN (first, entry->number);
01325 last = entry->number;
01326 }
01327 }
01328
01329 if (selected)
01330 {
01331 playlist->selected_count = entries;
01332 playlist->selected_length = playlist->total_length;
01333 }
01334 else
01335 {
01336 playlist->selected_count = 0;
01337 playlist->selected_length = 0;
01338 }
01339
01340 if (first < entries)
01341 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01342
01343 LEAVE;
01344 }
01345
01346 int playlist_shift (int playlist_num, int entry_num, int distance)
01347 {
01348 ENTER;
01349 DECLARE_PLAYLIST_ENTRY;
01350 LOOKUP_PLAYLIST_ENTRY_RET (0);
01351
01352 if (! entry->selected || ! distance)
01353 LEAVE_RET (0);
01354
01355 int entries = index_count (playlist->entries);
01356 int shift = 0, center, top, bottom;
01357
01358 if (distance < 0)
01359 {
01360 for (center = entry_num; center > 0 && shift > distance; )
01361 {
01362 entry = index_get (playlist->entries, -- center);
01363 if (! entry->selected)
01364 shift --;
01365 }
01366 }
01367 else
01368 {
01369 for (center = entry_num + 1; center < entries && shift < distance; )
01370 {
01371 entry = index_get (playlist->entries, center ++);
01372 if (! entry->selected)
01373 shift ++;
01374 }
01375 }
01376
01377 top = bottom = center;
01378
01379 for (int i = 0; i < top; i ++)
01380 {
01381 entry = index_get (playlist->entries, i);
01382 if (entry->selected)
01383 top = i;
01384 }
01385
01386 for (int i = entries; i > bottom; i --)
01387 {
01388 entry = index_get (playlist->entries, i - 1);
01389 if (entry->selected)
01390 bottom = i;
01391 }
01392
01393 Index * temp = index_new ();
01394
01395 for (int i = top; i < center; i ++)
01396 {
01397 entry = index_get (playlist->entries, i);
01398 if (! entry->selected)
01399 index_append (temp, entry);
01400 }
01401
01402 for (int i = top; i < bottom; i ++)
01403 {
01404 entry = index_get (playlist->entries, i);
01405 if (entry->selected)
01406 index_append (temp, entry);
01407 }
01408
01409 for (int i = center; i < bottom; i ++)
01410 {
01411 entry = index_get (playlist->entries, i);
01412 if (! entry->selected)
01413 index_append (temp, entry);
01414 }
01415
01416 index_copy_set (temp, 0, playlist->entries, top, bottom - top);
01417
01418 number_entries (playlist, top, bottom - top);
01419 PLAYLIST_HAS_CHANGED (playlist->number, top, bottom - top);
01420
01421 LEAVE_RET (shift);
01422 }
01423
01424 void playlist_delete_selected (int playlist_num)
01425 {
01426 if (playback_get_playing () && playlist_num == playlist_get_playing () &&
01427 playlist_get_position (playlist_num) >= 0 && playlist_entry_get_selected
01428 (playlist_num, playlist_get_position (playlist_num)))
01429 playback_stop ();
01430
01431 ENTER;
01432 DECLARE_PLAYLIST;
01433 LOOKUP_PLAYLIST;
01434
01435 if (! playlist->selected_count)
01436 LEAVE_RET_VOID;
01437
01438 int entries = index_count (playlist->entries);
01439
01440 Index * others = index_new ();
01441 index_allocate (others, entries - playlist->selected_count);
01442
01443 if (playlist->position && playlist->position->selected)
01444 set_position (playlist, NULL);
01445
01446 int before = 0, after = 0;
01447 bool_t found = FALSE;
01448
01449 for (int count = 0; count < entries; count++)
01450 {
01451 Entry * entry = index_get (playlist->entries, count);
01452
01453 if (entry->selected)
01454 {
01455 if (entry->queued)
01456 playlist->queued = g_list_remove (playlist->queued, entry);
01457
01458 playlist->total_length -= entry->length;
01459 entry_free (entry);
01460
01461 found = TRUE;
01462 after = 0;
01463 }
01464 else
01465 {
01466 index_append (others, entry);
01467
01468 if (found)
01469 after ++;
01470 else
01471 before ++;
01472 }
01473 }
01474
01475 index_free (playlist->entries);
01476 playlist->entries = others;
01477
01478 playlist->selected_count = 0;
01479 playlist->selected_length = 0;
01480
01481 number_entries (playlist, before, index_count (playlist->entries) - before);
01482 PLAYLIST_HAS_CHANGED (playlist->number, before, index_count
01483 (playlist->entries) - after - before);
01484 LEAVE;
01485 }
01486
01487 void playlist_reverse (int playlist_num)
01488 {
01489 ENTER;
01490 DECLARE_PLAYLIST;
01491 LOOKUP_PLAYLIST;
01492
01493 int entries = index_count (playlist->entries);
01494
01495 Index * reversed = index_new ();
01496 index_allocate (reversed, entries);
01497
01498 for (int count = entries; count --; )
01499 index_append (reversed, index_get (playlist->entries, count));
01500
01501 index_free (playlist->entries);
01502 playlist->entries = reversed;
01503
01504 number_entries (playlist, 0, entries);
01505 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries);
01506 LEAVE;
01507 }
01508
01509 void playlist_randomize (int playlist_num)
01510 {
01511 ENTER;
01512 DECLARE_PLAYLIST;
01513 LOOKUP_PLAYLIST;
01514
01515 int entries = index_count (playlist->entries);
01516
01517 for (int i = 0; i < entries; i ++)
01518 {
01519 int j = i + rand () % (entries - i);
01520
01521 struct entry * entry = index_get (playlist->entries, j);
01522 index_set (playlist->entries, j, index_get (playlist->entries, i));
01523 index_set (playlist->entries, i, entry);
01524 }
01525
01526 number_entries (playlist, 0, entries);
01527 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries);
01528 LEAVE;
01529 }
01530
01531 static int filename_compare (const void * _a, const void * _b, void * compare)
01532 {
01533 const Entry * a = _a, * b = _b;
01534
01535 int diff = ((int (*) (const char * a, const char * b)) compare)
01536 (a->filename, b->filename);
01537
01538 if (diff)
01539 return diff;
01540
01541
01542 return a->number - b->number;
01543 }
01544
01545 static int tuple_compare (const void * _a, const void * _b, void * compare)
01546 {
01547 const Entry * a = _a, * b = _b;
01548
01549 if (! a->tuple)
01550 return b->tuple ? -1 : 0;
01551 if (! b->tuple)
01552 return 1;
01553
01554 int diff = ((int (*) (const Tuple * a, const Tuple * b)) compare)
01555 (a->tuple, b->tuple);
01556
01557 if (diff)
01558 return diff;
01559
01560
01561 return a->number - b->number;
01562 }
01563
01564 static int title_compare (const void * _a, const void * _b, void * compare)
01565 {
01566 const Entry * a = _a, * b = _b;
01567
01568 int diff = ((int (*) (const char * a, const char * b)) compare)
01569 (a->formatted ? a->formatted : a->filename,
01570 b->formatted ? b->formatted : b->filename);
01571
01572 if (diff)
01573 return diff;
01574
01575
01576 return a->number - b->number;
01577 }
01578
01579 static void sort (Playlist * playlist, int (* compare) (const void * a,
01580 const void * b, void * inner), void * inner)
01581 {
01582 index_sort_with_data (playlist->entries, compare, inner);
01583 number_entries (playlist, 0, index_count (playlist->entries));
01584
01585 PLAYLIST_HAS_CHANGED (playlist->number, 0, index_count (playlist->entries));
01586 }
01587
01588 static void sort_selected (Playlist * playlist, int (* compare) (const void *
01589 a, const void * b, void * inner), void * inner)
01590 {
01591 int entries = index_count (playlist->entries);
01592
01593 Index * selected = index_new ();
01594 index_allocate (selected, playlist->selected_count);
01595
01596 for (int count = 0; count < entries; count++)
01597 {
01598 Entry * entry = index_get (playlist->entries, count);
01599 if (entry->selected)
01600 index_append (selected, entry);
01601 }
01602
01603 index_sort_with_data (selected, compare, inner);
01604
01605 int count2 = 0;
01606 for (int count = 0; count < entries; count++)
01607 {
01608 Entry * entry = index_get (playlist->entries, count);
01609 if (entry->selected)
01610 index_set (playlist->entries, count, index_get (selected, count2 ++));
01611 }
01612
01613 index_free (selected);
01614
01615 number_entries (playlist, 0, entries);
01616 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries);
01617 }
01618
01619 static bool_t entries_are_scanned (Playlist * playlist, bool_t selected)
01620 {
01621 int entries = index_count (playlist->entries);
01622 for (int count = 0; count < entries; count ++)
01623 {
01624 Entry * entry = index_get (playlist->entries, count);
01625 if (selected && ! entry->selected)
01626 continue;
01627
01628 if (! entry->tuple)
01629 {
01630 interface_show_error (_("The playlist cannot be sorted because "
01631 "metadata scanning is still in progress (or has been disabled)."));
01632 return FALSE;
01633 }
01634 }
01635
01636 return TRUE;
01637 }
01638
01639 void playlist_sort_by_filename (int playlist_num, int (* compare)
01640 (const char * a, const char * b))
01641 {
01642 ENTER;
01643 DECLARE_PLAYLIST;
01644 LOOKUP_PLAYLIST;
01645
01646 sort (playlist, filename_compare, (void *) compare);
01647
01648 LEAVE;
01649 }
01650
01651 void playlist_sort_by_tuple (int playlist_num, int (* compare)
01652 (const Tuple * a, const Tuple * b))
01653 {
01654 ENTER;
01655 DECLARE_PLAYLIST;
01656 LOOKUP_PLAYLIST;
01657
01658 if (entries_are_scanned (playlist, FALSE))
01659 sort (playlist, tuple_compare, (void *) compare);
01660
01661 LEAVE;
01662 }
01663
01664 void playlist_sort_by_title (int playlist_num, int (* compare) (const char *
01665 a, const char * b))
01666 {
01667 ENTER;
01668 DECLARE_PLAYLIST;
01669 LOOKUP_PLAYLIST;
01670
01671 if (entries_are_scanned (playlist, FALSE))
01672 sort (playlist, title_compare, (void *) compare);
01673
01674 LEAVE;
01675 }
01676
01677 void playlist_sort_selected_by_filename (int playlist_num, int (* compare)
01678 (const char * a, const char * b))
01679 {
01680 ENTER;
01681 DECLARE_PLAYLIST;
01682 LOOKUP_PLAYLIST;
01683
01684 sort_selected (playlist, filename_compare, (void *) compare);
01685
01686 LEAVE;
01687 }
01688
01689 void playlist_sort_selected_by_tuple (int playlist_num, int (* compare)
01690 (const Tuple * a, const Tuple * b))
01691 {
01692 ENTER;
01693 DECLARE_PLAYLIST;
01694 LOOKUP_PLAYLIST;
01695
01696 if (entries_are_scanned (playlist, TRUE))
01697 sort_selected (playlist, tuple_compare, (void *) compare);
01698
01699 LEAVE;
01700 }
01701
01702 void playlist_sort_selected_by_title (int playlist_num, int (* compare)
01703 (const char * a, const char * b))
01704 {
01705 ENTER;
01706 DECLARE_PLAYLIST;
01707 LOOKUP_PLAYLIST;
01708
01709 if (entries_are_scanned (playlist, TRUE))
01710 sort (playlist, title_compare, (void *) compare);
01711
01712 LEAVE;
01713 }
01714
01715 void playlist_reformat_titles (void)
01716 {
01717 ENTER;
01718
01719 g_free (title_format);
01720 title_format = NULL;
01721
01722 for (int playlist_num = 0; playlist_num < index_count (playlists);
01723 playlist_num ++)
01724 {
01725 Playlist * playlist = index_get (playlists, playlist_num);
01726 int entries = index_count (playlist->entries);
01727
01728 for (int count = 0; count < entries; count++)
01729 {
01730 Entry * entry = index_get (playlist->entries, count);
01731 str_unref (entry->formatted);
01732 entry->formatted = entry->tuple ? title_from_tuple (entry->tuple) : NULL;
01733 }
01734
01735 METADATA_HAS_CHANGED (playlist_num, 0, entries);
01736 }
01737
01738 LEAVE;
01739 }
01740
01741 void playlist_trigger_scan (void)
01742 {
01743 ENTER;
01744
01745 for (int i = 0; i < index_count (playlists); i ++)
01746 {
01747 Playlist * p = index_get (playlists, i);
01748 p->scanning = TRUE;
01749 }
01750
01751 scan_trigger ();
01752
01753 LEAVE;
01754 }
01755
01756 static void playlist_rescan_real (int playlist_num, bool_t selected)
01757 {
01758 ENTER;
01759 DECLARE_PLAYLIST;
01760 LOOKUP_PLAYLIST;
01761
01762 int entries = index_count (playlist->entries);
01763
01764 for (int count = 0; count < entries; count ++)
01765 {
01766 Entry * entry = index_get (playlist->entries, count);
01767 if (! selected || entry->selected)
01768 {
01769 entry_set_tuple (playlist, entry, NULL);
01770 entry->failed = FALSE;
01771 }
01772 }
01773
01774 METADATA_HAS_CHANGED (playlist->number, 0, entries);
01775 LEAVE;
01776 }
01777
01778 void playlist_rescan (int playlist_num)
01779 {
01780 playlist_rescan_real (playlist_num, FALSE);
01781 }
01782
01783 void playlist_rescan_selected (int playlist_num)
01784 {
01785 playlist_rescan_real (playlist_num, TRUE);
01786 }
01787
01788 void playlist_rescan_file (const char * filename)
01789 {
01790 ENTER;
01791
01792 int num_playlists = index_count (playlists);
01793
01794 for (int playlist_num = 0; playlist_num < num_playlists; playlist_num ++)
01795 {
01796 Playlist * playlist = index_get (playlists, playlist_num);
01797 int num_entries = index_count (playlist->entries);
01798
01799 for (int entry_num = 0; entry_num < num_entries; entry_num ++)
01800 {
01801 Entry * entry = index_get (playlist->entries, entry_num);
01802
01803 if (! strcmp (entry->filename, filename))
01804 {
01805 entry_set_tuple (playlist, entry, NULL);
01806 entry->failed = FALSE;
01807
01808 METADATA_HAS_CHANGED (playlist_num, entry_num, 1);
01809 }
01810 }
01811 }
01812
01813 LEAVE;
01814 }
01815
01816 int64_t playlist_get_total_length (int playlist_num)
01817 {
01818 ENTER;
01819 DECLARE_PLAYLIST;
01820 LOOKUP_PLAYLIST_RET (0);
01821
01822 int64_t length = playlist->total_length;
01823
01824 LEAVE_RET (length);
01825 }
01826
01827 int64_t playlist_get_selected_length (int playlist_num)
01828 {
01829 ENTER;
01830 DECLARE_PLAYLIST;
01831 LOOKUP_PLAYLIST_RET (0);
01832
01833 int64_t length = playlist->selected_length;
01834
01835 LEAVE_RET (length);
01836 }
01837
01838 int playlist_queue_count (int playlist_num)
01839 {
01840 ENTER;
01841 DECLARE_PLAYLIST;
01842 LOOKUP_PLAYLIST_RET (0);
01843
01844 int count = g_list_length (playlist->queued);
01845
01846 LEAVE_RET (count);
01847 }
01848
01849 void playlist_queue_insert (int playlist_num, int at, int entry_num)
01850 {
01851 ENTER;
01852 DECLARE_PLAYLIST_ENTRY;
01853 LOOKUP_PLAYLIST_ENTRY;
01854
01855 if (entry->queued)
01856 LEAVE_RET_VOID;
01857
01858 if (at < 0)
01859 playlist->queued = g_list_append (playlist->queued, entry);
01860 else
01861 playlist->queued = g_list_insert (playlist->queued, entry, at);
01862
01863 entry->queued = TRUE;
01864
01865 SELECTION_HAS_CHANGED (playlist->number, entry_num, 1);
01866 LEAVE;
01867 }
01868
01869 void playlist_queue_insert_selected (int playlist_num, int at)
01870 {
01871 ENTER;
01872 DECLARE_PLAYLIST;
01873 LOOKUP_PLAYLIST;
01874
01875 int entries = index_count(playlist->entries);
01876 int first = entries, last = 0;
01877
01878 for (int count = 0; count < entries; count++)
01879 {
01880 Entry * entry = index_get (playlist->entries, count);
01881
01882 if (! entry->selected || entry->queued)
01883 continue;
01884
01885 if (at < 0)
01886 playlist->queued = g_list_append (playlist->queued, entry);
01887 else
01888 playlist->queued = g_list_insert (playlist->queued, entry, at++);
01889
01890 entry->queued = TRUE;
01891 first = MIN (first, entry->number);
01892 last = entry->number;
01893 }
01894
01895 if (first < entries)
01896 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01897
01898 LEAVE;
01899 }
01900
01901 int playlist_queue_get_entry (int playlist_num, int at)
01902 {
01903 ENTER;
01904 DECLARE_PLAYLIST;
01905 LOOKUP_PLAYLIST_RET (-1);
01906
01907 GList * node = g_list_nth (playlist->queued, at);
01908 int entry_num = node ? ((Entry *) node->data)->number : -1;
01909
01910 LEAVE_RET (entry_num);
01911 }
01912
01913 int playlist_queue_find_entry (int playlist_num, int entry_num)
01914 {
01915 ENTER;
01916 DECLARE_PLAYLIST_ENTRY;
01917 LOOKUP_PLAYLIST_ENTRY_RET (-1);
01918
01919 int pos = entry->queued ? g_list_index (playlist->queued, entry) : -1;
01920
01921 LEAVE_RET (pos);
01922 }
01923
01924 void playlist_queue_delete (int playlist_num, int at, int number)
01925 {
01926 ENTER;
01927 DECLARE_PLAYLIST;
01928 LOOKUP_PLAYLIST;
01929
01930 int entries = index_count (playlist->entries);
01931 int first = entries, last = 0;
01932
01933 if (at == 0)
01934 {
01935 while (playlist->queued && number --)
01936 {
01937 Entry * entry = playlist->queued->data;
01938 entry->queued = FALSE;
01939 first = MIN (first, entry->number);
01940 last = entry->number;
01941
01942 playlist->queued = g_list_delete_link (playlist->queued,
01943 playlist->queued);
01944 }
01945 }
01946 else
01947 {
01948 GList * anchor = g_list_nth (playlist->queued, at - 1);
01949 if (! anchor)
01950 goto DONE;
01951
01952 while (anchor->next && number --)
01953 {
01954 Entry * entry = anchor->next->data;
01955 entry->queued = FALSE;
01956 first = MIN (first, entry->number);
01957 last = entry->number;
01958
01959 playlist->queued = g_list_delete_link (playlist->queued,
01960 anchor->next);
01961 }
01962 }
01963
01964 DONE:
01965 if (first < entries)
01966 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01967
01968 LEAVE;
01969 }
01970
01971 void playlist_queue_delete_selected (int playlist_num)
01972 {
01973 ENTER;
01974 DECLARE_PLAYLIST;
01975 LOOKUP_PLAYLIST;
01976
01977 int entries = index_count (playlist->entries);
01978 int first = entries, last = 0;
01979
01980 for (GList * node = playlist->queued; node; )
01981 {
01982 GList * next = node->next;
01983 Entry * entry = node->data;
01984
01985 if (entry->selected)
01986 {
01987 entry->queued = FALSE;
01988 playlist->queued = g_list_delete_link (playlist->queued, node);
01989 first = MIN (first, entry->number);
01990 last = entry->number;
01991 }
01992
01993 node = next;
01994 }
01995
01996 if (first < entries)
01997 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01998
01999 LEAVE;
02000 }
02001
02002 static bool_t shuffle_prev (Playlist * playlist)
02003 {
02004 int entries = index_count (playlist->entries);
02005 Entry * found = NULL;
02006
02007 for (int count = 0; count < entries; count ++)
02008 {
02009 Entry * entry = index_get (playlist->entries, count);
02010
02011 if (entry->shuffle_num && (! playlist->position ||
02012 entry->shuffle_num < playlist->position->shuffle_num) && (! found
02013 || entry->shuffle_num > found->shuffle_num))
02014 found = entry;
02015 }
02016
02017 if (! found)
02018 return FALSE;
02019
02020 playlist->position = found;
02021 return TRUE;
02022 }
02023
02024 bool_t playlist_prev_song (int playlist_num)
02025 {
02026 if (playback_get_playing () && playlist_num == playlist_get_playing ())
02027 playback_stop ();
02028
02029 ENTER;
02030 DECLARE_PLAYLIST;
02031 LOOKUP_PLAYLIST_RET (FALSE);
02032
02033 if (get_bool (NULL, "shuffle"))
02034 {
02035 if (! shuffle_prev (playlist))
02036 LEAVE_RET (FALSE);
02037 }
02038 else
02039 {
02040 if (! playlist->position || playlist->position->number == 0)
02041 LEAVE_RET (FALSE);
02042
02043 set_position (playlist, index_get (playlist->entries,
02044 playlist->position->number - 1));
02045 }
02046
02047 LEAVE;
02048
02049 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
02050 return TRUE;
02051 }
02052
02053 static bool_t shuffle_next (Playlist * playlist)
02054 {
02055 int entries = index_count (playlist->entries), choice = 0, count;
02056 Entry * found = NULL;
02057
02058 for (count = 0; count < entries; count ++)
02059 {
02060 Entry * entry = index_get (playlist->entries, count);
02061
02062 if (! entry->shuffle_num)
02063 choice ++;
02064 else if (playlist->position && entry->shuffle_num >
02065 playlist->position->shuffle_num && (! found || entry->shuffle_num
02066 < found->shuffle_num))
02067 found = entry;
02068 }
02069
02070 if (found)
02071 {
02072 playlist->position = found;
02073 return TRUE;
02074 }
02075
02076 if (! choice)
02077 return FALSE;
02078
02079 choice = rand () % choice;
02080
02081 for (count = 0; ; count ++)
02082 {
02083 Entry * entry = index_get (playlist->entries, count);
02084
02085 if (! entry->shuffle_num)
02086 {
02087 if (! choice)
02088 {
02089 set_position (playlist, entry);
02090 return TRUE;
02091 }
02092
02093 choice --;
02094 }
02095 }
02096 }
02097
02098 static void shuffle_reset (Playlist * playlist)
02099 {
02100 int entries = index_count (playlist->entries);
02101
02102 playlist->last_shuffle_num = 0;
02103
02104 for (int count = 0; count < entries; count ++)
02105 {
02106 Entry * entry = index_get (playlist->entries, count);
02107 entry->shuffle_num = 0;
02108 }
02109 }
02110
02111 bool_t playlist_next_song (int playlist_num, bool_t repeat)
02112 {
02113 if (playback_get_playing () && playlist_num == playlist_get_playing ())
02114 playback_stop ();
02115
02116 ENTER;
02117 DECLARE_PLAYLIST;
02118 LOOKUP_PLAYLIST_RET (FALSE);
02119
02120 int entries = index_count(playlist->entries);
02121
02122 if (! entries)
02123 LEAVE_RET (FALSE);
02124
02125 if (playlist->queued)
02126 {
02127 set_position (playlist, playlist->queued->data);
02128 playlist->queued = g_list_remove (playlist->queued, playlist->position);
02129 playlist->position->queued = FALSE;
02130 }
02131 else if (get_bool (NULL, "shuffle"))
02132 {
02133 if (! shuffle_next (playlist))
02134 {
02135 if (! repeat)
02136 LEAVE_RET (FALSE);
02137
02138 shuffle_reset (playlist);
02139
02140 if (! shuffle_next (playlist))
02141 LEAVE_RET (FALSE);
02142 }
02143 }
02144 else
02145 {
02146 if (! playlist->position)
02147 set_position (playlist, index_get (playlist->entries, 0));
02148 else if (playlist->position->number == entries - 1)
02149 {
02150 if (! repeat)
02151 LEAVE_RET (FALSE);
02152
02153 set_position (playlist, index_get (playlist->entries, 0));
02154 }
02155 else
02156 set_position (playlist, index_get (playlist->entries,
02157 playlist->position->number + 1));
02158 }
02159
02160 LEAVE;
02161
02162 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
02163 return TRUE;
02164 }
02165
02166 int playback_entry_get_position (void)
02167 {
02168 ENTER;
02169
02170 Entry * entry = get_playback_entry (FALSE, FALSE);
02171 int entry_num = entry ? entry->number : -1;
02172
02173 LEAVE_RET (entry_num);
02174 }
02175
02176 PluginHandle * playback_entry_get_decoder (void)
02177 {
02178 ENTER;
02179
02180 Entry * entry = get_playback_entry (TRUE, FALSE);
02181 PluginHandle * decoder = entry ? entry->decoder : NULL;
02182
02183 LEAVE_RET (decoder);
02184 }
02185
02186 Tuple * playback_entry_get_tuple (void)
02187 {
02188 ENTER;
02189
02190 Entry * entry = get_playback_entry (FALSE, TRUE);
02191 Tuple * tuple = entry ? entry->tuple : NULL;
02192
02193 if (tuple)
02194 tuple_ref (tuple);
02195
02196 LEAVE_RET (tuple);
02197 }
02198
02199 char * playback_entry_get_title (void)
02200 {
02201 ENTER;
02202
02203 Entry * entry = get_playback_entry (FALSE, TRUE);
02204 char * title = entry ? str_ref (entry->formatted ? entry->formatted :
02205 entry->filename) : NULL;
02206
02207 LEAVE_RET (title);
02208 }
02209
02210 int playback_entry_get_length (void)
02211 {
02212 ENTER;
02213
02214 Entry * entry = get_playback_entry (FALSE, TRUE);
02215 int length = entry->length;
02216
02217 LEAVE_RET (length);
02218 }
02219
02220 void playback_entry_set_tuple (Tuple * tuple)
02221 {
02222 ENTER;
02223 if (! playing_playlist || ! playing_playlist->position)
02224 LEAVE_RET_VOID;
02225
02226 Entry * entry = playing_playlist->position;
02227 entry_cancel_scan (entry);
02228 entry_set_tuple (playing_playlist, entry, tuple);
02229
02230 METADATA_HAS_CHANGED (playing_playlist->number, entry->number, 1);
02231 LEAVE;
02232 }
02233
02234 int playback_entry_get_start_time (void)
02235 {
02236 ENTER;
02237 if (! playing_playlist || ! playing_playlist->position)
02238 LEAVE_RET (0);
02239
02240 int start = playing_playlist->position->start;
02241 LEAVE_RET (start);
02242 }
02243
02244 int playback_entry_get_end_time (void)
02245 {
02246 ENTER;
02247 if (! playing_playlist || ! playing_playlist->position)
02248 LEAVE_RET (-1);
02249
02250 int end = playing_playlist->position->end;
02251 LEAVE_RET (end);
02252 }
02253
02254 void playlist_save_state (void)
02255 {
02256 ENTER;
02257
02258 char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
02259 FILE * handle = fopen (path, "w");
02260 g_free (path);
02261 if (! handle)
02262 LEAVE_RET_VOID;
02263
02264 resume_state = playback_get_playing () ? (playback_get_paused () ?
02265 RESUME_PAUSE : RESUME_PLAY) : RESUME_STOP;
02266 resume_time = playback_get_playing () ? playback_get_time () : 0;
02267
02268 fprintf (handle, "resume-state %d\n", resume_state);
02269 fprintf (handle, "resume-time %d\n", resume_time);
02270
02271 fprintf (handle, "active %d\n", active_playlist ? active_playlist->number : -1);
02272 fprintf (handle, "playing %d\n", playing_playlist ? playing_playlist->number : -1);
02273
02274 for (int playlist_num = 0; playlist_num < index_count (playlists);
02275 playlist_num ++)
02276 {
02277 Playlist * playlist = index_get (playlists, playlist_num);
02278
02279 fprintf (handle, "playlist %d\n", playlist_num);
02280
02281 if (playlist->filename)
02282 fprintf (handle, "filename %s\n", playlist->filename);
02283
02284 fprintf (handle, "position %d\n", playlist->position ?
02285 playlist->position->number : -1);
02286 }
02287
02288 fclose (handle);
02289 LEAVE;
02290 }
02291
02292 static char parse_key[512];
02293 static char * parse_value;
02294
02295 static void parse_next (FILE * handle)
02296 {
02297 parse_value = NULL;
02298
02299 if (! fgets (parse_key, sizeof parse_key, handle))
02300 return;
02301
02302 char * space = strchr (parse_key, ' ');
02303 if (! space)
02304 return;
02305
02306 * space = 0;
02307 parse_value = space + 1;
02308
02309 char * newline = strchr (parse_value, '\n');
02310 if (newline)
02311 * newline = 0;
02312 }
02313
02314 static bool_t parse_integer (const char * key, int * value)
02315 {
02316 return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value,
02317 "%d", value) == 1);
02318 }
02319
02320 static char * parse_string (const char * key)
02321 {
02322 return (parse_value && ! strcmp (parse_key, key)) ? str_get (parse_value) : NULL;
02323 }
02324
02325 void playlist_load_state (void)
02326 {
02327 ENTER;
02328 int playlist_num;
02329
02330 char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
02331 FILE * handle = fopen (path, "r");
02332 g_free (path);
02333 if (! handle)
02334 LEAVE_RET_VOID;
02335
02336 parse_next (handle);
02337
02338 if (parse_integer ("resume-state", & resume_state))
02339 parse_next (handle);
02340 if (parse_integer ("resume-time", & resume_time))
02341 parse_next (handle);
02342
02343 if (parse_integer ("active", & playlist_num))
02344 {
02345 if (! (active_playlist = lookup_playlist (playlist_num)))
02346 active_playlist = index_get (playlists, 0);
02347 parse_next (handle);
02348 }
02349
02350 if (parse_integer ("playing", & playlist_num))
02351 {
02352 playing_playlist = lookup_playlist (playlist_num);
02353 parse_next (handle);
02354 }
02355
02356 while (parse_integer ("playlist", & playlist_num) && playlist_num >= 0 &&
02357 playlist_num < index_count (playlists))
02358 {
02359 Playlist * playlist = index_get (playlists, playlist_num);
02360 int entries = index_count (playlist->entries), position;
02361 char * s;
02362
02363 parse_next (handle);
02364
02365 if ((s = parse_string ("filename")))
02366 {
02367 str_unref (playlist->filename);
02368 playlist->filename = s;
02369 parse_next (handle);
02370 }
02371
02372 if (parse_integer ("position", & position))
02373 parse_next (handle);
02374
02375 if (position >= 0 && position < entries)
02376 set_position (playlist, index_get (playlist->entries, position));
02377 }
02378
02379 fclose (handle);
02380
02381
02382
02383 for (int i = 0; i < index_count (playlists); i ++)
02384 {
02385 Playlist * p = index_get (playlists, i);
02386 memset (& p->last_update, 0, sizeof (Update));
02387 memset (& p->next_update, 0, sizeof (Update));
02388 }
02389
02390 update_level = 0;
02391
02392 if (update_source)
02393 {
02394 g_source_remove (update_source);
02395 update_source = 0;
02396 }
02397
02398 LEAVE;
02399 }
02400
02401 void playlist_resume (void)
02402 {
02403 if (resume_state == RESUME_PLAY || resume_state == RESUME_PAUSE)
02404 playback_play (resume_time, resume_state == RESUME_PAUSE);
02405 }