Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * playlist-new.c 00003 * Copyright 2009-2011 John Lindgren 00004 * 00005 * This file is part of Audacious. 00006 * 00007 * Audacious is free software: you can redistribute it and/or modify it under 00008 * the terms of the GNU General Public License as published by the Free Software 00009 * Foundation, version 2 or version 3 of the License. 00010 * 00011 * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY 00012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 00013 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along with 00016 * Audacious. If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 * The Audacious team does not consider modular code linking to Audacious or 00019 * using our public API to be a derived work. 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 /* Hack: We cannot refresh segmented entries (since their info is read from 00159 * the cue sheet when it is first loaded), so leave them alone. -jlindgren */ 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 /* Not initted or already shut down */ 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 /* scan_mutex must be locked! */ 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; /* thread not in use or still working */ 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; /* thread already in use */ 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]); /* scan in progress == very, very bad */ 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 /* As soon as we know the caller is looking for metadata, we start the threaded 00552 * scanner. Though it may be faster in the short run simply to scan the entry 00553 * we are concerned with in the main thread, this is better in the long run 00554 * because the scanner can work on the following entries while the caller is 00555 * processing this one. */ 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 /* If we are already at the song or it is already at the top of the shuffle 00883 * list, we let it be. Otherwise, we move it to the top. */ 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 /* Preallocate space to avoid reallocs. (The actual number of entries may 00974 * turn out to be greater due to subtunes.) */ 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 /* preserve order of "equal" entries */ 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 /* preserve order of "equal" entries */ 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 /* preserve order of "equal" entries */ 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 /* If we have a song in queue, jump to it, _then_ remove it from queue */ 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)) /* compatibility with 2.3 */ 02135 parse_next (handle); 02136 02137 if (parse_integer ("last-shuffled", & playlist->last_shuffle_num)) 02138 parse_next (handle); 02139 else /* compatibility with 2.3 beta */ 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 }