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