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 00857 gint playlist_get_playing (void) 00858 { 00859 ENTER; 00860 gint list = playing_playlist ? playing_playlist->number: -1; 00861 LEAVE_RET (list); 00862 } 00863 00864 /* If we are already at the song or it is already at the top of the shuffle 00865 * list, we let it be. Otherwise, we move it to the top. */ 00866 static void set_position (Playlist * playlist, Entry * entry) 00867 { 00868 if (entry == playlist->position) 00869 return; 00870 00871 playlist->position = entry; 00872 00873 if (! entry) 00874 return; 00875 00876 if (! entry->shuffle_num || entry->shuffle_num != playlist->last_shuffle_num) 00877 { 00878 playlist->last_shuffle_num ++; 00879 entry->shuffle_num = playlist->last_shuffle_num; 00880 } 00881 } 00882 00883 gint playlist_entry_count (gint playlist_num) 00884 { 00885 ENTER; 00886 DECLARE_PLAYLIST; 00887 LOOKUP_PLAYLIST_RET (0); 00888 00889 gint count = index_count (playlist->entries); 00890 00891 LEAVE_RET (count); 00892 } 00893 00894 void playlist_entry_insert_batch_raw (gint playlist_num, gint at, 00895 struct index * filenames, struct index * tuples, struct index * decoders) 00896 { 00897 ENTER; 00898 DECLARE_PLAYLIST; 00899 LOOKUP_PLAYLIST; 00900 00901 gint entries = index_count (playlist->entries); 00902 00903 if (at < 0 || at > entries) 00904 at = entries; 00905 00906 gint number = index_count (filenames); 00907 00908 struct index * add = index_new (); 00909 index_allocate (add, number); 00910 00911 for (gint i = 0; i < number; i ++) 00912 { 00913 gchar * filename = index_get (filenames, i); 00914 uri_check_utf8 (& filename, TRUE); 00915 Tuple * tuple = tuples ? index_get (tuples, i) : NULL; 00916 PluginHandle * decoder = decoders ? index_get (decoders, i) : NULL; 00917 index_append (add, entry_new (filename, tuple, decoder)); 00918 } 00919 00920 index_free (filenames); 00921 if (decoders) 00922 index_free (decoders); 00923 if (tuples) 00924 index_free (tuples); 00925 00926 number = index_count (add); 00927 index_merge_insert (playlist->entries, at, add); 00928 index_free (add); 00929 00930 number_entries (playlist, at, entries + number - at); 00931 00932 for (gint count = 0; count < number; count ++) 00933 { 00934 Entry * entry = index_get (playlist->entries, at + count); 00935 playlist->total_length += entry->length; 00936 } 00937 00938 PLAYLIST_HAS_CHANGED (playlist->number, at, number); 00939 LEAVE; 00940 } 00941 00942 void playlist_entry_delete (gint playlist_num, gint at, gint number) 00943 { 00944 if (playback_get_playing () && playlist_num == playlist_get_playing () && 00945 playlist_get_position (playlist_num) >= at && playlist_get_position 00946 (playlist_num) < at + number) 00947 playback_stop (); 00948 00949 ENTER; 00950 DECLARE_PLAYLIST; 00951 LOOKUP_PLAYLIST; 00952 00953 gint entries = index_count (playlist->entries); 00954 00955 if (at < 0 || at > entries) 00956 at = entries; 00957 if (number < 0 || number > entries - at) 00958 number = entries - at; 00959 00960 if (playlist->position && playlist->position->number >= at && 00961 playlist->position->number < at + number) 00962 set_position (playlist, NULL); 00963 00964 for (gint count = 0; count < number; count ++) 00965 { 00966 Entry * entry = index_get (playlist->entries, at + count); 00967 00968 if (entry->queued) 00969 playlist->queued = g_list_remove (playlist->queued, entry); 00970 00971 if (entry->selected) 00972 { 00973 playlist->selected_count --; 00974 playlist->selected_length -= entry->length; 00975 } 00976 00977 playlist->total_length -= entry->length; 00978 entry_free (entry); 00979 } 00980 00981 index_delete (playlist->entries, at, number); 00982 number_entries (playlist, at, entries - at - number); 00983 00984 PLAYLIST_HAS_CHANGED (playlist->number, at, 0); 00985 LEAVE; 00986 } 00987 00988 gchar * playlist_entry_get_filename (gint playlist_num, gint entry_num) 00989 { 00990 ENTER; 00991 DECLARE_PLAYLIST_ENTRY; 00992 LOOKUP_PLAYLIST_ENTRY_RET (NULL); 00993 00994 gchar * filename = g_strdup (entry->filename); 00995 00996 LEAVE_RET (filename); 00997 } 00998 00999 PluginHandle * playlist_entry_get_decoder (gint playlist_num, gint entry_num, 01000 gboolean fast) 01001 { 01002 ENTER; 01003 DECLARE_PLAYLIST_ENTRY; 01004 LOOKUP_PLAYLIST_ENTRY_RET (NULL); 01005 01006 if (! entry->decoder && ! fast) 01007 { 01008 while (entry_scan_in_progress (entry)) 01009 g_cond_wait (cond, mutex); 01010 01011 if (! entry->decoder) 01012 entry->decoder = file_find_decoder (entry->filename, FALSE); 01013 } 01014 01015 PluginHandle * decoder = entry->decoder; 01016 01017 LEAVE_RET (decoder); 01018 } 01019 01020 void playlist_entry_set_tuple (gint playlist_num, gint entry_num, Tuple * tuple) 01021 { 01022 ENTER; 01023 DECLARE_PLAYLIST_ENTRY; 01024 LOOKUP_PLAYLIST_ENTRY; 01025 01026 while (entry_scan_in_progress (entry)) 01027 g_cond_wait (cond, mutex); 01028 01029 entry_set_tuple (playlist, entry, tuple); 01030 01031 METADATA_HAS_CHANGED (playlist->number, entry_num, 1); 01032 LEAVE; 01033 } 01034 01035 Tuple * playlist_entry_get_tuple (gint playlist_num, gint entry_num, 01036 gboolean fast) 01037 { 01038 ENTER; 01039 DECLARE_PLAYLIST_ENTRY; 01040 LOOKUP_PLAYLIST_ENTRY_RET (NULL); 01041 01042 if (! fast) 01043 check_scanned (playlist, entry); 01044 01045 Tuple * tuple = entry->tuple; 01046 if (tuple) 01047 mowgli_object_ref (tuple); 01048 01049 LEAVE_RET (tuple); 01050 } 01051 01052 gchar * playlist_entry_get_title (gint playlist_num, gint entry_num, 01053 gboolean fast) 01054 { 01055 ENTER; 01056 DECLARE_PLAYLIST_ENTRY; 01057 LOOKUP_PLAYLIST_ENTRY_RET (NULL); 01058 01059 if (! fast) 01060 check_scanned (playlist, entry); 01061 01062 gchar * title = g_strdup (entry->formatted ? entry->formatted : 01063 entry->filename); 01064 01065 LEAVE_RET (title); 01066 } 01067 01068 void playlist_entry_describe (gint playlist_num, gint entry_num, 01069 gchar * * title, gchar * * artist, gchar * * album, gboolean fast) 01070 { 01071 * title = * artist = * album = NULL; 01072 01073 ENTER; 01074 DECLARE_PLAYLIST_ENTRY; 01075 LOOKUP_PLAYLIST_ENTRY; 01076 01077 if (! fast) 01078 check_scanned (playlist, entry); 01079 01080 if (entry->title) 01081 { 01082 * title = g_strdup (entry->title); 01083 * artist = g_strdup (entry->artist); 01084 * album = g_strdup (entry->album); 01085 } 01086 else 01087 * title = g_strdup (entry->filename); 01088 01089 LEAVE; 01090 } 01091 01092 gint playlist_entry_get_length (gint playlist_num, gint entry_num, gboolean fast) 01093 { 01094 ENTER; 01095 DECLARE_PLAYLIST_ENTRY; 01096 LOOKUP_PLAYLIST_ENTRY_RET (0); 01097 01098 if (! fast) 01099 check_scanned (playlist, entry); 01100 01101 gint length = entry->length; 01102 01103 LEAVE_RET (length); 01104 } 01105 01106 gboolean playlist_entry_is_segmented (gint playlist_num, gint entry_num) 01107 { 01108 ENTER; 01109 DECLARE_PLAYLIST_ENTRY; 01110 LOOKUP_PLAYLIST_ENTRY_RET (FALSE); 01111 01112 gboolean segmented = entry->segmented; 01113 01114 LEAVE_RET (segmented); 01115 } 01116 01117 gint playlist_entry_get_start_time (gint playlist_num, gint entry_num) 01118 { 01119 ENTER; 01120 DECLARE_PLAYLIST_ENTRY; 01121 LOOKUP_PLAYLIST_ENTRY_RET (-1); 01122 01123 gint start = entry->start; 01124 01125 LEAVE_RET (start); 01126 } 01127 01128 gint playlist_entry_get_end_time (gint playlist_num, gint entry_num) 01129 { 01130 ENTER; 01131 DECLARE_PLAYLIST_ENTRY; 01132 LOOKUP_PLAYLIST_ENTRY_RET (-1); 01133 01134 gint end = entry->end; 01135 01136 LEAVE_RET (end); 01137 } 01138 01139 void playlist_set_position (gint playlist_num, gint entry_num) 01140 { 01141 if (playback_get_playing () && playlist_num == playlist_get_playing ()) 01142 playback_stop (); 01143 01144 ENTER; 01145 DECLARE_PLAYLIST_ENTRY; 01146 01147 if (entry_num == -1) 01148 { 01149 LOOKUP_PLAYLIST; 01150 entry = NULL; 01151 } 01152 else 01153 LOOKUP_PLAYLIST_ENTRY; 01154 01155 set_position (playlist, entry); 01156 LEAVE; 01157 01158 hook_call ("playlist position", GINT_TO_POINTER (playlist_num)); 01159 } 01160 01161 gint playlist_get_position (gint playlist_num) 01162 { 01163 ENTER; 01164 DECLARE_PLAYLIST; 01165 LOOKUP_PLAYLIST_RET (-1); 01166 01167 gint position = playlist->position ? playlist->position->number : -1; 01168 01169 LEAVE_RET (position); 01170 } 01171 01172 void playlist_entry_set_selected (gint playlist_num, gint entry_num, 01173 gboolean selected) 01174 { 01175 ENTER; 01176 DECLARE_PLAYLIST_ENTRY; 01177 LOOKUP_PLAYLIST_ENTRY; 01178 01179 if (entry->selected == selected) 01180 LEAVE_RET_VOID; 01181 01182 entry->selected = selected; 01183 01184 if (selected) 01185 { 01186 playlist->selected_count++; 01187 playlist->selected_length += entry->length; 01188 } 01189 else 01190 { 01191 playlist->selected_count--; 01192 playlist->selected_length -= entry->length; 01193 } 01194 01195 SELECTION_HAS_CHANGED (playlist->number, entry_num, 1); 01196 LEAVE; 01197 } 01198 01199 gboolean playlist_entry_get_selected (gint playlist_num, gint entry_num) 01200 { 01201 ENTER; 01202 DECLARE_PLAYLIST_ENTRY; 01203 LOOKUP_PLAYLIST_ENTRY_RET (FALSE); 01204 01205 gboolean selected = entry->selected; 01206 01207 LEAVE_RET (selected); 01208 } 01209 01210 gint playlist_selected_count (gint playlist_num) 01211 { 01212 ENTER; 01213 DECLARE_PLAYLIST; 01214 LOOKUP_PLAYLIST_RET (0); 01215 01216 gint selected_count = playlist->selected_count; 01217 01218 LEAVE_RET (selected_count); 01219 } 01220 01221 void playlist_select_all (gint playlist_num, gboolean selected) 01222 { 01223 ENTER; 01224 DECLARE_PLAYLIST; 01225 LOOKUP_PLAYLIST; 01226 01227 gint entries = index_count (playlist->entries); 01228 gint first = entries, last = 0; 01229 01230 for (gint count = 0; count < entries; count ++) 01231 { 01232 Entry * entry = index_get (playlist->entries, count); 01233 01234 if ((selected && ! entry->selected) || (entry->selected && ! selected)) 01235 { 01236 entry->selected = selected; 01237 first = MIN (first, entry->number); 01238 last = entry->number; 01239 } 01240 } 01241 01242 if (selected) 01243 { 01244 playlist->selected_count = entries; 01245 playlist->selected_length = playlist->total_length; 01246 } 01247 else 01248 { 01249 playlist->selected_count = 0; 01250 playlist->selected_length = 0; 01251 } 01252 01253 if (first < entries) 01254 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first); 01255 01256 LEAVE; 01257 } 01258 01259 gint playlist_shift (gint playlist_num, gint entry_num, gint distance) 01260 { 01261 ENTER; 01262 DECLARE_PLAYLIST_ENTRY; 01263 LOOKUP_PLAYLIST_ENTRY_RET (0); 01264 01265 if (! entry->selected || ! distance) 01266 LEAVE_RET (0); 01267 01268 gint entries = index_count (playlist->entries); 01269 gint shift = 0, center, top, bottom; 01270 01271 if (distance < 0) 01272 { 01273 for (center = entry_num; center > 0 && shift > distance; ) 01274 { 01275 entry = index_get (playlist->entries, -- center); 01276 if (! entry->selected) 01277 shift --; 01278 } 01279 } 01280 else 01281 { 01282 for (center = entry_num + 1; center < entries && shift < distance; ) 01283 { 01284 entry = index_get (playlist->entries, center ++); 01285 if (! entry->selected) 01286 shift ++; 01287 } 01288 } 01289 01290 top = bottom = center; 01291 01292 for (gint i = 0; i < top; i ++) 01293 { 01294 entry = index_get (playlist->entries, i); 01295 if (entry->selected) 01296 top = i; 01297 } 01298 01299 for (gint i = entries; i > bottom; i --) 01300 { 01301 entry = index_get (playlist->entries, i - 1); 01302 if (entry->selected) 01303 bottom = i; 01304 } 01305 01306 struct index * temp = index_new (); 01307 01308 for (gint i = top; i < center; i ++) 01309 { 01310 entry = index_get (playlist->entries, i); 01311 if (! entry->selected) 01312 index_append (temp, entry); 01313 } 01314 01315 for (gint i = top; i < bottom; i ++) 01316 { 01317 entry = index_get (playlist->entries, i); 01318 if (entry->selected) 01319 index_append (temp, entry); 01320 } 01321 01322 for (gint i = center; i < bottom; i ++) 01323 { 01324 entry = index_get (playlist->entries, i); 01325 if (! entry->selected) 01326 index_append (temp, entry); 01327 } 01328 01329 index_copy_set (temp, 0, playlist->entries, top, bottom - top); 01330 01331 number_entries (playlist, top, bottom - top); 01332 PLAYLIST_HAS_CHANGED (playlist->number, top, bottom - top); 01333 01334 LEAVE_RET (shift); 01335 } 01336 01337 void playlist_delete_selected (gint playlist_num) 01338 { 01339 if (playback_get_playing () && playlist_num == playlist_get_playing () && 01340 playlist_get_position (playlist_num) >= 0 && playlist_entry_get_selected 01341 (playlist_num, playlist_get_position (playlist_num))) 01342 playback_stop (); 01343 01344 ENTER; 01345 DECLARE_PLAYLIST; 01346 LOOKUP_PLAYLIST; 01347 01348 if (! playlist->selected_count) 01349 LEAVE_RET_VOID; 01350 01351 gint entries = index_count (playlist->entries); 01352 01353 struct index * others = index_new (); 01354 index_allocate (others, entries - playlist->selected_count); 01355 01356 if (playlist->position && playlist->position->selected) 01357 set_position (playlist, NULL); 01358 01359 gint before = 0, after = 0; 01360 gboolean found = FALSE; 01361 01362 for (gint count = 0; count < entries; count++) 01363 { 01364 Entry * entry = index_get (playlist->entries, count); 01365 01366 if (entry->selected) 01367 { 01368 if (entry->queued) 01369 playlist->queued = g_list_remove (playlist->queued, entry); 01370 01371 playlist->total_length -= entry->length; 01372 entry_free (entry); 01373 01374 found = TRUE; 01375 after = 0; 01376 } 01377 else 01378 { 01379 index_append (others, entry); 01380 01381 if (found) 01382 after ++; 01383 else 01384 before ++; 01385 } 01386 } 01387 01388 index_free (playlist->entries); 01389 playlist->entries = others; 01390 01391 playlist->selected_count = 0; 01392 playlist->selected_length = 0; 01393 01394 number_entries (playlist, before, index_count (playlist->entries) - before); 01395 PLAYLIST_HAS_CHANGED (playlist->number, before, index_count 01396 (playlist->entries) - after - before); 01397 LEAVE; 01398 } 01399 01400 void playlist_reverse (gint playlist_num) 01401 { 01402 ENTER; 01403 DECLARE_PLAYLIST; 01404 LOOKUP_PLAYLIST; 01405 01406 gint entries = index_count (playlist->entries); 01407 01408 struct index * reversed = index_new (); 01409 index_allocate (reversed, entries); 01410 01411 for (gint count = entries; count --; ) 01412 index_append (reversed, index_get (playlist->entries, count)); 01413 01414 index_free (playlist->entries); 01415 playlist->entries = reversed; 01416 01417 number_entries (playlist, 0, entries); 01418 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries); 01419 LEAVE; 01420 } 01421 01422 void playlist_randomize (gint playlist_num) 01423 { 01424 ENTER; 01425 DECLARE_PLAYLIST; 01426 LOOKUP_PLAYLIST; 01427 01428 gint entries = index_count (playlist->entries); 01429 01430 for (gint i = 0; i < entries; i ++) 01431 { 01432 gint j = i + rand () % (entries - i); 01433 01434 struct entry * entry = index_get (playlist->entries, j); 01435 index_set (playlist->entries, j, index_get (playlist->entries, i)); 01436 index_set (playlist->entries, i, entry); 01437 } 01438 01439 number_entries (playlist, 0, entries); 01440 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries); 01441 LEAVE; 01442 } 01443 01444 static gint filename_compare (const void * _a, const void * _b, void * _compare) 01445 { 01446 const Entry * a = _a, * b = _b; 01447 gint (* compare) (const gchar * a, const gchar * b) = _compare; 01448 01449 gint diff = compare (a->filename, b->filename); 01450 if (diff) 01451 return diff; 01452 01453 /* preserve order of "equal" entries */ 01454 return a->number - b->number; 01455 } 01456 01457 static gint tuple_compare (const void * _a, const void * _b, void * _compare) 01458 { 01459 const Entry * a = _a, * b = _b; 01460 gint (* compare) (const Tuple * a, const Tuple * b) = _compare; 01461 01462 if (! a->tuple) 01463 return b->tuple ? -1 : 0; 01464 if (! b->tuple) 01465 return 1; 01466 01467 gint diff = compare (a->tuple, b->tuple); 01468 if (diff) 01469 return diff; 01470 01471 /* preserve order of "equal" entries */ 01472 return a->number - b->number; 01473 } 01474 01475 static gint title_compare (const void * _a, const void * _b, void * _compare) 01476 { 01477 const Entry * a = _a, * b = _b; 01478 gint (* compare) (const gchar * a, const gchar * b) = _compare; 01479 01480 gint diff = compare (a->formatted ? a->formatted : a->filename, b->formatted 01481 ? b->formatted : b->filename); 01482 if (diff) 01483 return diff; 01484 01485 /* preserve order of "equal" entries */ 01486 return a->number - b->number; 01487 } 01488 01489 static void sort (Playlist * playlist, gint (* compare) (const void * a, 01490 const void * b, void * inner), void * inner) 01491 { 01492 index_sort_with_data (playlist->entries, compare, inner); 01493 number_entries (playlist, 0, index_count (playlist->entries)); 01494 01495 PLAYLIST_HAS_CHANGED (playlist->number, 0, index_count (playlist->entries)); 01496 } 01497 01498 static void sort_selected (Playlist * playlist, gint (* compare) (const void * 01499 a, const void * b, void * inner), void * inner) 01500 { 01501 gint entries = index_count (playlist->entries); 01502 01503 struct index * selected = index_new (); 01504 index_allocate (selected, playlist->selected_count); 01505 01506 for (gint count = 0; count < entries; count++) 01507 { 01508 Entry * entry = index_get (playlist->entries, count); 01509 if (entry->selected) 01510 index_append (selected, entry); 01511 } 01512 01513 index_sort_with_data (selected, compare, inner); 01514 01515 gint count2 = 0; 01516 for (gint count = 0; count < entries; count++) 01517 { 01518 Entry * entry = index_get (playlist->entries, count); 01519 if (entry->selected) 01520 index_set (playlist->entries, count, index_get (selected, count2 ++)); 01521 } 01522 01523 index_free (selected); 01524 01525 number_entries (playlist, 0, entries); 01526 PLAYLIST_HAS_CHANGED (playlist->number, 0, entries); 01527 } 01528 01529 void playlist_sort_by_filename (gint playlist_num, gint (* compare) 01530 (const gchar * a, const gchar * b)) 01531 { 01532 ENTER; 01533 DECLARE_PLAYLIST; 01534 LOOKUP_PLAYLIST; 01535 01536 sort (playlist, filename_compare, compare); 01537 01538 LEAVE; 01539 } 01540 01541 void playlist_sort_by_tuple (gint playlist_num, gint (* compare) 01542 (const Tuple * a, const Tuple * b)) 01543 { 01544 ENTER; 01545 DECLARE_PLAYLIST; 01546 LOOKUP_PLAYLIST; 01547 01548 check_all_scanned (playlist); 01549 sort (playlist, tuple_compare, compare); 01550 01551 LEAVE; 01552 } 01553 01554 void playlist_sort_by_title (gint playlist_num, gint (* compare) (const gchar * 01555 a, const gchar * b)) 01556 { 01557 ENTER; 01558 DECLARE_PLAYLIST; 01559 LOOKUP_PLAYLIST; 01560 01561 check_all_scanned (playlist); 01562 sort (playlist, title_compare, compare); 01563 01564 LEAVE; 01565 } 01566 01567 void playlist_sort_selected_by_filename (gint playlist_num, gint (* compare) 01568 (const gchar * a, const gchar * b)) 01569 { 01570 ENTER; 01571 DECLARE_PLAYLIST; 01572 LOOKUP_PLAYLIST; 01573 01574 sort_selected (playlist, filename_compare, compare); 01575 01576 LEAVE; 01577 } 01578 01579 void playlist_sort_selected_by_tuple (gint playlist_num, gint (* compare) 01580 (const Tuple * a, const Tuple * b)) 01581 { 01582 ENTER; 01583 DECLARE_PLAYLIST; 01584 LOOKUP_PLAYLIST; 01585 01586 check_selected_scanned (playlist); 01587 sort_selected (playlist, tuple_compare, compare); 01588 01589 LEAVE; 01590 } 01591 01592 void playlist_sort_selected_by_title (gint playlist_num, gint (* compare) 01593 (const gchar * a, const gchar * b)) 01594 { 01595 ENTER; 01596 DECLARE_PLAYLIST; 01597 LOOKUP_PLAYLIST; 01598 01599 check_selected_scanned (playlist); 01600 sort (playlist, title_compare, compare); 01601 01602 LEAVE; 01603 } 01604 01605 void playlist_reformat_titles (void) 01606 { 01607 ENTER; 01608 01609 for (gint playlist_num = 0; playlist_num < index_count (playlists); 01610 playlist_num ++) 01611 { 01612 Playlist * playlist = index_get (playlists, playlist_num); 01613 gint entries = index_count (playlist->entries); 01614 01615 for (gint count = 0; count < entries; count++) 01616 { 01617 Entry * entry = index_get (playlist->entries, count); 01618 g_free (entry->formatted); 01619 entry->formatted = entry->tuple ? title_from_tuple (entry->tuple) : 01620 NULL; 01621 } 01622 } 01623 01624 METADATA_HAS_CHANGED (-1, 0, 0); 01625 LEAVE; 01626 } 01627 01628 static void playlist_rescan_real (gint playlist_num, gboolean selected) 01629 { 01630 ENTER; 01631 DECLARE_PLAYLIST; 01632 LOOKUP_PLAYLIST; 01633 01634 gint entries = index_count (playlist->entries); 01635 01636 for (gint count = 0; count < entries; count ++) 01637 { 01638 Entry * entry = index_get (playlist->entries, count); 01639 if (! selected || entry->selected) 01640 { 01641 entry_set_tuple (playlist, entry, NULL); 01642 entry->failed = FALSE; 01643 } 01644 } 01645 01646 METADATA_HAS_CHANGED (playlist->number, 0, entries); 01647 LEAVE; 01648 } 01649 01650 void playlist_rescan (gint playlist_num) 01651 { 01652 playlist_rescan_real (playlist_num, FALSE); 01653 } 01654 01655 void playlist_rescan_selected (gint playlist_num) 01656 { 01657 playlist_rescan_real (playlist_num, TRUE); 01658 } 01659 01660 void playlist_rescan_file (const gchar * filename) 01661 { 01662 ENTER; 01663 01664 gint num_playlists = index_count (playlists); 01665 01666 gchar * copy = NULL; 01667 if (! uri_is_utf8 (filename, TRUE)) 01668 filename = copy = uri_to_utf8 (filename); 01669 01670 for (gint playlist_num = 0; playlist_num < num_playlists; playlist_num ++) 01671 { 01672 Playlist * playlist = index_get (playlists, playlist_num); 01673 gint num_entries = index_count (playlist->entries); 01674 01675 for (gint entry_num = 0; entry_num < num_entries; entry_num ++) 01676 { 01677 Entry * entry = index_get (playlist->entries, entry_num); 01678 01679 if (! strcmp (entry->filename, filename)) 01680 { 01681 entry_set_tuple (playlist, entry, NULL); 01682 entry->failed = FALSE; 01683 } 01684 } 01685 } 01686 01687 g_free (copy); 01688 01689 METADATA_HAS_CHANGED (-1, 0, 0); 01690 LEAVE; 01691 } 01692 01693 gint64 playlist_get_total_length (gint playlist_num, gboolean fast) 01694 { 01695 ENTER; 01696 DECLARE_PLAYLIST; 01697 LOOKUP_PLAYLIST_RET (0); 01698 01699 if (! fast) 01700 check_all_scanned (playlist); 01701 01702 gint64 length = playlist->total_length; 01703 01704 LEAVE_RET (length); 01705 } 01706 01707 gint64 playlist_get_selected_length (gint playlist_num, gboolean fast) 01708 { 01709 ENTER; 01710 DECLARE_PLAYLIST; 01711 LOOKUP_PLAYLIST_RET (0); 01712 01713 if (! fast) 01714 check_selected_scanned (playlist); 01715 01716 gint64 length = playlist->selected_length; 01717 01718 LEAVE_RET (length); 01719 } 01720 01721 gint playlist_queue_count (gint playlist_num) 01722 { 01723 ENTER; 01724 DECLARE_PLAYLIST; 01725 LOOKUP_PLAYLIST_RET (0); 01726 01727 gint count = g_list_length (playlist->queued); 01728 01729 LEAVE_RET (count); 01730 } 01731 01732 void playlist_queue_insert (gint playlist_num, gint at, gint entry_num) 01733 { 01734 ENTER; 01735 DECLARE_PLAYLIST_ENTRY; 01736 LOOKUP_PLAYLIST_ENTRY; 01737 01738 if (entry->queued) 01739 LEAVE_RET_VOID; 01740 01741 if (at < 0) 01742 playlist->queued = g_list_append (playlist->queued, entry); 01743 else 01744 playlist->queued = g_list_insert (playlist->queued, entry, at); 01745 01746 entry->queued = TRUE; 01747 01748 SELECTION_HAS_CHANGED (playlist->number, entry_num, 1); 01749 LEAVE; 01750 } 01751 01752 void playlist_queue_insert_selected (gint playlist_num, gint at) 01753 { 01754 ENTER; 01755 DECLARE_PLAYLIST; 01756 LOOKUP_PLAYLIST; 01757 01758 gint entries = index_count(playlist->entries); 01759 gint first = entries, last = 0; 01760 01761 for (gint count = 0; count < entries; count++) 01762 { 01763 Entry * entry = index_get (playlist->entries, count); 01764 01765 if (! entry->selected || entry->queued) 01766 continue; 01767 01768 if (at < 0) 01769 playlist->queued = g_list_append (playlist->queued, entry); 01770 else 01771 playlist->queued = g_list_insert (playlist->queued, entry, at++); 01772 01773 entry->queued = TRUE; 01774 first = MIN (first, entry->number); 01775 last = entry->number; 01776 } 01777 01778 if (first < entries) 01779 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first); 01780 01781 LEAVE; 01782 } 01783 01784 gint playlist_queue_get_entry (gint playlist_num, gint at) 01785 { 01786 ENTER; 01787 DECLARE_PLAYLIST; 01788 LOOKUP_PLAYLIST_RET (-1); 01789 01790 GList * node = g_list_nth (playlist->queued, at); 01791 gint entry_num = node ? ((Entry *) node->data)->number : -1; 01792 01793 LEAVE_RET (entry_num); 01794 } 01795 01796 gint playlist_queue_find_entry (gint playlist_num, gint entry_num) 01797 { 01798 ENTER; 01799 DECLARE_PLAYLIST_ENTRY; 01800 LOOKUP_PLAYLIST_ENTRY_RET (-1); 01801 01802 gint pos = entry->queued ? g_list_index (playlist->queued, entry) : -1; 01803 01804 LEAVE_RET (pos); 01805 } 01806 01807 void playlist_queue_delete (gint playlist_num, gint at, gint number) 01808 { 01809 ENTER; 01810 DECLARE_PLAYLIST; 01811 LOOKUP_PLAYLIST; 01812 01813 gint entries = index_count (playlist->entries); 01814 gint first = entries, last = 0; 01815 01816 if (at == 0) 01817 { 01818 while (playlist->queued && number --) 01819 { 01820 Entry * entry = playlist->queued->data; 01821 entry->queued = FALSE; 01822 first = MIN (first, entry->number); 01823 last = entry->number; 01824 01825 playlist->queued = g_list_delete_link (playlist->queued, 01826 playlist->queued); 01827 } 01828 } 01829 else 01830 { 01831 GList * anchor = g_list_nth (playlist->queued, at - 1); 01832 if (! anchor) 01833 goto DONE; 01834 01835 while (anchor->next && number --) 01836 { 01837 Entry * entry = anchor->next->data; 01838 entry->queued = FALSE; 01839 first = MIN (first, entry->number); 01840 last = entry->number; 01841 01842 playlist->queued = g_list_delete_link (playlist->queued, 01843 anchor->next); 01844 } 01845 } 01846 01847 DONE: 01848 if (first < entries) 01849 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first); 01850 01851 LEAVE; 01852 } 01853 01854 void playlist_queue_delete_selected (gint playlist_num) 01855 { 01856 ENTER; 01857 DECLARE_PLAYLIST; 01858 LOOKUP_PLAYLIST; 01859 01860 gint entries = index_count (playlist->entries); 01861 gint first = entries, last = 0; 01862 01863 for (GList * node = playlist->queued; node; ) 01864 { 01865 GList * next = node->next; 01866 Entry * entry = node->data; 01867 01868 if (entry->selected) 01869 { 01870 entry->queued = FALSE; 01871 playlist->queued = g_list_delete_link (playlist->queued, node); 01872 first = MIN (first, entry->number); 01873 last = entry->number; 01874 } 01875 01876 node = next; 01877 } 01878 01879 if (first < entries) 01880 SELECTION_HAS_CHANGED (playlist->number, first, last + 1 - first); 01881 01882 LEAVE; 01883 } 01884 01885 static gboolean shuffle_prev (Playlist * playlist) 01886 { 01887 gint entries = index_count (playlist->entries); 01888 Entry * found = NULL; 01889 01890 for (gint count = 0; count < entries; count ++) 01891 { 01892 Entry * entry = index_get (playlist->entries, count); 01893 01894 if (entry->shuffle_num && (! playlist->position || 01895 entry->shuffle_num < playlist->position->shuffle_num) && (! found 01896 || entry->shuffle_num > found->shuffle_num)) 01897 found = entry; 01898 } 01899 01900 if (! found) 01901 return FALSE; 01902 01903 playlist->position = found; 01904 return TRUE; 01905 } 01906 01907 gboolean playlist_prev_song (gint playlist_num) 01908 { 01909 if (playback_get_playing () && playlist_num == playlist_get_playing ()) 01910 playback_stop (); 01911 01912 ENTER; 01913 DECLARE_PLAYLIST; 01914 LOOKUP_PLAYLIST_RET (FALSE); 01915 01916 if (cfg.shuffle) 01917 { 01918 if (! shuffle_prev (playlist)) 01919 LEAVE_RET (FALSE); 01920 } 01921 else 01922 { 01923 if (! playlist->position || playlist->position->number == 0) 01924 LEAVE_RET (FALSE); 01925 01926 set_position (playlist, index_get (playlist->entries, 01927 playlist->position->number - 1)); 01928 } 01929 01930 LEAVE; 01931 01932 hook_call ("playlist position", GINT_TO_POINTER (playlist_num)); 01933 return TRUE; 01934 } 01935 01936 static gboolean shuffle_next (Playlist * playlist) 01937 { 01938 gint entries = index_count (playlist->entries), choice = 0, count; 01939 Entry * found = NULL; 01940 01941 for (count = 0; count < entries; count ++) 01942 { 01943 Entry * entry = index_get (playlist->entries, count); 01944 01945 if (! entry->shuffle_num) 01946 choice ++; 01947 else if (playlist->position && entry->shuffle_num > 01948 playlist->position->shuffle_num && (! found || entry->shuffle_num 01949 < found->shuffle_num)) 01950 found = entry; 01951 } 01952 01953 if (found) 01954 { 01955 playlist->position = found; 01956 return TRUE; 01957 } 01958 01959 if (! choice) 01960 return FALSE; 01961 01962 choice = rand () % choice; 01963 01964 for (count = 0; ; count ++) 01965 { 01966 Entry * entry = index_get (playlist->entries, count); 01967 01968 if (! entry->shuffle_num) 01969 { 01970 if (! choice) 01971 { 01972 set_position (playlist, entry); 01973 return TRUE; 01974 } 01975 01976 choice --; 01977 } 01978 } 01979 } 01980 01981 static void shuffle_reset (Playlist * playlist) 01982 { 01983 gint entries = index_count (playlist->entries); 01984 01985 playlist->last_shuffle_num = 0; 01986 01987 for (gint count = 0; count < entries; count ++) 01988 { 01989 Entry * entry = index_get (playlist->entries, count); 01990 entry->shuffle_num = 0; 01991 } 01992 } 01993 01994 gboolean playlist_next_song (gint playlist_num, gboolean repeat) 01995 { 01996 if (playback_get_playing () && playlist_num == playlist_get_playing ()) 01997 playback_stop (); 01998 01999 ENTER; 02000 DECLARE_PLAYLIST; 02001 LOOKUP_PLAYLIST_RET (FALSE); 02002 02003 gint entries = index_count(playlist->entries); 02004 02005 if (! entries) 02006 LEAVE_RET (FALSE); 02007 02008 if (playlist->queued) 02009 { 02010 set_position (playlist, playlist->queued->data); 02011 playlist->queued = g_list_remove (playlist->queued, playlist->position); 02012 playlist->position->queued = FALSE; 02013 } 02014 else if (cfg.shuffle) 02015 { 02016 if (! shuffle_next (playlist)) 02017 { 02018 if (! repeat) 02019 LEAVE_RET (FALSE); 02020 02021 shuffle_reset (playlist); 02022 02023 if (! shuffle_next (playlist)) 02024 LEAVE_RET (FALSE); 02025 } 02026 } 02027 else 02028 { 02029 if (! playlist->position) 02030 set_position (playlist, index_get (playlist->entries, 0)); 02031 else if (playlist->position->number == entries - 1) 02032 { 02033 if (! repeat) 02034 LEAVE_RET (FALSE); 02035 02036 set_position (playlist, index_get (playlist->entries, 0)); 02037 } 02038 else 02039 set_position (playlist, index_get (playlist->entries, 02040 playlist->position->number + 1)); 02041 } 02042 02043 LEAVE; 02044 02045 hook_call ("playlist position", GINT_TO_POINTER (playlist_num)); 02046 return TRUE; 02047 } 02048 02049 void playlist_save_state (void) 02050 { 02051 ENTER; 02052 02053 gchar scratch[PATH_MAX]; 02054 snprintf (scratch, sizeof scratch, "%s/" STATE_FILE, 02055 get_path (AUD_PATH_USER_DIR)); 02056 02057 FILE * handle = fopen (scratch, "w"); 02058 if (! handle) 02059 LEAVE_RET_VOID; 02060 02061 fprintf (handle, "active %d\n", active_playlist->number); 02062 fprintf (handle, "playing %d\n", playing_playlist ? playing_playlist->number 02063 : -1); 02064 02065 for (gint playlist_num = 0; playlist_num < index_count (playlists); 02066 playlist_num ++) 02067 { 02068 Playlist * playlist = index_get (playlists, playlist_num); 02069 gint entries = index_count (playlist->entries); 02070 02071 fprintf (handle, "playlist %d\n", playlist_num); 02072 02073 if (playlist->filename) 02074 fprintf (handle, "filename %s\n", playlist->filename); 02075 02076 fprintf (handle, "position %d\n", playlist->position ? 02077 playlist->position->number : -1); 02078 fprintf (handle, "last-shuffled %d\n", playlist->last_shuffle_num); 02079 02080 for (gint count = 0; count < entries; count ++) 02081 { 02082 Entry * entry = index_get (playlist->entries, count); 02083 fprintf (handle, "S %d\n", entry->shuffle_num); 02084 } 02085 } 02086 02087 fclose (handle); 02088 LEAVE; 02089 } 02090 02091 static gchar parse_key[512]; 02092 static gchar * parse_value; 02093 02094 static void parse_next (FILE * handle) 02095 { 02096 parse_value = NULL; 02097 02098 if (! fgets (parse_key, sizeof parse_key, handle)) 02099 return; 02100 02101 gchar * space = strchr (parse_key, ' '); 02102 if (! space) 02103 return; 02104 02105 * space = 0; 02106 parse_value = space + 1; 02107 02108 gchar * newline = strchr (parse_value, '\n'); 02109 if (newline) 02110 * newline = 0; 02111 } 02112 02113 static gboolean parse_integer (const gchar * key, gint * value) 02114 { 02115 return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value, 02116 "%d", value) == 1); 02117 } 02118 02119 static gchar * parse_string (const gchar * key) 02120 { 02121 return (parse_value && ! strcmp (parse_key, key)) ? g_strdup (parse_value) : 02122 NULL; 02123 } 02124 02125 void playlist_load_state (void) 02126 { 02127 ENTER; 02128 gint playlist_num; 02129 02130 gchar scratch[PATH_MAX]; 02131 snprintf (scratch, sizeof scratch, "%s/" STATE_FILE, 02132 get_path (AUD_PATH_USER_DIR)); 02133 02134 FILE * handle = fopen (scratch, "r"); 02135 if (! handle) 02136 LEAVE_RET_VOID; 02137 02138 parse_next (handle); 02139 02140 if (parse_integer ("active", & playlist_num)) 02141 { 02142 if (! (active_playlist = lookup_playlist (playlist_num))) 02143 active_playlist = index_get (playlists, 0); 02144 parse_next (handle); 02145 } 02146 02147 if (parse_integer ("playing", & playlist_num)) 02148 { 02149 playing_playlist = lookup_playlist (playlist_num); 02150 parse_next (handle); 02151 } 02152 02153 while (parse_integer ("playlist", & playlist_num) && playlist_num >= 0 && 02154 playlist_num < index_count (playlists)) 02155 { 02156 Playlist * playlist = index_get (playlists, playlist_num); 02157 gint entries = index_count (playlist->entries), position, count; 02158 gchar * s; 02159 02160 parse_next (handle); 02161 02162 if ((s = parse_string ("filename"))) 02163 { 02164 g_free (playlist->filename); 02165 playlist->filename = s; 02166 parse_next (handle); 02167 } 02168 02169 if (parse_integer ("position", & position)) 02170 parse_next (handle); 02171 02172 if (position >= 0 && position < entries) 02173 playlist->position = index_get (playlist->entries, position); 02174 02175 if (parse_integer ("last-shuffled", & playlist->last_shuffle_num)) 02176 parse_next (handle); 02177 02178 for (count = 0; count < entries; count ++) 02179 { 02180 Entry * entry = index_get (playlist->entries, count); 02181 if (parse_integer ("S", & entry->shuffle_num)) 02182 parse_next (handle); 02183 } 02184 } 02185 02186 fclose (handle); 02187 LEAVE; 02188 }