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