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