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