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
00826 if (playback_get_playing () && playlist_num == playlist_get_playing ())
00827 playback_stop ();
00828
00829 ENTER;
00830 DECLARE_PLAYLIST;
00831 LOOKUP_PLAYLIST;
00832
00833 index_delete (playlists, playlist_num, 1);
00834 playlist_free (playlist);
00835
00836 if (! index_count (playlists))
00837 index_insert (playlists, 0, playlist_new (-1));
00838
00839 number_playlists (playlist_num, index_count (playlists) - playlist_num);
00840
00841 if (playlist == active_playlist)
00842 active_playlist = index_get (playlists, MIN (playlist_num, index_count
00843 (playlists) - 1));
00844 if (playlist == playing_playlist)
00845 playing_playlist = NULL;
00846
00847 PLAYLIST_HAS_CHANGED (-1, 0, 0);
00848 LEAVE;
00849 }
00850
00851 int playlist_get_unique_id (int playlist_num)
00852 {
00853 ENTER;
00854 DECLARE_PLAYLIST;
00855 LOOKUP_PLAYLIST_RET (-1);
00856
00857 int unique_id = playlist->unique_id;
00858
00859 LEAVE_RET (unique_id);
00860 }
00861
00862 int playlist_by_unique_id (int id)
00863 {
00864 ENTER;
00865
00866 Playlist * p = g_hash_table_lookup (unique_id_table, GINT_TO_POINTER (id));
00867 int num = p ? p->number : -1;
00868
00869 LEAVE_RET (num);
00870 }
00871
00872 void playlist_set_filename (int playlist_num, const char * filename)
00873 {
00874 ENTER;
00875 DECLARE_PLAYLIST;
00876 LOOKUP_PLAYLIST;
00877
00878 str_unref (playlist->filename);
00879 playlist->filename = str_get (filename);
00880 playlist->modified = TRUE;
00881
00882 METADATA_HAS_CHANGED (-1, 0, 0);
00883 LEAVE;
00884 }
00885
00886 char * playlist_get_filename (int playlist_num)
00887 {
00888 ENTER;
00889 DECLARE_PLAYLIST;
00890 LOOKUP_PLAYLIST_RET (NULL);
00891
00892 char * filename = str_ref (playlist->filename);
00893
00894 LEAVE_RET (filename);
00895 }
00896
00897 void playlist_set_title (int playlist_num, const char * title)
00898 {
00899 ENTER;
00900 DECLARE_PLAYLIST;
00901 LOOKUP_PLAYLIST;
00902
00903 str_unref (playlist->title);
00904 playlist->title = str_get (title);
00905 playlist->modified = TRUE;
00906
00907 METADATA_HAS_CHANGED (-1, 0, 0);
00908 LEAVE;
00909 }
00910
00911 char * playlist_get_title (int playlist_num)
00912 {
00913 ENTER;
00914 DECLARE_PLAYLIST;
00915 LOOKUP_PLAYLIST_RET (NULL);
00916
00917 char * title = str_ref (playlist->title);
00918
00919 LEAVE_RET (title);
00920 }
00921
00922 void playlist_set_modified (int playlist_num, bool_t modified)
00923 {
00924 ENTER;
00925 DECLARE_PLAYLIST;
00926 LOOKUP_PLAYLIST;
00927
00928 playlist->modified = modified;
00929
00930 LEAVE;
00931 }
00932
00933 bool_t playlist_get_modified (int playlist_num)
00934 {
00935 ENTER;
00936 DECLARE_PLAYLIST;
00937 LOOKUP_PLAYLIST_RET (FALSE);
00938
00939 bool_t modified = playlist->modified;
00940
00941 LEAVE_RET (modified);
00942 }
00943
00944 void playlist_set_active (int playlist_num)
00945 {
00946 ENTER;
00947 DECLARE_PLAYLIST;
00948 LOOKUP_PLAYLIST;
00949
00950 bool_t changed = FALSE;
00951
00952 if (playlist != active_playlist)
00953 {
00954 changed = TRUE;
00955 active_playlist = playlist;
00956 }
00957
00958 LEAVE;
00959
00960 if (changed)
00961 hook_call ("playlist activate", NULL);
00962 }
00963
00964 int playlist_get_active (void)
00965 {
00966 ENTER;
00967 int list = active_playlist ? active_playlist->number : -1;
00968 LEAVE_RET (list);
00969 }
00970
00971 void playlist_set_playing (int playlist_num)
00972 {
00973
00974 if (playback_get_playing ())
00975 playback_stop ();
00976
00977 ENTER;
00978 DECLARE_PLAYLIST;
00979
00980 if (playlist_num < 0)
00981 playlist = NULL;
00982 else
00983 LOOKUP_PLAYLIST;
00984
00985 playing_playlist = playlist;
00986
00987 LEAVE;
00988
00989 hook_call ("playlist set playing", NULL);
00990 }
00991
00992 int playlist_get_playing (void)
00993 {
00994 ENTER;
00995 int list = playing_playlist ? playing_playlist->number: -1;
00996 LEAVE_RET (list);
00997 }
00998
00999 int playlist_get_blank (void)
01000 {
01001 int list = playlist_get_active ();
01002 char * title = playlist_get_title (list);
01003
01004 if (strcmp (title, _(default_title)) || playlist_entry_count (list) > 0)
01005 {
01006 list = playlist_count ();
01007 playlist_insert (list);
01008 }
01009
01010 str_unref (title);
01011 return list;
01012 }
01013
01014 int playlist_get_temporary (void)
01015 {
01016 int list, count = playlist_count ();
01017 bool_t found = FALSE;
01018
01019 for (list = 0; list < count; list ++)
01020 {
01021 char * title = playlist_get_title (list);
01022 found = ! strcmp (title, _(temp_title));
01023 str_unref (title);
01024
01025 if (found)
01026 break;
01027 }
01028
01029 if (! found)
01030 {
01031 list = playlist_get_blank ();
01032 playlist_set_title (list, _(temp_title));
01033 }
01034
01035 return list;
01036 }
01037
01038
01039
01040 static void set_position (Playlist * playlist, Entry * entry)
01041 {
01042 if (entry == playlist->position)
01043 return;
01044
01045 playlist->position = entry;
01046
01047 if (! entry)
01048 return;
01049
01050 if (! entry->shuffle_num || entry->shuffle_num != playlist->last_shuffle_num)
01051 {
01052 playlist->last_shuffle_num ++;
01053 entry->shuffle_num = playlist->last_shuffle_num;
01054 }
01055 }
01056
01057 int playlist_entry_count (int playlist_num)
01058 {
01059 ENTER;
01060 DECLARE_PLAYLIST;
01061 LOOKUP_PLAYLIST_RET (0);
01062
01063 int count = index_count (playlist->entries);
01064
01065 LEAVE_RET (count);
01066 }
01067
01068 void playlist_entry_insert_batch_raw (int playlist_num, int at,
01069 Index * filenames, Index * tuples, Index * decoders)
01070 {
01071 ENTER;
01072 DECLARE_PLAYLIST;
01073 LOOKUP_PLAYLIST;
01074
01075 int entries = index_count (playlist->entries);
01076
01077 if (at < 0 || at > entries)
01078 at = entries;
01079
01080 int number = index_count (filenames);
01081
01082 Index * add = index_new ();
01083 index_allocate (add, number);
01084
01085 for (int i = 0; i < number; i ++)
01086 {
01087 char * filename = index_get (filenames, i);
01088 Tuple * tuple = tuples ? index_get (tuples, i) : NULL;
01089 PluginHandle * decoder = decoders ? index_get (decoders, i) : NULL;
01090 index_append (add, entry_new (filename, tuple, decoder));
01091 }
01092
01093 index_free (filenames);
01094 if (decoders)
01095 index_free (decoders);
01096 if (tuples)
01097 index_free (tuples);
01098
01099 number = index_count (add);
01100 index_merge_insert (playlist->entries, at, add);
01101 index_free (add);
01102
01103 number_entries (playlist, at, entries + number - at);
01104
01105 for (int count = 0; count < number; count ++)
01106 {
01107 Entry * entry = index_get (playlist->entries, at + count);
01108 playlist->total_length += entry->length;
01109 }
01110
01111 PLAYLIST_HAS_CHANGED (playlist->number, at, number);
01112 LEAVE;
01113 }
01114
01115 void playlist_entry_delete (int playlist_num, int at, int number)
01116 {
01117
01118 if (playback_get_playing () && playlist_num == playlist_get_playing () &&
01119 playlist_get_position (playlist_num) >= at && playlist_get_position
01120 (playlist_num) < at + number)
01121 playback_stop ();
01122
01123 ENTER;
01124 DECLARE_PLAYLIST;
01125 LOOKUP_PLAYLIST;
01126
01127 int entries = index_count (playlist->entries);
01128
01129 if (at < 0 || at > entries)
01130 at = entries;
01131 if (number < 0 || number > entries - at)
01132 number = entries - at;
01133
01134 if (playlist->position && playlist->position->number >= at &&
01135 playlist->position->number < at + number)
01136 set_position (playlist, NULL);
01137
01138 for (int count = 0; count < number; count ++)
01139 {
01140 Entry * entry = index_get (playlist->entries, at + count);
01141
01142 if (entry->queued)
01143 playlist->queued = g_list_remove (playlist->queued, entry);
01144
01145 if (entry->selected)
01146 {
01147 playlist->selected_count --;
01148 playlist->selected_length -= entry->length;
01149 }
01150
01151 playlist->total_length -= entry->length;
01152 entry_free (entry);
01153 }
01154
01155 index_delete (playlist->entries, at, number);
01156 number_entries (playlist, at, entries - at - number);
01157
01158 PLAYLIST_HAS_CHANGED (playlist->number, at, 0);
01159 LEAVE;
01160 }
01161
01162 char * playlist_entry_get_filename (int playlist_num, int entry_num)
01163 {
01164 ENTER;
01165 DECLARE_PLAYLIST_ENTRY;
01166 LOOKUP_PLAYLIST_ENTRY_RET (NULL);
01167
01168 char * filename = str_ref (entry->filename);
01169
01170 LEAVE_RET (filename);
01171 }
01172
01173 PluginHandle * playlist_entry_get_decoder (int playlist_num, int entry_num, bool_t fast)
01174 {
01175 ENTER;
01176
01177 Entry * entry = get_entry (playlist_num, entry_num, ! fast, FALSE);
01178 PluginHandle * decoder = entry ? entry->decoder : NULL;
01179
01180 LEAVE_RET (decoder);
01181 }
01182
01183 Tuple * playlist_entry_get_tuple (int playlist_num, int entry_num, bool_t fast)
01184 {
01185 ENTER;
01186
01187 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01188 Tuple * tuple = entry ? entry->tuple : NULL;
01189
01190 if (tuple)
01191 tuple_ref (tuple);
01192
01193 LEAVE_RET (tuple);
01194 }
01195
01196 char * playlist_entry_get_title (int playlist_num, int entry_num, bool_t fast)
01197 {
01198 ENTER;
01199
01200 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01201 char * title = entry ? str_ref (entry->formatted ? entry->formatted : entry->title) : NULL;
01202
01203 LEAVE_RET (title);
01204 }
01205
01206 void playlist_entry_describe (int playlist_num, int entry_num,
01207 char * * title, char * * artist, char * * album, bool_t fast)
01208 {
01209 ENTER;
01210
01211 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01212 * title = (entry && entry->title) ? str_ref (entry->title) : NULL;
01213 * artist = (entry && entry->artist) ? str_ref (entry->artist) : NULL;
01214 * album = (entry && entry->album) ? str_ref (entry->album) : NULL;
01215
01216 LEAVE;
01217 }
01218
01219 int playlist_entry_get_length (int playlist_num, int entry_num, bool_t fast)
01220 {
01221 ENTER;
01222
01223 Entry * entry = get_entry (playlist_num, entry_num, FALSE, ! fast);
01224 int length = entry ? entry->length : 0;
01225
01226 LEAVE_RET (length);
01227 }
01228
01229 void playlist_set_position (int playlist_num, int entry_num)
01230 {
01231
01232 if (playback_get_playing () && playlist_num == playlist_get_playing ())
01233 playback_stop ();
01234
01235 ENTER;
01236 DECLARE_PLAYLIST_ENTRY;
01237
01238 if (entry_num == -1)
01239 {
01240 LOOKUP_PLAYLIST;
01241 entry = NULL;
01242 }
01243 else
01244 LOOKUP_PLAYLIST_ENTRY;
01245
01246 set_position (playlist, entry);
01247 LEAVE;
01248
01249 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
01250 }
01251
01252 int playlist_get_position (int playlist_num)
01253 {
01254 ENTER;
01255 DECLARE_PLAYLIST;
01256 LOOKUP_PLAYLIST_RET (-1);
01257
01258 int position = playlist->position ? playlist->position->number : -1;
01259
01260 LEAVE_RET (position);
01261 }
01262
01263 void playlist_entry_set_selected (int playlist_num, int entry_num,
01264 bool_t selected)
01265 {
01266 ENTER;
01267 DECLARE_PLAYLIST_ENTRY;
01268 LOOKUP_PLAYLIST_ENTRY;
01269
01270 if (entry->selected == selected)
01271 LEAVE_RET_VOID;
01272
01273 entry->selected = selected;
01274
01275 if (selected)
01276 {
01277 playlist->selected_count++;
01278 playlist->selected_length += entry->length;
01279 }
01280 else
01281 {
01282 playlist->selected_count--;
01283 playlist->selected_length -= entry->length;
01284 }
01285
01286 SELECTION_HAS_CHANGED (playlist->number, entry_num, 1);
01287 LEAVE;
01288 }
01289
01290 bool_t playlist_entry_get_selected (int playlist_num, int entry_num)
01291 {
01292 ENTER;
01293 DECLARE_PLAYLIST_ENTRY;
01294 LOOKUP_PLAYLIST_ENTRY_RET (FALSE);
01295
01296 bool_t selected = entry->selected;
01297
01298 LEAVE_RET (selected);
01299 }
01300
01301 int playlist_selected_count (int playlist_num)
01302 {
01303 ENTER;
01304 DECLARE_PLAYLIST;
01305 LOOKUP_PLAYLIST_RET (0);
01306
01307 int selected_count = playlist->selected_count;
01308
01309 LEAVE_RET (selected_count);
01310 }
01311
01312 void playlist_select_all (int playlist_num, bool_t selected)
01313 {
01314 ENTER;
01315 DECLARE_PLAYLIST;
01316 LOOKUP_PLAYLIST;
01317
01318 int entries = index_count (playlist->entries);
01319 int first = entries, last = 0;
01320
01321 for (int count = 0; count < entries; count ++)
01322 {
01323 Entry * entry = index_get (playlist->entries, count);
01324
01325 if ((selected && ! entry->selected) || (entry->selected && ! selected))
01326 {
01327 entry->selected = selected;
01328 first = MIN (first, entry->number);
01329 last = entry->number;
01330 }
01331 }
01332
01333 if (selected)
01334 {
01335 playlist->selected_count = entries;
01336 playlist->selected_length = playlist->total_length;
01337 }
01338 else
01339 {
01340 playlist->selected_count = 0;
01341 playlist->selected_length = 0;
01342 }
01343
01344 if (first < entries)
01345 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01346
01347 LEAVE;
01348 }
01349
01350 int playlist_shift (int playlist_num, int entry_num, int distance)
01351 {
01352 ENTER;
01353 DECLARE_PLAYLIST_ENTRY;
01354 LOOKUP_PLAYLIST_ENTRY_RET (0);
01355
01356 if (! entry->selected || ! distance)
01357 LEAVE_RET (0);
01358
01359 int entries = index_count (playlist->entries);
01360 int shift = 0, center, top, bottom;
01361
01362 if (distance < 0)
01363 {
01364 for (center = entry_num; center > 0 && shift > distance; )
01365 {
01366 entry = index_get (playlist->entries, -- center);
01367 if (! entry->selected)
01368 shift --;
01369 }
01370 }
01371 else
01372 {
01373 for (center = entry_num + 1; center < entries && shift < distance; )
01374 {
01375 entry = index_get (playlist->entries, center ++);
01376 if (! entry->selected)
01377 shift ++;
01378 }
01379 }
01380
01381 top = bottom = center;
01382
01383 for (int i = 0; i < top; i ++)
01384 {
01385 entry = index_get (playlist->entries, i);
01386 if (entry->selected)
01387 top = i;
01388 }
01389
01390 for (int i = entries; i > bottom; i --)
01391 {
01392 entry = index_get (playlist->entries, i - 1);
01393 if (entry->selected)
01394 bottom = i;
01395 }
01396
01397 Index * temp = index_new ();
01398
01399 for (int i = top; i < center; i ++)
01400 {
01401 entry = index_get (playlist->entries, i);
01402 if (! entry->selected)
01403 index_append (temp, entry);
01404 }
01405
01406 for (int i = top; i < bottom; i ++)
01407 {
01408 entry = index_get (playlist->entries, i);
01409 if (entry->selected)
01410 index_append (temp, entry);
01411 }
01412
01413 for (int i = center; i < bottom; i ++)
01414 {
01415 entry = index_get (playlist->entries, i);
01416 if (! entry->selected)
01417 index_append (temp, entry);
01418 }
01419
01420 index_copy_set (temp, 0, playlist->entries, top, bottom - top);
01421
01422 number_entries (playlist, top, bottom - top);
01423 PLAYLIST_HAS_CHANGED (playlist->number, top, bottom - top);
01424
01425 LEAVE_RET (shift);
01426 }
01427
01428 void playlist_delete_selected (int playlist_num)
01429 {
01430
01431 if (playback_get_playing () && playlist_num == playlist_get_playing () &&
01432 playlist_get_position (playlist_num) >= 0 && playlist_entry_get_selected
01433 (playlist_num, playlist_get_position (playlist_num)))
01434 playback_stop ();
01435
01436 ENTER;
01437 DECLARE_PLAYLIST;
01438 LOOKUP_PLAYLIST;
01439
01440 if (! playlist->selected_count)
01441 LEAVE_RET_VOID;
01442
01443 int entries = index_count (playlist->entries);
01444
01445 Index * others = index_new ();
01446 index_allocate (others, entries - playlist->selected_count);
01447
01448 if (playlist->position && playlist->position->selected)
01449 set_position (playlist, NULL);
01450
01451 int before = 0, after = 0;
01452 bool_t found = FALSE;
01453
01454 for (int count = 0; count < entries; count++)
01455 {
01456 Entry * entry = index_get (playlist->entries, count);
01457
01458 if (entry->selected)
01459 {
01460 if (entry->queued)
01461 playlist->queued = g_list_remove (playlist->queued, entry);
01462
01463 playlist->total_length -= entry->length;
01464 entry_free (entry);
01465
01466 found = TRUE;
01467 after = 0;
01468 }
01469 else
01470 {
01471 index_append (others, entry);
01472
01473 if (found)
01474 after ++;
01475 else
01476 before ++;
01477 }
01478 }
01479
01480 index_free (playlist->entries);
01481 playlist->entries = others;
01482
01483 playlist->selected_count = 0;
01484 playlist->selected_length = 0;
01485
01486 number_entries (playlist, before, index_count (playlist->entries) - before);
01487 PLAYLIST_HAS_CHANGED (playlist->number, before, index_count
01488 (playlist->entries) - after - before);
01489 LEAVE;
01490 }
01491
01492 void playlist_reverse (int playlist_num)
01493 {
01494 ENTER;
01495 DECLARE_PLAYLIST;
01496 LOOKUP_PLAYLIST;
01497
01498 int entries = index_count (playlist->entries);
01499
01500 Index * reversed = index_new ();
01501 index_allocate (reversed, entries);
01502
01503 for (int count = entries; count --; )
01504 index_append (reversed, index_get (playlist->entries, count));
01505
01506 index_free (playlist->entries);
01507 playlist->entries = reversed;
01508
01509 number_entries (playlist, 0, entries);
01510 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries);
01511 LEAVE;
01512 }
01513
01514 void playlist_randomize (int playlist_num)
01515 {
01516 ENTER;
01517 DECLARE_PLAYLIST;
01518 LOOKUP_PLAYLIST;
01519
01520 int entries = index_count (playlist->entries);
01521
01522 for (int i = 0; i < entries; i ++)
01523 {
01524 int j = i + rand () % (entries - i);
01525
01526 struct entry * entry = index_get (playlist->entries, j);
01527 index_set (playlist->entries, j, index_get (playlist->entries, i));
01528 index_set (playlist->entries, i, entry);
01529 }
01530
01531 number_entries (playlist, 0, entries);
01532 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries);
01533 LEAVE;
01534 }
01535
01536 static int filename_compare (const void * _a, const void * _b, void * compare)
01537 {
01538 const Entry * a = _a, * b = _b;
01539
01540 int diff = ((int (*) (const char * a, const char * b)) compare)
01541 (a->filename, b->filename);
01542
01543 if (diff)
01544 return diff;
01545
01546
01547 return a->number - b->number;
01548 }
01549
01550 static int tuple_compare (const void * _a, const void * _b, void * compare)
01551 {
01552 const Entry * a = _a, * b = _b;
01553
01554 if (! a->tuple)
01555 return b->tuple ? -1 : 0;
01556 if (! b->tuple)
01557 return 1;
01558
01559 int diff = ((int (*) (const Tuple * a, const Tuple * b)) compare)
01560 (a->tuple, b->tuple);
01561
01562 if (diff)
01563 return diff;
01564
01565
01566 return a->number - b->number;
01567 }
01568
01569 static int title_compare (const void * _a, const void * _b, void * compare)
01570 {
01571 const Entry * a = _a, * b = _b;
01572
01573 int diff = ((int (*) (const char * a, const char * b)) compare)
01574 (a->formatted ? a->formatted : a->filename,
01575 b->formatted ? b->formatted : b->filename);
01576
01577 if (diff)
01578 return diff;
01579
01580
01581 return a->number - b->number;
01582 }
01583
01584 static void sort (Playlist * playlist, int (* compare) (const void * a,
01585 const void * b, void * inner), void * inner)
01586 {
01587 index_sort_with_data (playlist->entries, compare, inner);
01588 number_entries (playlist, 0, index_count (playlist->entries));
01589
01590 PLAYLIST_HAS_CHANGED (playlist->number, 0, index_count (playlist->entries));
01591 }
01592
01593 static void sort_selected (Playlist * playlist, int (* compare) (const void *
01594 a, const void * b, void * inner), void * inner)
01595 {
01596 int entries = index_count (playlist->entries);
01597
01598 Index * selected = index_new ();
01599 index_allocate (selected, playlist->selected_count);
01600
01601 for (int count = 0; count < entries; count++)
01602 {
01603 Entry * entry = index_get (playlist->entries, count);
01604 if (entry->selected)
01605 index_append (selected, entry);
01606 }
01607
01608 index_sort_with_data (selected, compare, inner);
01609
01610 int count2 = 0;
01611 for (int count = 0; count < entries; count++)
01612 {
01613 Entry * entry = index_get (playlist->entries, count);
01614 if (entry->selected)
01615 index_set (playlist->entries, count, index_get (selected, count2 ++));
01616 }
01617
01618 index_free (selected);
01619
01620 number_entries (playlist, 0, entries);
01621 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries);
01622 }
01623
01624 static bool_t entries_are_scanned (Playlist * playlist, bool_t selected)
01625 {
01626 int entries = index_count (playlist->entries);
01627 for (int count = 0; count < entries; count ++)
01628 {
01629 Entry * entry = index_get (playlist->entries, count);
01630 if (selected && ! entry->selected)
01631 continue;
01632
01633 if (! entry->tuple)
01634 {
01635 interface_show_error (_("The playlist cannot be sorted because "
01636 "metadata scanning is still in progress (or has been disabled)."));
01637 return FALSE;
01638 }
01639 }
01640
01641 return TRUE;
01642 }
01643
01644 void playlist_sort_by_filename (int playlist_num, int (* compare)
01645 (const char * a, const char * b))
01646 {
01647 ENTER;
01648 DECLARE_PLAYLIST;
01649 LOOKUP_PLAYLIST;
01650
01651 sort (playlist, filename_compare, (void *) compare);
01652
01653 LEAVE;
01654 }
01655
01656 void playlist_sort_by_tuple (int playlist_num, int (* compare)
01657 (const Tuple * a, const Tuple * b))
01658 {
01659 ENTER;
01660 DECLARE_PLAYLIST;
01661 LOOKUP_PLAYLIST;
01662
01663 if (entries_are_scanned (playlist, FALSE))
01664 sort (playlist, tuple_compare, (void *) compare);
01665
01666 LEAVE;
01667 }
01668
01669 void playlist_sort_by_title (int playlist_num, int (* compare) (const char *
01670 a, const char * b))
01671 {
01672 ENTER;
01673 DECLARE_PLAYLIST;
01674 LOOKUP_PLAYLIST;
01675
01676 if (entries_are_scanned (playlist, FALSE))
01677 sort (playlist, title_compare, (void *) compare);
01678
01679 LEAVE;
01680 }
01681
01682 void playlist_sort_selected_by_filename (int playlist_num, int (* compare)
01683 (const char * a, const char * b))
01684 {
01685 ENTER;
01686 DECLARE_PLAYLIST;
01687 LOOKUP_PLAYLIST;
01688
01689 sort_selected (playlist, filename_compare, (void *) compare);
01690
01691 LEAVE;
01692 }
01693
01694 void playlist_sort_selected_by_tuple (int playlist_num, int (* compare)
01695 (const Tuple * a, const Tuple * b))
01696 {
01697 ENTER;
01698 DECLARE_PLAYLIST;
01699 LOOKUP_PLAYLIST;
01700
01701 if (entries_are_scanned (playlist, TRUE))
01702 sort_selected (playlist, tuple_compare, (void *) compare);
01703
01704 LEAVE;
01705 }
01706
01707 void playlist_sort_selected_by_title (int playlist_num, int (* compare)
01708 (const char * a, const char * b))
01709 {
01710 ENTER;
01711 DECLARE_PLAYLIST;
01712 LOOKUP_PLAYLIST;
01713
01714 if (entries_are_scanned (playlist, TRUE))
01715 sort (playlist, title_compare, (void *) compare);
01716
01717 LEAVE;
01718 }
01719
01720 void playlist_reformat_titles (void)
01721 {
01722 ENTER;
01723
01724 g_free (title_format);
01725 title_format = NULL;
01726
01727 for (int playlist_num = 0; playlist_num < index_count (playlists);
01728 playlist_num ++)
01729 {
01730 Playlist * playlist = index_get (playlists, playlist_num);
01731 int entries = index_count (playlist->entries);
01732
01733 for (int count = 0; count < entries; count++)
01734 {
01735 Entry * entry = index_get (playlist->entries, count);
01736 str_unref (entry->formatted);
01737 entry->formatted = entry->tuple ? title_from_tuple (entry->tuple) : NULL;
01738 }
01739
01740 METADATA_HAS_CHANGED (playlist_num, 0, entries);
01741 }
01742
01743 LEAVE;
01744 }
01745
01746 void playlist_trigger_scan (void)
01747 {
01748 ENTER;
01749
01750 for (int i = 0; i < index_count (playlists); i ++)
01751 {
01752 Playlist * p = index_get (playlists, i);
01753 p->scanning = TRUE;
01754 }
01755
01756 scan_trigger ();
01757
01758 LEAVE;
01759 }
01760
01761 static void playlist_rescan_real (int playlist_num, bool_t selected)
01762 {
01763 ENTER;
01764 DECLARE_PLAYLIST;
01765 LOOKUP_PLAYLIST;
01766
01767 int entries = index_count (playlist->entries);
01768
01769 for (int count = 0; count < entries; count ++)
01770 {
01771 Entry * entry = index_get (playlist->entries, count);
01772 if (! selected || entry->selected)
01773 {
01774 entry_set_tuple (playlist, entry, NULL);
01775 entry->failed = FALSE;
01776 }
01777 }
01778
01779 METADATA_HAS_CHANGED (playlist->number, 0, entries);
01780 LEAVE;
01781 }
01782
01783 void playlist_rescan (int playlist_num)
01784 {
01785 playlist_rescan_real (playlist_num, FALSE);
01786 }
01787
01788 void playlist_rescan_selected (int playlist_num)
01789 {
01790 playlist_rescan_real (playlist_num, TRUE);
01791 }
01792
01793 void playlist_rescan_file (const char * filename)
01794 {
01795 ENTER;
01796
01797 int num_playlists = index_count (playlists);
01798
01799 for (int playlist_num = 0; playlist_num < num_playlists; playlist_num ++)
01800 {
01801 Playlist * playlist = index_get (playlists, playlist_num);
01802 int num_entries = index_count (playlist->entries);
01803
01804 for (int entry_num = 0; entry_num < num_entries; entry_num ++)
01805 {
01806 Entry * entry = index_get (playlist->entries, entry_num);
01807
01808 if (! strcmp (entry->filename, filename))
01809 {
01810 entry_set_tuple (playlist, entry, NULL);
01811 entry->failed = FALSE;
01812
01813 METADATA_HAS_CHANGED (playlist_num, entry_num, 1);
01814 }
01815 }
01816 }
01817
01818 LEAVE;
01819 }
01820
01821 int64_t playlist_get_total_length (int playlist_num)
01822 {
01823 ENTER;
01824 DECLARE_PLAYLIST;
01825 LOOKUP_PLAYLIST_RET (0);
01826
01827 int64_t length = playlist->total_length;
01828
01829 LEAVE_RET (length);
01830 }
01831
01832 int64_t playlist_get_selected_length (int playlist_num)
01833 {
01834 ENTER;
01835 DECLARE_PLAYLIST;
01836 LOOKUP_PLAYLIST_RET (0);
01837
01838 int64_t length = playlist->selected_length;
01839
01840 LEAVE_RET (length);
01841 }
01842
01843 int playlist_queue_count (int playlist_num)
01844 {
01845 ENTER;
01846 DECLARE_PLAYLIST;
01847 LOOKUP_PLAYLIST_RET (0);
01848
01849 int count = g_list_length (playlist->queued);
01850
01851 LEAVE_RET (count);
01852 }
01853
01854 void playlist_queue_insert (int playlist_num, int at, int entry_num)
01855 {
01856 ENTER;
01857 DECLARE_PLAYLIST_ENTRY;
01858 LOOKUP_PLAYLIST_ENTRY;
01859
01860 if (entry->queued)
01861 LEAVE_RET_VOID;
01862
01863 if (at < 0)
01864 playlist->queued = g_list_append (playlist->queued, entry);
01865 else
01866 playlist->queued = g_list_insert (playlist->queued, entry, at);
01867
01868 entry->queued = TRUE;
01869
01870 SELECTION_HAS_CHANGED (playlist->number, entry_num, 1);
01871 LEAVE;
01872 }
01873
01874 void playlist_queue_insert_selected (int playlist_num, int at)
01875 {
01876 ENTER;
01877 DECLARE_PLAYLIST;
01878 LOOKUP_PLAYLIST;
01879
01880 int entries = index_count(playlist->entries);
01881 int first = entries, last = 0;
01882
01883 for (int count = 0; count < entries; count++)
01884 {
01885 Entry * entry = index_get (playlist->entries, count);
01886
01887 if (! entry->selected || entry->queued)
01888 continue;
01889
01890 if (at < 0)
01891 playlist->queued = g_list_append (playlist->queued, entry);
01892 else
01893 playlist->queued = g_list_insert (playlist->queued, entry, at++);
01894
01895 entry->queued = TRUE;
01896 first = MIN (first, entry->number);
01897 last = entry->number;
01898 }
01899
01900 if (first < entries)
01901 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01902
01903 LEAVE;
01904 }
01905
01906 int playlist_queue_get_entry (int playlist_num, int at)
01907 {
01908 ENTER;
01909 DECLARE_PLAYLIST;
01910 LOOKUP_PLAYLIST_RET (-1);
01911
01912 GList * node = g_list_nth (playlist->queued, at);
01913 int entry_num = node ? ((Entry *) node->data)->number : -1;
01914
01915 LEAVE_RET (entry_num);
01916 }
01917
01918 int playlist_queue_find_entry (int playlist_num, int entry_num)
01919 {
01920 ENTER;
01921 DECLARE_PLAYLIST_ENTRY;
01922 LOOKUP_PLAYLIST_ENTRY_RET (-1);
01923
01924 int pos = entry->queued ? g_list_index (playlist->queued, entry) : -1;
01925
01926 LEAVE_RET (pos);
01927 }
01928
01929 void playlist_queue_delete (int playlist_num, int at, int number)
01930 {
01931 ENTER;
01932 DECLARE_PLAYLIST;
01933 LOOKUP_PLAYLIST;
01934
01935 int entries = index_count (playlist->entries);
01936 int first = entries, last = 0;
01937
01938 if (at == 0)
01939 {
01940 while (playlist->queued && number --)
01941 {
01942 Entry * entry = playlist->queued->data;
01943 entry->queued = FALSE;
01944 first = MIN (first, entry->number);
01945 last = entry->number;
01946
01947 playlist->queued = g_list_delete_link (playlist->queued,
01948 playlist->queued);
01949 }
01950 }
01951 else
01952 {
01953 GList * anchor = g_list_nth (playlist->queued, at - 1);
01954 if (! anchor)
01955 goto DONE;
01956
01957 while (anchor->next && number --)
01958 {
01959 Entry * entry = anchor->next->data;
01960 entry->queued = FALSE;
01961 first = MIN (first, entry->number);
01962 last = entry->number;
01963
01964 playlist->queued = g_list_delete_link (playlist->queued,
01965 anchor->next);
01966 }
01967 }
01968
01969 DONE:
01970 if (first < entries)
01971 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
01972
01973 LEAVE;
01974 }
01975
01976 void playlist_queue_delete_selected (int playlist_num)
01977 {
01978 ENTER;
01979 DECLARE_PLAYLIST;
01980 LOOKUP_PLAYLIST;
01981
01982 int entries = index_count (playlist->entries);
01983 int first = entries, last = 0;
01984
01985 for (GList * node = playlist->queued; node; )
01986 {
01987 GList * next = node->next;
01988 Entry * entry = node->data;
01989
01990 if (entry->selected)
01991 {
01992 entry->queued = FALSE;
01993 playlist->queued = g_list_delete_link (playlist->queued, node);
01994 first = MIN (first, entry->number);
01995 last = entry->number;
01996 }
01997
01998 node = next;
01999 }
02000
02001 if (first < entries)
02002 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first);
02003
02004 LEAVE;
02005 }
02006
02007 static bool_t shuffle_prev (Playlist * playlist)
02008 {
02009 int entries = index_count (playlist->entries);
02010 Entry * found = NULL;
02011
02012 for (int count = 0; count < entries; count ++)
02013 {
02014 Entry * entry = index_get (playlist->entries, count);
02015
02016 if (entry->shuffle_num && (! playlist->position ||
02017 entry->shuffle_num < playlist->position->shuffle_num) && (! found
02018 || entry->shuffle_num > found->shuffle_num))
02019 found = entry;
02020 }
02021
02022 if (! found)
02023 return FALSE;
02024
02025 playlist->position = found;
02026 return TRUE;
02027 }
02028
02029 bool_t playlist_prev_song (int playlist_num)
02030 {
02031
02032 if (playback_get_playing () && playlist_num == playlist_get_playing ())
02033 playback_stop ();
02034
02035 ENTER;
02036 DECLARE_PLAYLIST;
02037 LOOKUP_PLAYLIST_RET (FALSE);
02038
02039 if (get_bool (NULL, "shuffle"))
02040 {
02041 if (! shuffle_prev (playlist))
02042 LEAVE_RET (FALSE);
02043 }
02044 else
02045 {
02046 if (! playlist->position || playlist->position->number == 0)
02047 LEAVE_RET (FALSE);
02048
02049 set_position (playlist, index_get (playlist->entries,
02050 playlist->position->number - 1));
02051 }
02052
02053 LEAVE;
02054
02055 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
02056 return TRUE;
02057 }
02058
02059 static bool_t shuffle_next (Playlist * playlist)
02060 {
02061 int entries = index_count (playlist->entries), choice = 0, count;
02062 Entry * found = NULL;
02063
02064 for (count = 0; count < entries; count ++)
02065 {
02066 Entry * entry = index_get (playlist->entries, count);
02067
02068 if (! entry->shuffle_num)
02069 choice ++;
02070 else if (playlist->position && entry->shuffle_num >
02071 playlist->position->shuffle_num && (! found || entry->shuffle_num
02072 < found->shuffle_num))
02073 found = entry;
02074 }
02075
02076 if (found)
02077 {
02078 playlist->position = found;
02079 return TRUE;
02080 }
02081
02082 if (! choice)
02083 return FALSE;
02084
02085 choice = rand () % choice;
02086
02087 for (count = 0; ; count ++)
02088 {
02089 Entry * entry = index_get (playlist->entries, count);
02090
02091 if (! entry->shuffle_num)
02092 {
02093 if (! choice)
02094 {
02095 set_position (playlist, entry);
02096 return TRUE;
02097 }
02098
02099 choice --;
02100 }
02101 }
02102 }
02103
02104 static void shuffle_reset (Playlist * playlist)
02105 {
02106 int entries = index_count (playlist->entries);
02107
02108 playlist->last_shuffle_num = 0;
02109
02110 for (int count = 0; count < entries; count ++)
02111 {
02112 Entry * entry = index_get (playlist->entries, count);
02113 entry->shuffle_num = 0;
02114 }
02115 }
02116
02117 bool_t playlist_next_song (int playlist_num, bool_t repeat)
02118 {
02119
02120 if (playback_get_playing () && playlist_num == playlist_get_playing ())
02121 playback_stop ();
02122
02123 ENTER;
02124 DECLARE_PLAYLIST;
02125 LOOKUP_PLAYLIST_RET (FALSE);
02126
02127 int entries = index_count(playlist->entries);
02128
02129 if (! entries)
02130 LEAVE_RET (FALSE);
02131
02132 if (playlist->queued)
02133 {
02134 set_position (playlist, playlist->queued->data);
02135 playlist->queued = g_list_remove (playlist->queued, playlist->position);
02136 playlist->position->queued = FALSE;
02137 }
02138 else if (get_bool (NULL, "shuffle"))
02139 {
02140 if (! shuffle_next (playlist))
02141 {
02142 if (! repeat)
02143 LEAVE_RET (FALSE);
02144
02145 shuffle_reset (playlist);
02146
02147 if (! shuffle_next (playlist))
02148 LEAVE_RET (FALSE);
02149 }
02150 }
02151 else
02152 {
02153 if (! playlist->position)
02154 set_position (playlist, index_get (playlist->entries, 0));
02155 else if (playlist->position->number == entries - 1)
02156 {
02157 if (! repeat)
02158 LEAVE_RET (FALSE);
02159
02160 set_position (playlist, index_get (playlist->entries, 0));
02161 }
02162 else
02163 set_position (playlist, index_get (playlist->entries,
02164 playlist->position->number + 1));
02165 }
02166
02167 LEAVE;
02168
02169 hook_call ("playlist position", GINT_TO_POINTER (playlist_num));
02170 return TRUE;
02171 }
02172
02173 int playback_entry_get_position (void)
02174 {
02175 ENTER;
02176
02177 Entry * entry = get_playback_entry (FALSE, FALSE);
02178 int entry_num = entry ? entry->number : -1;
02179
02180 LEAVE_RET (entry_num);
02181 }
02182
02183 PluginHandle * playback_entry_get_decoder (void)
02184 {
02185 ENTER;
02186
02187 Entry * entry = get_playback_entry (TRUE, FALSE);
02188 PluginHandle * decoder = entry ? entry->decoder : NULL;
02189
02190 LEAVE_RET (decoder);
02191 }
02192
02193 Tuple * playback_entry_get_tuple (void)
02194 {
02195 ENTER;
02196
02197 Entry * entry = get_playback_entry (FALSE, TRUE);
02198 Tuple * tuple = entry ? entry->tuple : NULL;
02199
02200 if (tuple)
02201 tuple_ref (tuple);
02202
02203 LEAVE_RET (tuple);
02204 }
02205
02206 char * playback_entry_get_title (void)
02207 {
02208 ENTER;
02209
02210 Entry * entry = get_playback_entry (FALSE, TRUE);
02211 char * title = entry ? str_ref (entry->formatted ? entry->formatted :
02212 entry->title) : NULL;
02213
02214 LEAVE_RET (title);
02215 }
02216
02217 int playback_entry_get_length (void)
02218 {
02219 ENTER;
02220
02221 Entry * entry = get_playback_entry (FALSE, TRUE);
02222 int length = entry->length;
02223
02224 LEAVE_RET (length);
02225 }
02226
02227 void playback_entry_set_tuple (Tuple * tuple)
02228 {
02229 ENTER;
02230 if (! playing_playlist || ! playing_playlist->position)
02231 LEAVE_RET_VOID;
02232
02233 Entry * entry = playing_playlist->position;
02234 entry_cancel_scan (entry);
02235 entry_set_tuple (playing_playlist, entry, tuple);
02236
02237 METADATA_HAS_CHANGED (playing_playlist->number, entry->number, 1);
02238 LEAVE;
02239 }
02240
02241 int playback_entry_get_start_time (void)
02242 {
02243 ENTER;
02244 if (! playing_playlist || ! playing_playlist->position)
02245 LEAVE_RET (0);
02246
02247 int start = playing_playlist->position->start;
02248 LEAVE_RET (start);
02249 }
02250
02251 int playback_entry_get_end_time (void)
02252 {
02253 ENTER;
02254 if (! playing_playlist || ! playing_playlist->position)
02255 LEAVE_RET (-1);
02256
02257 int end = playing_playlist->position->end;
02258 LEAVE_RET (end);
02259 }
02260
02261 void playlist_save_state (void)
02262 {
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 ENTER;
02269
02270 char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
02271 FILE * handle = fopen (path, "w");
02272 g_free (path);
02273 if (! handle)
02274 LEAVE_RET_VOID;
02275
02276 fprintf (handle, "resume-state %d\n", resume_state);
02277 fprintf (handle, "resume-time %d\n", resume_time);
02278
02279 fprintf (handle, "active %d\n", active_playlist ? active_playlist->number : -1);
02280 fprintf (handle, "playing %d\n", playing_playlist ? playing_playlist->number : -1);
02281
02282 for (int playlist_num = 0; playlist_num < index_count (playlists);
02283 playlist_num ++)
02284 {
02285 Playlist * playlist = index_get (playlists, playlist_num);
02286
02287 fprintf (handle, "playlist %d\n", playlist_num);
02288
02289 if (playlist->filename)
02290 fprintf (handle, "filename %s\n", playlist->filename);
02291
02292 fprintf (handle, "position %d\n", playlist->position ?
02293 playlist->position->number : -1);
02294 }
02295
02296 fclose (handle);
02297 LEAVE;
02298 }
02299
02300 static char parse_key[512];
02301 static char * parse_value;
02302
02303 static void parse_next (FILE * handle)
02304 {
02305 parse_value = NULL;
02306
02307 if (! fgets (parse_key, sizeof parse_key, handle))
02308 return;
02309
02310 char * space = strchr (parse_key, ' ');
02311 if (! space)
02312 return;
02313
02314 * space = 0;
02315 parse_value = space + 1;
02316
02317 char * newline = strchr (parse_value, '\n');
02318 if (newline)
02319 * newline = 0;
02320 }
02321
02322 static bool_t parse_integer (const char * key, int * value)
02323 {
02324 return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value,
02325 "%d", value) == 1);
02326 }
02327
02328 static char * parse_string (const char * key)
02329 {
02330 return (parse_value && ! strcmp (parse_key, key)) ? str_get (parse_value) : NULL;
02331 }
02332
02333 void playlist_load_state (void)
02334 {
02335 ENTER;
02336 int playlist_num;
02337
02338 char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
02339 FILE * handle = fopen (path, "r");
02340 g_free (path);
02341 if (! handle)
02342 LEAVE_RET_VOID;
02343
02344 parse_next (handle);
02345
02346 if (parse_integer ("resume-state", & resume_state))
02347 parse_next (handle);
02348 if (parse_integer ("resume-time", & resume_time))
02349 parse_next (handle);
02350
02351 if (parse_integer ("active", & playlist_num))
02352 {
02353 if (! (active_playlist = lookup_playlist (playlist_num)))
02354 active_playlist = index_get (playlists, 0);
02355 parse_next (handle);
02356 }
02357
02358 if (parse_integer ("playing", & playlist_num))
02359 {
02360 playing_playlist = lookup_playlist (playlist_num);
02361 parse_next (handle);
02362 }
02363
02364 while (parse_integer ("playlist", & playlist_num) && playlist_num >= 0 &&
02365 playlist_num < index_count (playlists))
02366 {
02367 Playlist * playlist = index_get (playlists, playlist_num);
02368 int entries = index_count (playlist->entries), position;
02369 char * s;
02370
02371 parse_next (handle);
02372
02373 if ((s = parse_string ("filename")))
02374 {
02375 str_unref (playlist->filename);
02376 playlist->filename = s;
02377 parse_next (handle);
02378 }
02379
02380 if (parse_integer ("position", & position))
02381 parse_next (handle);
02382
02383 if (position >= 0 && position < entries)
02384 set_position (playlist, index_get (playlist->entries, position));
02385 }
02386
02387 fclose (handle);
02388
02389
02390
02391 for (int i = 0; i < index_count (playlists); i ++)
02392 {
02393 Playlist * p = index_get (playlists, i);
02394 memset (& p->last_update, 0, sizeof (Update));
02395 memset (& p->next_update, 0, sizeof (Update));
02396 }
02397
02398 update_level = 0;
02399
02400 if (update_source)
02401 {
02402 g_source_remove (update_source);
02403 update_source = 0;
02404 }
02405
02406 LEAVE;
02407 }
02408
02409 void playlist_resume (void)
02410 {
02411 if (resume_state == RESUME_PLAY || resume_state == RESUME_PAUSE)
02412 playback_play (resume_time, resume_state == RESUME_PAUSE);
02413 }