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