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