Mercurial > audlegacy
annotate src/audacious/playlist.c @ 2574:40407b7363f3 trunk
[svn] - enforce playback stop when clearing the playlist to load new files in. closes #808.
| author | nenolod |
|---|---|
| date | Sun, 25 Feb 2007 00:53:19 -0800 |
| parents | 7b1b52eb663b |
| children | 9713b5a67cba |
| rev | line source |
|---|---|
| 2313 | 1 /* Audacious |
| 2 * Copyright (C) 2005-2007 Audacious team. | |
| 3 * | |
| 4 * BMP (C) GPL 2003 $top_src_dir/AUTHORS | |
| 5 * | |
| 6 * based on: | |
| 7 * | |
| 8 * XMMS - Cross-platform multimedia player | |
| 9 * Copyright (C) 1998-2003 Peter Alm, Mikael Alm, Olle Hallnas, | |
| 10 * Thomas Nilsson and 4Front Technologies | |
| 11 * Copyright (C) 1999-2003 Haavard Kvaalen | |
| 12 * | |
| 13 * This program is free software; you can redistribute it and/or modify | |
| 14 * it under the terms of the GNU General Public License as published by | |
| 15 * the Free Software Foundation; under version 2 of the License. | |
| 16 * | |
| 17 * This program is distributed in the hope that it will be useful, | |
| 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 20 * GNU General Public License for more details. | |
| 21 * | |
| 22 * You should have received a copy of the GNU General Public License | |
| 23 * along with this program; if not, write to the Free Software | |
| 24 * Foundation, Inc., 59 Tmple Place - Suite 330, Boston, MA 02110-1301, USA. | |
| 25 */ | |
| 26 | |
| 27 #ifdef HAVE_CONFIG_H | |
| 28 # include "config.h" | |
| 29 #endif | |
| 30 | |
| 31 #include "playlist.h" | |
| 32 | |
| 33 #include <glib.h> | |
| 34 #include <glib/gprintf.h> | |
| 35 #include <stdlib.h> | |
| 36 #include <string.h> | |
| 37 #include <time.h> | |
| 38 | |
| 39 #include <unistd.h> | |
| 40 #include <sys/types.h> | |
| 41 #include <sys/stat.h> | |
| 42 #include <sys/errno.h> | |
| 43 | |
| 44 #if defined(USE_REGEX_ONIGURUMA) | |
| 45 #include <onigposix.h> | |
| 46 #elif defined(USE_REGEX_PCRE) | |
| 47 #include <pcreposix.h> | |
| 48 #else | |
| 49 #include <regex.h> | |
| 50 #endif | |
| 51 | |
| 52 #include "input.h" | |
| 53 #include "main.h" | |
| 54 #include "ui_main.h" | |
|
2416
0fd7f4f969ad
[svn] integrated urldecode.* from libaudacious into audacious directory, made separate ui_fileopener.*
mf0102
parents:
2408
diff
changeset
|
55 #include "util.h" |
| 2313 | 56 #include "libaudacious/configdb.h" |
| 57 #include "vfs.h" | |
|
2416
0fd7f4f969ad
[svn] integrated urldecode.* from libaudacious into audacious directory, made separate ui_fileopener.*
mf0102
parents:
2408
diff
changeset
|
58 #include "urldecode.h" |
| 2313 | 59 #include "ui_equalizer.h" |
| 60 #include "playback.h" | |
| 61 #include "playlist.h" | |
| 62 #include "playlist_container.h" | |
|
2499
15a1f5ee4d1c
[svn] - playlist_manager -> ui_playlist_manager, since it's a UI component.
nenolod
parents:
2493
diff
changeset
|
63 #include "ui_playlist_manager.h" |
| 2313 | 64 #include "ui_playlist.h" |
|
2373
ad1d7687814c
[svn] made strings.h for existing strings.c, cleanups
mf0102
parents:
2328
diff
changeset
|
65 #include "strings.h" |
| 2313 | 66 #include "ui_fileinfo.h" |
| 67 | |
| 68 #include "debug.h" | |
| 69 | |
|
2407
1dc1d36d0347
[svn] - add hooks: playback begin, playback end, playlist reached end
nenolod
parents:
2380
diff
changeset
|
70 #include "hook.h" |
|
1dc1d36d0347
[svn] - add hooks: playback begin, playback end, playlist reached end
nenolod
parents:
2380
diff
changeset
|
71 |
| 2313 | 72 typedef gint (*PlaylistCompareFunc) (PlaylistEntry * a, PlaylistEntry * b); |
| 73 typedef void (*PlaylistSaveFunc) (FILE * file); | |
| 74 | |
| 75 /* If we manually change the song, p_p_b_j will show us where to go back to */ | |
| 76 PlaylistEntry *playlist_position_before_jump = NULL; | |
| 77 | |
| 78 static GList *playlists = NULL; | |
| 79 static GList *playlists_iter; | |
| 80 | |
| 81 /* If this is set to TRUE, we do not probe upon playlist add. | |
| 82 * | |
| 83 * Under Audacious 0.1.x, this was not a big deal because we used | |
| 84 * file extension introspection instead of looking for file format magic | |
| 85 * strings. | |
| 86 * | |
| 87 * Because we use file magic strings, we have to fstat a file being added | |
| 88 * to a playlist up to 1 * <number of input plugins installed> times. | |
| 89 * | |
| 90 * This can get really slow now that we're looking for files to add to a | |
| 91 * playlist. (Up to 5 minutes for 5000 songs, etcetera.) | |
| 92 * | |
| 93 * So, we obviously don't want to probe while opening a large playlist | |
| 94 * up. Hince the boolean below. | |
| 95 * | |
| 96 * January 7, 2006, William Pitcock <nenolod@nenolod.net> | |
| 97 */ | |
| 98 | |
| 99 G_LOCK_DEFINE(playlist_get_info_going); | |
| 100 | |
| 2328 | 101 //static gchar *playlist_current_name = NULL; |
| 2313 | 102 |
| 103 static gboolean playlist_get_info_scan_active = FALSE; | |
| 104 static gboolean playlist_get_info_going = FALSE; | |
| 105 static GThread *playlist_get_info_thread; | |
| 106 | |
| 107 static gint path_compare(const gchar * a, const gchar * b); | |
| 108 static gint playlist_compare_path(PlaylistEntry * a, PlaylistEntry * b); | |
| 109 static gint playlist_compare_filename(PlaylistEntry * a, PlaylistEntry * b); | |
| 110 static gint playlist_compare_title(PlaylistEntry * a, PlaylistEntry * b); | |
| 111 static gint playlist_compare_artist(PlaylistEntry * a, PlaylistEntry * b); | |
| 112 static time_t playlist_get_mtime(const gchar *filename); | |
| 113 static gint playlist_compare_date(PlaylistEntry * a, PlaylistEntry * b); | |
| 114 static gint playlist_compare_track(PlaylistEntry * a, PlaylistEntry * b); | |
| 115 static gint playlist_compare_playlist(PlaylistEntry * a, PlaylistEntry * b); | |
| 116 | |
| 117 static gint playlist_dupscmp_path(PlaylistEntry * a, PlaylistEntry * b); | |
| 118 static gint playlist_dupscmp_filename(PlaylistEntry * a, PlaylistEntry * b); | |
| 119 static gint playlist_dupscmp_title(PlaylistEntry * a, PlaylistEntry * b); | |
| 120 | |
| 121 static PlaylistCompareFunc playlist_compare_func_table[] = { | |
| 122 playlist_compare_path, | |
| 123 playlist_compare_filename, | |
| 124 playlist_compare_title, | |
| 125 playlist_compare_artist, | |
| 126 playlist_compare_date, | |
| 127 playlist_compare_track, | |
| 128 playlist_compare_playlist | |
| 129 }; | |
| 130 | |
| 131 static guint playlist_load_ins(Playlist * playlist, const gchar * filename, gint pos); | |
| 132 | |
| 133 static void playlist_generate_shuffle_list(Playlist *); | |
| 134 static void playlist_generate_shuffle_list_nolock(Playlist *); | |
| 135 | |
| 136 static void playlist_recalc_total_time_nolock(Playlist *); | |
| 137 static void playlist_recalc_total_time(Playlist *); | |
| 138 static gboolean playlist_entry_get_info(PlaylistEntry * entry); | |
| 139 | |
| 140 /* *********************** playlist entry code ********************** */ | |
| 141 | |
| 142 PlaylistEntry * | |
| 143 playlist_entry_new(const gchar * filename, | |
| 144 const gchar * title, | |
| 145 const gint length, | |
| 146 InputPlugin * dec) | |
| 147 { | |
| 148 PlaylistEntry *entry; | |
| 149 | |
| 150 entry = g_new0(PlaylistEntry, 1); | |
| 151 entry->filename = g_strdup(filename); | |
| 152 entry->title = str_to_utf8(title); | |
| 153 entry->length = length; | |
| 154 entry->selected = FALSE; | |
| 155 entry->decoder = dec; | |
| 156 | |
| 157 /* only do this if we have a decoder, otherwise it just takes too long */ | |
| 158 if (entry->decoder) | |
| 159 playlist_entry_get_info(entry); | |
| 160 | |
| 161 return entry; | |
| 162 } | |
| 163 | |
| 164 void | |
| 165 playlist_entry_free(PlaylistEntry * entry) | |
| 166 { | |
| 167 if (!entry) | |
| 168 return; | |
| 169 | |
| 170 if (entry->tuple != NULL) { | |
| 171 bmp_title_input_free(entry->tuple); | |
| 172 entry->tuple = NULL; | |
| 173 } | |
| 174 | |
| 175 if (entry->filename != NULL) | |
| 176 g_free(entry->filename); | |
| 177 | |
| 178 if (entry->title != NULL) | |
| 179 g_free(entry->title); | |
| 180 | |
| 181 g_free(entry); | |
| 182 } | |
| 183 | |
| 184 static gboolean | |
| 185 playlist_entry_get_info(PlaylistEntry * entry) | |
| 186 { | |
| 187 TitleInput *tuple; | |
| 188 time_t modtime; | |
| 189 | |
| 190 g_return_val_if_fail(entry != NULL, FALSE); | |
| 191 | |
|
2433
6cdccf094e51
[svn] - Why are we probing streams so unnecessarily often?
nhjm449
parents:
2416
diff
changeset
|
192 // Let's not randomly probe streams, mmkay? |
|
6cdccf094e51
[svn] - Why are we probing streams so unnecessarily often?
nhjm449
parents:
2416
diff
changeset
|
193 if (entry->decoder == NULL && (str_has_prefix_nocase(entry->filename, "http://") || str_has_prefix_nocase(entry->filename, "https://"))) |
|
6cdccf094e51
[svn] - Why are we probing streams so unnecessarily often?
nhjm449
parents:
2416
diff
changeset
|
194 return TRUE; |
|
6cdccf094e51
[svn] - Why are we probing streams so unnecessarily often?
nhjm449
parents:
2416
diff
changeset
|
195 |
| 2313 | 196 if (entry->tuple == NULL || entry->tuple->mtime > 0 || entry->tuple->mtime == -1) |
| 197 modtime = playlist_get_mtime(entry->filename); | |
| 198 else | |
| 199 modtime = 0; /* URI -nenolod */ | |
| 200 | |
| 201 if (entry->decoder == NULL) | |
| 202 entry->decoder = input_check_file(entry->filename, FALSE); | |
| 203 | |
| 204 /* renew tuple if file mtime is newer than tuple mtime. */ | |
| 205 if(entry->tuple){ | |
| 206 if(entry->tuple->mtime == modtime) | |
| 207 return TRUE; | |
| 208 else { | |
| 209 bmp_title_input_free(entry->tuple); | |
| 210 entry->tuple = NULL; | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 if (entry->decoder == NULL || entry->decoder->get_song_tuple == NULL) | |
| 215 tuple = input_get_song_tuple(entry->filename); | |
| 216 else | |
| 217 tuple = entry->decoder->get_song_tuple(entry->filename); | |
| 218 | |
| 219 if (tuple == NULL) | |
| 220 return FALSE; | |
| 221 | |
| 222 /* attach mtime */ | |
| 223 tuple->mtime = modtime; | |
| 224 | |
| 225 /* entry is still around */ | |
| 226 entry->title = xmms_get_titlestring(tuple->formatter != NULL ? tuple->formatter : xmms_get_gentitle_format(), tuple); | |
| 227 entry->length = tuple->length; | |
| 228 entry->tuple = tuple; | |
| 229 | |
| 230 return TRUE; | |
| 231 } | |
| 232 | |
| 233 /* *********************** playlist selector code ************************* */ | |
| 234 | |
| 235 void | |
| 236 playlist_init(void) | |
| 237 { | |
| 238 Playlist *initial_pl; | |
| 239 | |
| 240 /* FIXME: is this really necessary? REQUIRE_STATIC_LOCK(playlists); */ | |
| 241 | |
| 242 initial_pl = playlist_new(); | |
| 243 | |
| 244 playlist_add_playlist(initial_pl); | |
| 245 } | |
| 246 | |
| 247 void | |
| 248 playlist_add_playlist(Playlist *playlist) | |
| 249 { | |
| 250 playlists = g_list_append(playlists, playlist); | |
| 251 | |
| 252 if (playlists_iter == NULL) | |
| 253 playlists_iter = playlists; | |
| 254 | |
| 255 playlist_manager_update(); | |
| 256 } | |
| 257 | |
| 258 void | |
| 259 playlist_remove_playlist(Playlist *playlist) | |
| 260 { | |
| 261 /* trying to free the last playlist simply clears and resets it */ | |
| 262 if (g_list_length(playlists) < 2) { | |
| 263 playlist_clear(playlist); | |
| 264 playlist_set_current_name(playlist, NULL); | |
| 265 return; | |
| 266 } | |
| 267 | |
| 268 if (playlist == playlist_get_active()) | |
| 269 playlist_select_next(); | |
| 270 | |
| 271 /* upon removal, a playlist should be cleared and freed */ | |
| 272 playlists = g_list_remove(playlists, playlist); | |
| 273 playlist_clear(playlist); | |
| 274 playlist_free(playlist); | |
| 275 | |
| 276 if (playlists_iter == NULL) | |
| 277 playlists_iter = playlists; | |
| 278 | |
| 279 playlist_manager_update(); | |
| 280 } | |
| 281 | |
| 282 GList * | |
| 283 playlist_get_playlists(void) | |
| 284 { | |
| 285 return playlists; | |
| 286 } | |
| 287 | |
| 288 void | |
| 289 playlist_select_next(void) | |
| 290 { | |
| 291 if (playlists_iter == NULL) | |
| 292 playlists_iter = playlists; | |
| 293 | |
| 294 playlists_iter = g_list_next(playlists_iter); | |
| 295 | |
| 296 if (playlists_iter == NULL) | |
| 297 playlists_iter = playlists; | |
| 298 | |
| 299 playlistwin_update_list(playlist_get_active()); | |
| 300 } | |
| 301 | |
| 302 void | |
| 303 playlist_select_prev(void) | |
| 304 { | |
| 305 if (playlists_iter == NULL) | |
| 306 playlists_iter = playlists; | |
| 307 | |
| 308 playlists_iter = g_list_previous(playlists_iter); | |
| 309 | |
| 310 if (playlists_iter == NULL) | |
| 311 playlists_iter = playlists; | |
| 312 | |
| 313 playlistwin_update_list(playlist_get_active()); | |
| 314 } | |
| 315 | |
| 316 void | |
| 317 playlist_select_playlist(Playlist *playlist) | |
| 318 { | |
| 319 if (playlists_iter == NULL) | |
| 320 playlists_iter = playlists; | |
| 321 | |
| 322 playlists_iter = g_list_find(playlists, playlist); | |
| 323 | |
| 324 if (playlists_iter == NULL) | |
| 325 playlists_iter = playlists; | |
| 326 | |
| 327 playlistwin_update_list(playlist); | |
| 328 } | |
| 329 | |
| 330 /* *********************** playlist code ********************** */ | |
| 331 | |
| 332 const gchar * | |
| 333 playlist_get_current_name(Playlist *playlist) | |
| 334 { | |
| 335 return playlist->title; | |
| 336 } | |
| 337 | |
| 338 gboolean | |
| 339 playlist_set_current_name(Playlist *playlist, const gchar * filename) | |
| 340 { | |
| 341 if (playlist->title) | |
| 342 g_free(playlist->title); | |
| 343 | |
| 344 if (!filename) { | |
| 345 playlist->title = NULL; | |
| 346 return FALSE; | |
| 347 } | |
| 348 | |
|
2504
fcf730269639
[svn] - convert filename string to utf8 for playlist->title (closes bug #789)
giacomo
parents:
2499
diff
changeset
|
349 playlist->title = filename_to_utf8(filename); |
| 2313 | 350 return TRUE; |
| 351 } | |
| 352 | |
| 353 static GList * | |
| 354 find_playlist_position_list(Playlist *playlist) | |
| 355 { | |
| 356 REQUIRE_LOCK(playlist->mutex); | |
| 357 | |
| 358 if (!playlist->position) { | |
| 359 if (cfg.shuffle) | |
| 360 return playlist->shuffle; | |
| 361 else | |
| 362 return playlist->entries; | |
| 363 } | |
| 364 | |
| 365 if (cfg.shuffle) | |
| 366 return g_list_find(playlist->shuffle, playlist->position); | |
| 367 else | |
| 368 return g_list_find(playlist->entries, playlist->position); | |
| 369 } | |
| 370 | |
| 371 static void | |
| 372 play_queued(Playlist *playlist) | |
| 373 { | |
| 374 GList *tmp = playlist->queue; | |
| 375 | |
| 376 REQUIRE_LOCK( playlist->mutex ); | |
| 377 | |
| 378 playlist->position = playlist->queue->data; | |
| 379 playlist->queue = g_list_remove_link(playlist->queue, playlist->queue); | |
| 380 g_list_free_1(tmp); | |
| 381 } | |
| 382 | |
| 383 void | |
| 384 playlist_clear(Playlist *playlist) | |
| 385 { | |
| 386 if (!playlist) | |
| 387 return; | |
| 388 | |
| 389 PLAYLIST_LOCK( playlist->mutex ); | |
| 390 | |
| 391 g_list_foreach(playlist->entries, (GFunc) playlist_entry_free, NULL); | |
| 392 g_list_free(playlist->entries); | |
| 393 playlist->position = NULL; | |
| 394 playlist->entries = NULL; | |
|
2549
ef59b072a5d2
[svn] - update playlist->tail when an entry has been removed.
yaz
parents:
2548
diff
changeset
|
395 playlist->tail = NULL; |
| 2313 | 396 |
| 397 PLAYLIST_UNLOCK( playlist->mutex ); | |
| 398 | |
| 399 playlist_generate_shuffle_list(playlist); | |
| 400 playlistwin_update_list(playlist); | |
| 401 playlist_recalc_total_time(playlist); | |
| 402 playlist_manager_update(); | |
| 403 } | |
| 404 | |
| 405 static void | |
| 406 playlist_delete_node(Playlist * playlist, GList * node, gboolean * set_info_text, | |
| 407 gboolean * restart_playing) | |
| 408 { | |
| 409 PlaylistEntry *entry; | |
| 410 GList *playing_song = NULL; | |
| 411 | |
| 412 REQUIRE_LOCK(playlist->mutex); | |
| 413 | |
| 414 /* We call g_list_find manually here because we don't want an item | |
| 415 * in the shuffle_list */ | |
| 416 | |
| 417 if (playlist->position) | |
| 418 playing_song = g_list_find(playlist->entries, playlist->position); | |
| 419 | |
| 420 entry = PLAYLIST_ENTRY(node->data); | |
| 421 | |
| 422 if (playing_song == node) { | |
| 423 *set_info_text = TRUE; | |
| 424 | |
| 425 if (playback_get_playing()) { | |
| 426 PLAYLIST_UNLOCK(playlist->mutex); | |
| 427 ip_data.stop = TRUE; | |
| 428 playback_stop(); | |
| 429 ip_data.stop = FALSE; | |
| 430 PLAYLIST_LOCK(playlist->mutex); | |
| 431 *restart_playing = TRUE; | |
| 432 } | |
| 433 | |
| 434 playing_song = find_playlist_position_list(playlist); | |
| 435 | |
| 436 if (g_list_next(playing_song)) | |
| 437 playlist->position = g_list_next(playing_song)->data; | |
| 438 else if (g_list_previous(playing_song)) | |
| 439 playlist->position = g_list_previous(playing_song)->data; | |
| 440 else | |
| 441 playlist->position = NULL; | |
| 442 | |
| 443 /* Make sure the entry did not disappear under us */ | |
| 444 if (g_list_index(playlist->entries, entry) == -1) | |
| 445 return; | |
| 446 | |
| 447 } | |
| 448 else if (g_list_position(playlist->entries, playing_song) > | |
| 449 g_list_position(playlist->entries, node)) { | |
| 450 *set_info_text = TRUE; | |
| 451 } | |
| 452 | |
| 453 playlist->shuffle = g_list_remove(playlist->shuffle, entry); | |
| 454 playlist->queue = g_list_remove(playlist->queue, entry); | |
| 455 playlist->entries = g_list_remove_link(playlist->entries, node); | |
|
2549
ef59b072a5d2
[svn] - update playlist->tail when an entry has been removed.
yaz
parents:
2548
diff
changeset
|
456 playlist->tail = g_list_last(playlist->entries); |
| 2313 | 457 playlist_entry_free(entry); |
| 458 g_list_free_1(node); | |
| 459 | |
| 460 playlist_recalc_total_time_nolock(playlist); | |
| 461 } | |
| 462 | |
| 463 void | |
| 464 playlist_delete_index(Playlist *playlist, guint pos) | |
| 465 { | |
| 466 gboolean restart_playing = FALSE, set_info_text = FALSE; | |
| 467 GList *node; | |
| 468 | |
| 469 if (!playlist) | |
| 470 return; | |
| 471 | |
| 472 PLAYLIST_LOCK(playlist->mutex); | |
| 473 | |
| 474 node = g_list_nth(playlist->entries, pos); | |
| 475 | |
| 476 if (!node) { | |
| 477 PLAYLIST_UNLOCK(playlist->mutex); | |
| 478 return; | |
| 479 } | |
| 480 | |
| 481 playlist_delete_node(playlist, node, &set_info_text, &restart_playing); | |
| 482 | |
| 483 PLAYLIST_UNLOCK(playlist->mutex); | |
| 484 | |
| 485 playlist_recalc_total_time(playlist); | |
| 486 | |
| 487 playlistwin_update_list(playlist); | |
| 488 if (restart_playing) { | |
| 489 if (playlist->position) { | |
| 490 playback_initiate(); | |
| 491 } | |
| 492 else { | |
| 493 mainwin_clear_song_info(); | |
| 494 } | |
| 495 } | |
| 496 else if (set_info_text) { | |
| 497 mainwin_set_info_text(); | |
| 498 } | |
| 499 | |
| 500 playlist_manager_update(); | |
| 501 } | |
| 502 | |
| 503 void | |
| 504 playlist_delete_filenames(Playlist * playlist, GList * filenames) | |
| 505 { | |
| 506 GList *node, *fnode; | |
| 507 gboolean set_info_text = FALSE, restart_playing = FALSE; | |
| 508 | |
| 509 PLAYLIST_LOCK(playlist->mutex); | |
| 510 | |
| 511 for (fnode = filenames; fnode; fnode = g_list_next(fnode)) { | |
| 512 node = playlist->entries; | |
| 513 | |
| 514 while (node) { | |
| 515 GList *next = g_list_next(node); | |
| 516 PlaylistEntry *entry = node->data; | |
| 517 | |
| 518 if (!strcmp(entry->filename, fnode->data)) | |
| 519 playlist_delete_node(playlist, node, &set_info_text, &restart_playing); | |
| 520 | |
| 521 node = next; | |
| 522 } | |
| 523 } | |
| 524 | |
| 525 playlist_recalc_total_time(playlist); | |
| 526 PLAYLIST_UNLOCK(playlist->mutex); | |
| 527 | |
| 528 playlistwin_update_list(playlist); | |
| 529 | |
| 530 if (restart_playing) { | |
| 531 if (playlist->position) { | |
| 532 playback_initiate(); | |
| 533 } | |
| 534 else { | |
| 535 mainwin_clear_song_info(); | |
| 536 } | |
| 537 } | |
| 538 else if (set_info_text) { | |
| 539 mainwin_set_info_text(); | |
| 540 } | |
| 541 | |
| 542 playlist_manager_update(); | |
| 543 } | |
| 544 | |
| 545 void | |
| 546 playlist_delete(Playlist * playlist, gboolean crop) | |
| 547 { | |
| 548 gboolean restart_playing = FALSE, set_info_text = FALSE; | |
| 549 GList *node, *next_node; | |
| 550 PlaylistEntry *entry; | |
| 551 | |
| 552 g_return_if_fail(playlist != NULL); | |
| 553 | |
| 554 PLAYLIST_LOCK(playlist->mutex); | |
| 555 | |
| 556 node = playlist->entries; | |
| 557 | |
| 558 while (node) { | |
| 559 entry = PLAYLIST_ENTRY(node->data); | |
| 560 | |
| 561 next_node = g_list_next(node); | |
| 562 | |
| 563 if ((entry->selected && !crop) || (!entry->selected && crop)) { | |
| 564 playlist_delete_node(playlist, node, &set_info_text, &restart_playing); | |
| 565 } | |
| 566 | |
| 567 node = next_node; | |
| 568 } | |
| 569 | |
| 570 PLAYLIST_UNLOCK(playlist->mutex); | |
| 571 | |
| 572 playlist_recalc_total_time(playlist); | |
| 573 | |
| 574 if (set_info_text) { | |
| 575 mainwin_set_info_text(); | |
| 576 } | |
| 577 | |
| 578 if (restart_playing) { | |
| 579 if (playlist->position) { | |
| 580 playback_initiate(); | |
| 581 } | |
| 582 else { | |
| 583 mainwin_clear_song_info(); | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 playlistwin_update_list(playlist); | |
| 588 playlist_manager_update(); | |
| 589 } | |
| 590 | |
| 591 static void | |
| 592 __playlist_ins_with_info(Playlist * playlist, | |
|
2545
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
593 const gchar * filename, |
| 2313 | 594 gint pos, |
| 595 const gchar * title, | |
| 596 gint len, | |
|
2545
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
597 InputPlugin * dec) |
| 2313 | 598 { |
| 599 g_return_if_fail(filename != NULL); | |
| 600 | |
| 601 PLAYLIST_LOCK(playlist->mutex); | |
| 602 playlist->entries = g_list_insert(playlist->entries, | |
| 603 playlist_entry_new(filename, title, len, dec), | |
| 604 pos); | |
| 605 PLAYLIST_UNLOCK(playlist->mutex); | |
| 606 | |
| 607 g_mutex_lock(mutex_scan); | |
| 608 playlist_get_info_scan_active = TRUE; | |
| 609 g_mutex_unlock(mutex_scan); | |
| 610 g_cond_signal(cond_scan); | |
| 611 } | |
| 612 | |
| 613 static void | |
| 614 __playlist_ins_with_info_tuple(Playlist * playlist, | |
| 615 const gchar * filename, | |
| 616 gint pos, | |
| 617 TitleInput *tuple, | |
| 618 InputPlugin * dec) | |
| 619 { | |
| 620 PlaylistEntry *entry; | |
| 621 | |
| 622 g_return_if_fail(playlist != NULL); | |
| 623 g_return_if_fail(filename != NULL); | |
| 624 | |
|
2548
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
625 entry = playlist_entry_new(filename, tuple->track_name, tuple->length, dec); |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
626 if(!playlist->tail) |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
627 playlist->tail = g_list_last(playlist->entries); |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
628 |
| 2313 | 629 PLAYLIST_LOCK(playlist->mutex); |
|
2548
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
630 |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
631 if(pos == -1) { // the common case |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
632 GList *element; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
633 element = g_list_alloc(); |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
634 element->data = entry; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
635 element->prev = playlist->tail; // NULL is allowed here. |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
636 element->next = NULL; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
637 |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
638 if(!playlist->entries) { // this is the first element |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
639 playlist->entries = element; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
640 playlist->tail = element; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
641 } |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
642 else { // the rests |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
643 g_return_if_fail(playlist->tail != NULL); |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
644 playlist->tail->next = element; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
645 playlist->tail = element; |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
646 } |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
647 } |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
648 else { |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
649 playlist->entries = g_list_insert(playlist->entries, entry, pos); |
|
68d1e9761cc5
[svn] - highly experimental code to speed up loading huge playlist. let me know if it breaks something.
yaz
parents:
2545
diff
changeset
|
650 } |
| 2313 | 651 |
| 652 if (tuple != NULL) { | |
| 653 entry->title = xmms_get_titlestring(tuple->formatter != NULL ? tuple->formatter : xmms_get_gentitle_format(), tuple); | |
| 654 entry->length = tuple->length; | |
| 655 entry->tuple = tuple; | |
| 656 } | |
| 657 | |
| 658 PLAYLIST_UNLOCK(playlist->mutex); | |
|
2545
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
659 if(tuple->mtime == -1) { // kick the scanner thread only if mtime = -1 (uninitialized). |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
660 g_mutex_lock(mutex_scan); |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
661 playlist_get_info_scan_active = TRUE; |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
662 g_mutex_unlock(mutex_scan); |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
663 g_cond_signal(cond_scan); |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
664 } |
| 2313 | 665 } |
| 666 | |
| 667 static void | |
| 668 __playlist_ins(Playlist * playlist, const gchar * filename, gint pos, InputPlugin *dec) | |
| 669 { | |
| 670 __playlist_ins_with_info(playlist, filename, pos, NULL, -1, dec); | |
| 671 playlist_recalc_total_time(playlist); | |
| 672 playlist_manager_update(); | |
| 673 } | |
| 674 | |
| 675 gboolean | |
| 676 playlist_ins(Playlist * playlist, const gchar * filename, gint pos) | |
| 677 { | |
| 678 gchar buf[64], *p; | |
| 679 gint r; | |
| 680 VFSFile *file; | |
| 681 InputPlugin *dec; | |
| 682 | |
| 683 g_return_val_if_fail(playlist != NULL, FALSE); | |
| 684 g_return_val_if_fail(filename != NULL, FALSE); | |
| 685 | |
| 686 if (is_playlist_name(filename)) { | |
| 687 playlist->loading_playlist = TRUE; | |
| 688 playlist_load_ins(playlist, filename, pos); | |
| 689 playlist->loading_playlist = FALSE; | |
| 690 return TRUE; | |
| 691 } | |
| 692 | |
| 693 if (playlist->loading_playlist == TRUE || cfg.playlist_detect == TRUE) | |
| 694 dec = NULL; | |
| 695 else | |
| 696 dec = input_check_file(filename, TRUE); | |
| 697 | |
| 698 if (cfg.playlist_detect == TRUE || playlist->loading_playlist == TRUE || (playlist->loading_playlist == FALSE && dec != NULL)) | |
| 699 { | |
|
2545
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
700 __playlist_ins(playlist, filename, pos, dec); |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
701 playlist_generate_shuffle_list(playlist); |
|
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
702 playlistwin_update_list(playlist); |
| 2313 | 703 return TRUE; |
| 704 } | |
| 705 | |
| 706 /* Some files (typically produced by some cgi-scripts) don't have | |
| 707 * the correct extension. Try to recognize these files by looking | |
| 708 * at their content. We only check for http entries since it does | |
| 709 * not make sense to have file entries in a playlist fetched from | |
| 710 * the net. */ | |
| 711 | |
| 712 /* Some strange people put fifo's with the .mp3 extension, so we | |
| 713 * need to make sure it's a real file (otherwise fread() may block | |
| 714 * and stall the entire program) */ | |
| 715 | |
| 716 /* FIXME: bah, FIFOs actually pass this regular file test */ | |
| 717 if (!vfs_file_test(filename, G_FILE_TEST_IS_REGULAR)) | |
| 718 return FALSE; | |
| 719 | |
| 720 if (!(file = vfs_fopen(filename, "rb"))) | |
| 721 return FALSE; | |
| 722 | |
| 723 r = vfs_fread(buf, 1, sizeof(buf), file); | |
| 724 vfs_fclose(file); | |
| 725 | |
| 726 for (p = buf; r-- > 0 && (*p == '\r' || *p == '\n'); p++); | |
| 727 | |
| 728 if (r > 5 && str_has_prefix_nocase(p, "http:")) { | |
| 729 playlist_load_ins(playlist, filename, pos); | |
| 730 return TRUE; | |
| 731 } | |
| 732 | |
|
2493
b7f48f00a342
[svn] - patch from Mark Glines to ad https:// URI support to playlists.
nenolod
parents:
2489
diff
changeset
|
733 if (r > 6 && str_has_prefix_nocase(p, "https:")) { |
|
b7f48f00a342
[svn] - patch from Mark Glines to ad https:// URI support to playlists.
nenolod
parents:
2489
diff
changeset
|
734 playlist_load_ins(playlist, filename, pos); |
|
b7f48f00a342
[svn] - patch from Mark Glines to ad https:// URI support to playlists.
nenolod
parents:
2489
diff
changeset
|
735 return TRUE; |
|
b7f48f00a342
[svn] - patch from Mark Glines to ad https:// URI support to playlists.
nenolod
parents:
2489
diff
changeset
|
736 } |
|
b7f48f00a342
[svn] - patch from Mark Glines to ad https:// URI support to playlists.
nenolod
parents:
2489
diff
changeset
|
737 |
| 2313 | 738 return FALSE; |
| 739 } | |
| 740 | |
| 741 /* FIXME: The next few functions are specific to Unix | |
| 742 * filesystems. Either abstract it away, or don't even bother checking | |
| 743 * at such low level */ | |
| 744 | |
| 745 typedef struct { | |
| 746 dev_t dev; | |
| 747 ino_t ino; | |
| 748 } DeviceInode; | |
| 749 | |
| 750 static DeviceInode * | |
| 751 devino_new(dev_t device, | |
| 752 ino_t inode) | |
| 753 { | |
| 754 DeviceInode *devino = g_new0(DeviceInode, 1); | |
| 755 | |
| 756 if (devino) | |
| 757 { | |
| 758 devino->dev = device; | |
| 759 devino->ino = inode; | |
| 760 } | |
| 761 | |
| 762 return devino; | |
| 763 } | |
| 764 | |
| 765 static guint | |
| 766 devino_hash(gconstpointer key) | |
| 767 { | |
| 768 const DeviceInode *d = key; | |
| 769 return d->ino; | |
| 770 } | |
| 771 | |
| 772 static gint | |
| 773 devino_compare(gconstpointer a, | |
| 774 gconstpointer b) | |
| 775 { | |
| 776 const DeviceInode *da = a, *db = b; | |
| 777 return (da->dev == db->dev && da->ino == db->ino); | |
| 778 } | |
| 779 | |
| 780 static gboolean | |
| 781 devino_destroy(gpointer key, | |
| 782 gpointer value, | |
| 783 gpointer data) | |
| 784 { | |
| 785 g_free(key); | |
| 786 return TRUE; | |
| 787 } | |
| 788 | |
| 789 static gboolean | |
| 790 file_is_hidden(const gchar * filename) | |
| 791 { | |
| 792 g_return_val_if_fail(filename != NULL, FALSE); | |
| 2570 | 793 return (g_basename(filename)[0] == '.'); |
| 2313 | 794 } |
| 795 | |
| 796 static GList * | |
| 797 playlist_dir_find_files(const gchar * path, | |
| 798 gboolean background, | |
| 799 GHashTable * htab) | |
| 800 { | |
| 801 GDir *dir; | |
| 802 GList *list = NULL, *ilist; | |
| 803 const gchar *dir_entry; | |
| 804 | |
| 805 struct stat statbuf; | |
| 806 DeviceInode *devino; | |
| 807 | |
| 808 if (!g_file_test(path, G_FILE_TEST_IS_DIR)) | |
| 809 return NULL; | |
| 810 | |
| 811 stat(path, &statbuf); | |
| 812 devino = devino_new(statbuf.st_dev, statbuf.st_ino); | |
| 813 | |
| 814 if (g_hash_table_lookup(htab, devino)) { | |
| 815 g_free(devino); | |
| 816 return NULL; | |
| 817 } | |
| 818 | |
| 819 g_hash_table_insert(htab, devino, GINT_TO_POINTER(1)); | |
| 820 | |
| 821 if ((ilist = input_scan_dir(path))) { | |
| 822 GList *node; | |
| 823 for (node = ilist; node; node = g_list_next(node)) { | |
| 824 gchar *name = g_build_filename(path, node->data, NULL); | |
| 825 list = g_list_prepend(list, name); | |
| 826 g_free(node->data); | |
| 827 } | |
| 828 g_list_free(ilist); | |
| 829 return list; | |
| 830 } | |
| 831 | |
| 832 if (!(dir = g_dir_open(path, 0, NULL))) | |
| 833 return NULL; | |
| 834 | |
| 835 while ((dir_entry = g_dir_read_name(dir))) { | |
| 836 gchar *filename; | |
| 837 | |
| 838 if (file_is_hidden(dir_entry)) | |
| 839 continue; | |
| 840 | |
| 841 filename = g_build_filename(path, dir_entry, NULL); | |
| 842 | |
| 843 if (g_file_test(filename, G_FILE_TEST_IS_DIR)) { | |
| 844 GList *sub; | |
| 845 sub = playlist_dir_find_files(filename, background, htab); | |
| 846 g_free(filename); | |
| 847 list = g_list_concat(list, sub); | |
| 848 } | |
| 849 else if (cfg.playlist_detect == TRUE) | |
| 850 list = g_list_prepend(list, filename); | |
| 851 else if (input_check_file(filename, TRUE)) | |
| 852 list = g_list_prepend(list, filename); | |
| 853 else | |
| 854 g_free(filename); | |
| 855 | |
| 856 while (background && gtk_events_pending()) | |
| 857 gtk_main_iteration(); | |
| 858 } | |
| 859 g_dir_close(dir); | |
| 860 | |
| 861 return list; | |
| 862 } | |
| 863 | |
| 864 gboolean | |
| 865 playlist_add(Playlist * playlist, const gchar * filename) | |
| 866 { | |
| 867 return playlist_ins(playlist, filename, -1); | |
| 868 } | |
| 869 | |
| 870 guint | |
| 871 playlist_add_dir(Playlist * playlist, const gchar * directory) | |
| 872 { | |
| 873 return playlist_ins_dir(playlist, directory, -1, TRUE); | |
| 874 } | |
| 875 | |
| 876 guint | |
| 877 playlist_add_url(Playlist * playlist, const gchar * url) | |
| 878 { | |
| 879 return playlist_ins_url(playlist, url, -1); | |
| 880 } | |
| 881 | |
| 882 guint | |
| 883 playlist_ins_dir(Playlist * playlist, const gchar * path, | |
| 884 gint pos, | |
| 885 gboolean background) | |
| 886 { | |
| 887 guint entries = 0; | |
| 888 GList *list, *node; | |
| 889 GHashTable *htab; | |
| 890 | |
| 891 htab = g_hash_table_new(devino_hash, devino_compare); | |
| 892 | |
| 893 list = playlist_dir_find_files(path, background, htab); | |
| 894 list = g_list_sort(list, (GCompareFunc) path_compare); | |
| 895 | |
| 896 g_hash_table_foreach_remove(htab, devino_destroy, NULL); | |
| 897 | |
| 898 for (node = list; node; node = g_list_next(node)) { | |
| 899 __playlist_ins(playlist, node->data, pos, NULL); | |
| 900 g_free(node->data); | |
| 901 entries++; | |
| 902 if (pos >= 0) | |
| 903 pos++; | |
| 904 } | |
| 905 | |
| 906 g_list_free(list); | |
| 907 | |
| 908 playlist_recalc_total_time(playlist); | |
| 909 playlist_generate_shuffle_list(playlist); | |
| 910 playlistwin_update_list(playlist); | |
| 911 playlist_manager_update(); | |
| 912 return entries; | |
| 913 } | |
| 914 | |
| 915 guint | |
| 916 playlist_ins_url(Playlist * playlist, const gchar * string, | |
| 917 gint pos) | |
| 918 { | |
| 919 gchar *tmp; | |
| 920 gint i = 1, entries = 0; | |
| 921 gboolean first = TRUE; | |
| 922 guint firstpos = 0; | |
| 923 gboolean success = FALSE; | |
| 924 gchar *decoded = NULL; | |
| 925 | |
| 926 g_return_val_if_fail(playlist != NULL, 0); | |
| 927 g_return_val_if_fail(string != NULL, 0); | |
| 928 | |
| 929 playlistwin_update_list(playlist); | |
| 930 | |
| 931 while (*string) { | |
| 932 GList *node; | |
| 933 tmp = strchr(string, '\n'); | |
| 934 if (tmp) { | |
| 935 if (*(tmp - 1) == '\r') | |
| 936 *(tmp - 1) = '\0'; | |
| 937 *tmp = '\0'; | |
| 938 } | |
| 939 | |
| 940 decoded = g_strdup(string); | |
| 941 | |
| 942 if (g_file_test(decoded, G_FILE_TEST_IS_DIR)) { | |
| 943 i = playlist_ins_dir(playlist, decoded, pos, FALSE); | |
| 944 } | |
| 945 else { | |
| 946 if (is_playlist_name(decoded)) { | |
| 947 i = playlist_load_ins(playlist, decoded, pos); | |
| 948 } | |
| 949 else { | |
| 950 success = playlist_ins(playlist, decoded, pos); | |
| 951 i = 1; | |
| 952 } | |
| 953 } | |
| 954 | |
| 955 g_free(decoded); | |
| 956 | |
| 957 PLAYLIST_LOCK(playlist->mutex); | |
| 958 node = g_list_nth(playlist->entries, pos); | |
| 959 PLAYLIST_UNLOCK(playlist->mutex); | |
| 960 | |
| 961 entries += i; | |
| 962 | |
| 963 if (first) { | |
| 964 first = FALSE; | |
| 965 firstpos = pos; | |
| 966 } | |
| 967 | |
| 968 if (pos >= 0) | |
| 969 pos += i; | |
| 970 if (!tmp) | |
| 971 break; | |
| 972 | |
| 973 string = tmp + 1; | |
| 974 } | |
| 975 | |
| 976 playlist_recalc_total_time(playlist); | |
| 977 playlist_generate_shuffle_list(playlist); | |
| 978 playlistwin_update_list(playlist); | |
| 979 | |
| 980 playlist_manager_update(); | |
| 981 | |
| 982 return entries; | |
| 983 } | |
| 984 | |
| 985 void | |
| 986 playlist_set_info_old_abi(const gchar * title, gint length, gint rate, | |
| 987 gint freq, gint nch) | |
| 988 { | |
| 989 Playlist *playlist = playlist_get_active(); | |
| 990 | |
| 991 PLAYLIST_LOCK(playlist->mutex); | |
| 992 | |
| 993 g_return_if_fail(playlist != NULL); | |
| 994 | |
| 995 if (playlist->position) { | |
| 996 g_free(playlist->position->title); | |
| 997 playlist->position->title = g_strdup(title); | |
| 998 playlist->position->length = length; | |
|
2488
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
999 |
|
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
1000 // overwrite tuple->track_name, mainly for streaming. it may incur side effects. --yaz |
|
2489
e56a55d07935
[svn] - revise r3986 to only override tuple->track_name on lengthless streams
nenolod
parents:
2488
diff
changeset
|
1001 if(playlist->position->tuple && length == -1){ |
|
2488
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
1002 if(playlist->position->tuple->track_name){ |
|
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
1003 g_free(playlist->position->tuple->track_name); |
|
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
1004 } |
|
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
1005 playlist->position->tuple->track_name = g_strdup(title); |
|
c5075a79f1aa
[svn] make input->set_info overwrite tuple->track_name. it allows input
yaz
parents:
2480
diff
changeset
|
1006 } |
| 2313 | 1007 } |
| 1008 | |
| 1009 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1010 | |
| 1011 playlist_recalc_total_time(playlist); | |
| 1012 | |
| 1013 mainwin_set_song_info(rate, freq, nch); | |
| 2460 | 1014 |
| 1015 if ( playlist->position ) | |
| 1016 hook_call( "playlist set info" , playlist->position ); | |
| 2313 | 1017 } |
| 1018 | |
| 1019 void | |
| 1020 playlist_set_info(Playlist * playlist, const gchar * title, gint length, gint rate, | |
| 1021 gint freq, gint nch) | |
| 1022 { | |
| 1023 PLAYLIST_LOCK(playlist->mutex); | |
| 1024 | |
| 1025 g_return_if_fail(playlist != NULL); | |
| 1026 | |
| 1027 if (playlist->position) { | |
| 1028 g_free(playlist->position->title); | |
| 1029 playlist->position->title = g_strdup(title); | |
| 1030 playlist->position->length = length; | |
| 1031 } | |
| 1032 | |
| 1033 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1034 | |
| 1035 playlist_recalc_total_time(playlist); | |
| 1036 | |
| 1037 mainwin_set_song_info(rate, freq, nch); | |
| 2460 | 1038 |
| 1039 if ( playlist->position ) | |
| 1040 hook_call( "playlist set info" , playlist->position ); | |
| 2313 | 1041 } |
| 1042 | |
| 1043 void | |
| 1044 playlist_check_pos_current(Playlist *playlist) | |
| 1045 { | |
| 1046 gint pos, row, bottom; | |
| 1047 | |
| 1048 if (!playlist) | |
| 1049 return; | |
| 1050 | |
| 1051 PLAYLIST_LOCK(playlist->mutex); | |
| 1052 if (!playlist->position || !playlistwin_list) { | |
| 1053 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1054 return; | |
| 1055 } | |
| 1056 | |
| 1057 pos = g_list_index(playlist->entries, playlist->position); | |
| 1058 | |
| 1059 if (playlistwin_item_visible(pos)) { | |
| 1060 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1061 return; | |
| 1062 } | |
| 1063 | |
| 1064 bottom = MAX(0, playlist_get_length_nolock(playlist) - | |
| 1065 playlistwin_list->pl_num_visible); | |
| 1066 row = CLAMP(pos - playlistwin_list->pl_num_visible / 2, 0, bottom); | |
| 1067 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1068 playlistwin_set_toprow(row); | |
| 1069 g_cond_signal(cond_scan); | |
| 1070 } | |
| 1071 | |
| 1072 void | |
| 1073 playlist_next(Playlist *playlist) | |
| 1074 { | |
| 1075 GList *plist_pos_list; | |
| 1076 gboolean restart_playing = FALSE; | |
|
2480
bb2f191895ce
[svn] - fixed crash when skipping to next song with an empty playlist
marvin
parents:
2460
diff
changeset
|
1077 if (!playlist_get_length(playlist)) |
| 2313 | 1078 return; |
| 1079 | |
| 1080 PLAYLIST_LOCK(playlist->mutex); | |
| 1081 | |
| 1082 if ((playlist_position_before_jump != NULL) && playlist->queue == NULL) | |
| 1083 { | |
| 1084 playlist->position = playlist_position_before_jump; | |
| 1085 playlist_position_before_jump = NULL; | |
| 1086 } | |
| 1087 | |
| 1088 plist_pos_list = find_playlist_position_list(playlist); | |
| 1089 | |
| 1090 if (!cfg.repeat && !g_list_next(plist_pos_list) && playlist->queue == NULL) { | |
| 1091 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1092 return; | |
| 1093 } | |
| 1094 | |
| 1095 if (playback_get_playing()) { | |
| 1096 /* We need to stop before changing playlist_position */ | |
| 1097 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1098 ip_data.stop = TRUE; | |
| 1099 playback_stop(); | |
| 1100 ip_data.stop = FALSE; | |
| 1101 PLAYLIST_LOCK(playlist->mutex); | |
| 1102 restart_playing = TRUE; | |
| 1103 } | |
| 1104 | |
| 1105 plist_pos_list = find_playlist_position_list(playlist); | |
| 1106 if (playlist->queue != NULL) | |
| 1107 play_queued(playlist); | |
| 1108 else if (g_list_next(plist_pos_list)) | |
| 1109 playlist->position = g_list_next(plist_pos_list)->data; | |
| 1110 else if (cfg.repeat) { | |
| 1111 playlist->position = NULL; | |
| 1112 playlist_generate_shuffle_list_nolock(playlist); | |
| 1113 if (cfg.shuffle) | |
| 1114 playlist->position = playlist->shuffle->data; | |
| 1115 else | |
| 1116 playlist->position = playlist->entries->data; | |
| 1117 } | |
| 1118 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1119 playlist_check_pos_current(playlist); | |
| 1120 | |
| 1121 if (restart_playing) | |
| 1122 playback_initiate(); | |
| 1123 else { | |
| 1124 mainwin_set_info_text(); | |
| 1125 playlistwin_update_list(playlist); | |
| 1126 } | |
| 1127 } | |
| 1128 | |
| 1129 void | |
| 1130 playlist_prev(Playlist *playlist) | |
| 1131 { | |
| 1132 GList *plist_pos_list; | |
| 1133 gboolean restart_playing = FALSE; | |
| 1134 | |
|
2480
bb2f191895ce
[svn] - fixed crash when skipping to next song with an empty playlist
marvin
parents:
2460
diff
changeset
|
1135 if (!playlist_get_length(playlist)) |
| 2313 | 1136 return; |
| 1137 | |
| 1138 PLAYLIST_LOCK(playlist->mutex); | |
| 1139 | |
| 1140 if ((playlist_position_before_jump != NULL) && playlist->queue == NULL) | |
| 1141 { | |
| 1142 playlist->position = playlist_position_before_jump; | |
| 1143 playlist_position_before_jump = NULL; | |
| 1144 } | |
| 1145 | |
| 1146 plist_pos_list = find_playlist_position_list(playlist); | |
| 1147 | |
| 1148 if (!cfg.repeat && !g_list_previous(plist_pos_list)) { | |
| 1149 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1150 return; | |
| 1151 } | |
| 1152 | |
| 1153 if (playback_get_playing()) { | |
| 1154 /* We need to stop before changing playlist_position */ | |
| 1155 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1156 ip_data.stop = TRUE; | |
| 1157 playback_stop(); | |
| 1158 ip_data.stop = FALSE; | |
| 1159 PLAYLIST_LOCK(playlist->mutex); | |
| 1160 restart_playing = TRUE; | |
| 1161 } | |
| 1162 | |
| 1163 plist_pos_list = find_playlist_position_list(playlist); | |
| 1164 if (g_list_previous(plist_pos_list)) { | |
| 1165 playlist->position = g_list_previous(plist_pos_list)->data; | |
| 1166 } | |
| 1167 else if (cfg.repeat) { | |
| 1168 GList *node; | |
| 1169 playlist->position = NULL; | |
| 1170 playlist_generate_shuffle_list_nolock(playlist); | |
| 1171 if (cfg.shuffle) | |
| 1172 node = g_list_last(playlist->shuffle); | |
| 1173 else | |
| 1174 node = g_list_last(playlist->entries); | |
| 1175 if (node) | |
| 1176 playlist->position = node->data; | |
| 1177 } | |
| 1178 | |
| 1179 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1180 | |
| 1181 playlist_check_pos_current(playlist); | |
| 1182 | |
| 1183 if (restart_playing) | |
| 1184 playback_initiate(); | |
| 1185 else { | |
| 1186 mainwin_set_info_text(); | |
| 1187 playlistwin_update_list(playlist); | |
| 1188 } | |
| 1189 } | |
| 1190 | |
| 1191 void | |
| 1192 playlist_queue(Playlist *playlist) | |
| 1193 { | |
| 1194 GList *list = playlist_get_selected(playlist); | |
| 1195 GList *it = list; | |
| 1196 | |
| 1197 PLAYLIST_LOCK(playlist->mutex); | |
| 1198 | |
| 1199 if ((cfg.shuffle) && (playlist_position_before_jump == NULL)) | |
| 1200 { | |
| 1201 /* Shuffling and this is our first manual jump. */ | |
| 1202 playlist_position_before_jump = playlist->position; | |
| 1203 } | |
| 1204 | |
| 1205 while (it) { | |
| 1206 GList *next = g_list_next(it); | |
| 1207 GList *tmp; | |
| 1208 | |
| 1209 /* XXX: WTF? --nenolod */ | |
| 1210 it->data = g_list_nth_data(playlist->entries, GPOINTER_TO_INT(it->data)); | |
| 1211 if ((tmp = g_list_find(playlist->queue, it->data))) { | |
| 1212 playlist->queue = g_list_remove_link(playlist->queue, tmp); | |
| 1213 g_list_free_1(tmp); | |
| 1214 list = g_list_remove_link(list, it); | |
| 1215 g_list_free_1(it); | |
| 1216 } | |
| 1217 | |
| 1218 it = next; | |
| 1219 } | |
| 1220 | |
| 1221 playlist->queue = g_list_concat(playlist->queue, list); | |
| 1222 | |
| 1223 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1224 | |
| 1225 playlist_recalc_total_time(playlist); | |
| 1226 playlistwin_update_list(playlist); | |
| 1227 } | |
| 1228 | |
| 1229 void | |
| 1230 playlist_queue_position(Playlist *playlist, guint pos) | |
| 1231 { | |
| 1232 GList *tmp; | |
| 1233 PlaylistEntry *entry; | |
| 1234 | |
| 1235 PLAYLIST_LOCK(playlist->mutex); | |
| 1236 | |
| 1237 if ((cfg.shuffle) && (playlist_position_before_jump == NULL)) | |
| 1238 { | |
| 1239 /* Shuffling and this is our first manual jump. */ | |
| 1240 playlist_position_before_jump = playlist->position; | |
| 1241 } | |
| 1242 | |
| 1243 entry = g_list_nth_data(playlist->entries, pos); | |
| 1244 if ((tmp = g_list_find(playlist->queue, entry))) { | |
| 1245 playlist->queue = g_list_remove_link(playlist->queue, tmp); | |
| 1246 g_list_free_1(tmp); | |
| 1247 } | |
| 1248 else | |
| 1249 playlist->queue = g_list_append(playlist->queue, entry); | |
| 1250 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1251 | |
| 1252 playlist_recalc_total_time(playlist); | |
| 1253 playlistwin_update_list(playlist); | |
| 1254 } | |
| 1255 | |
| 1256 gboolean | |
| 1257 playlist_is_position_queued(Playlist *playlist, guint pos) | |
| 1258 { | |
| 1259 PlaylistEntry *entry; | |
| 1260 GList *tmp; | |
| 1261 | |
| 1262 PLAYLIST_LOCK(playlist->mutex); | |
| 1263 entry = g_list_nth_data(playlist->entries, pos); | |
| 1264 tmp = g_list_find(playlist->queue, entry); | |
| 1265 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1266 | |
| 1267 return tmp != NULL; | |
| 1268 } | |
| 1269 | |
| 1270 gint | |
| 1271 playlist_get_queue_position_number(Playlist *playlist, guint pos) | |
| 1272 { | |
| 1273 PlaylistEntry *entry; | |
| 1274 gint tmp; | |
| 1275 | |
| 1276 PLAYLIST_LOCK(playlist->mutex); | |
| 1277 entry = g_list_nth_data(playlist->entries, pos); | |
| 1278 tmp = g_list_index(playlist->queue, entry); | |
| 1279 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1280 | |
| 1281 return tmp; | |
| 1282 } | |
| 1283 | |
| 1284 gint | |
| 1285 playlist_get_queue_qposition_number(Playlist *playlist, guint pos) | |
| 1286 { | |
| 1287 PlaylistEntry *entry; | |
| 1288 gint tmp; | |
| 1289 | |
| 1290 PLAYLIST_LOCK(playlist->mutex); | |
| 1291 entry = g_list_nth_data(playlist->queue, pos); | |
| 1292 tmp = g_list_index(playlist->entries, entry); | |
| 1293 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1294 | |
| 1295 return tmp; | |
| 1296 } | |
| 1297 | |
| 1298 void | |
| 1299 playlist_clear_queue(Playlist *playlist) | |
| 1300 { | |
| 1301 PLAYLIST_LOCK(playlist->mutex); | |
| 1302 g_list_free(playlist->queue); | |
| 1303 playlist->queue = NULL; | |
| 1304 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1305 | |
| 1306 playlist_recalc_total_time(playlist); | |
| 1307 playlistwin_update_list(playlist); | |
| 1308 } | |
| 1309 | |
| 1310 void | |
| 1311 playlist_queue_remove(Playlist *playlist, guint pos) | |
| 1312 { | |
| 1313 void *entry; | |
| 1314 | |
| 1315 PLAYLIST_LOCK(playlist->mutex); | |
| 1316 entry = g_list_nth_data(playlist->entries, pos); | |
| 1317 playlist->queue = g_list_remove(playlist->queue, entry); | |
| 1318 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1319 | |
| 1320 playlistwin_update_list(playlist); | |
| 1321 } | |
| 1322 | |
| 1323 gint | |
| 1324 playlist_get_queue_position(Playlist *playlist, PlaylistEntry * entry) | |
| 1325 { | |
| 1326 return g_list_index(playlist->queue, entry); | |
| 1327 } | |
| 1328 | |
| 1329 void | |
| 1330 playlist_set_position(Playlist *playlist, guint pos) | |
| 1331 { | |
| 1332 GList *node; | |
| 1333 gboolean restart_playing = FALSE; | |
| 1334 | |
| 1335 if (!playlist) | |
| 1336 return; | |
| 1337 | |
| 1338 PLAYLIST_LOCK(playlist->mutex); | |
| 1339 | |
| 1340 node = g_list_nth(playlist->entries, pos); | |
| 1341 if (!node) { | |
| 1342 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1343 return; | |
| 1344 } | |
| 1345 | |
| 1346 if (playback_get_playing()) { | |
| 1347 /* We need to stop before changing playlist_position */ | |
| 1348 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1349 ip_data.stop = TRUE; | |
| 1350 playback_stop(); | |
| 1351 ip_data.stop = FALSE; | |
| 1352 PLAYLIST_LOCK(playlist->mutex); | |
| 1353 restart_playing = TRUE; | |
| 1354 } | |
| 1355 | |
| 1356 if ((cfg.shuffle) && (playlist_position_before_jump == NULL)) | |
| 1357 { | |
| 1358 /* Shuffling and this is our first manual jump. */ | |
| 1359 playlist_position_before_jump = playlist->position; | |
| 1360 } | |
| 1361 | |
| 1362 playlist->position = node->data; | |
| 1363 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1364 playlist_check_pos_current(playlist); | |
| 1365 | |
| 1366 if (restart_playing) | |
| 1367 playback_initiate(); | |
| 1368 else { | |
| 1369 mainwin_set_info_text(); | |
| 1370 playlistwin_update_list(playlist); | |
| 1371 } | |
| 1372 } | |
| 1373 | |
| 1374 void | |
| 1375 playlist_eof_reached(Playlist *playlist) | |
| 1376 { | |
| 1377 GList *plist_pos_list; | |
| 1378 | |
| 1379 if ((cfg.no_playlist_advance && !cfg.repeat) || cfg.stopaftersong) | |
| 1380 ip_data.stop = TRUE; | |
| 1381 playback_stop(); | |
| 1382 if ((cfg.no_playlist_advance && !cfg.repeat) || cfg.stopaftersong) | |
| 1383 ip_data.stop = FALSE; | |
| 1384 | |
|
2408
b380e84148bb
[svn] - rename some hooks so that they are more logical
nenolod
parents:
2407
diff
changeset
|
1385 hook_call("playback end", playlist->position); |
|
2407
1dc1d36d0347
[svn] - add hooks: playback begin, playback end, playlist reached end
nenolod
parents:
2380
diff
changeset
|
1386 |
| 2313 | 1387 PLAYLIST_LOCK(playlist->mutex); |
| 1388 | |
| 1389 if ((playlist_position_before_jump != NULL) && playlist->queue == NULL) | |
| 1390 { | |
| 1391 playlist->position = playlist_position_before_jump; | |
| 1392 playlist_position_before_jump = NULL; | |
| 1393 } | |
| 1394 | |
| 1395 plist_pos_list = find_playlist_position_list(playlist); | |
| 1396 | |
| 1397 if (cfg.no_playlist_advance) { | |
| 1398 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1399 mainwin_clear_song_info(); | |
| 1400 if (cfg.repeat) | |
| 1401 playback_initiate(); | |
| 1402 return; | |
| 1403 } | |
| 1404 | |
| 1405 if (cfg.stopaftersong) { | |
| 1406 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1407 mainwin_clear_song_info(); | |
| 1408 mainwin_set_stopaftersong(FALSE); | |
| 1409 return; | |
| 1410 } | |
| 1411 | |
| 1412 if (playlist->queue != NULL) { | |
| 1413 play_queued(playlist); | |
| 1414 } | |
| 1415 else if (!g_list_next(plist_pos_list)) { | |
| 1416 if (cfg.shuffle) { | |
| 1417 playlist->position = NULL; | |
| 1418 playlist_generate_shuffle_list_nolock(playlist); | |
| 1419 } | |
| 2380 | 1420 else if (playlist->entries != NULL) |
| 2313 | 1421 playlist->position = playlist->entries->data; |
| 1422 | |
| 1423 if (!cfg.repeat) { | |
| 1424 PLAYLIST_UNLOCK(playlist->mutex); | |
|
2408
b380e84148bb
[svn] - rename some hooks so that they are more logical
nenolod
parents:
2407
diff
changeset
|
1425 hook_call("playlist end reached", playlist->position); |
| 2313 | 1426 mainwin_clear_song_info(); |
| 1427 mainwin_set_info_text(); | |
| 1428 return; | |
| 1429 } | |
| 1430 } | |
| 1431 else | |
| 1432 playlist->position = g_list_next(plist_pos_list)->data; | |
| 1433 | |
| 1434 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1435 | |
| 1436 playlist_check_pos_current(playlist); | |
| 1437 playback_initiate(); | |
| 1438 mainwin_set_info_text(); | |
| 1439 playlistwin_update_list(playlist); | |
| 1440 } | |
| 1441 | |
| 1442 gint | |
| 1443 playlist_get_length(Playlist *playlist) | |
| 1444 { | |
| 1445 gint retval; | |
| 1446 | |
| 1447 PLAYLIST_LOCK(playlist->mutex); | |
| 1448 retval = playlist_get_length_nolock(playlist); | |
| 1449 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1450 | |
| 1451 return retval; | |
| 1452 } | |
| 1453 | |
| 1454 gint | |
| 1455 playlist_queue_get_length(Playlist *playlist) | |
| 1456 { | |
| 1457 gint length; | |
| 1458 | |
| 1459 PLAYLIST_LOCK(playlist->mutex); | |
| 1460 length = g_list_length(playlist->queue); | |
| 1461 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1462 | |
| 1463 return length; | |
| 1464 } | |
| 1465 | |
| 1466 gint | |
| 1467 playlist_get_length_nolock(Playlist *playlist) | |
| 1468 { | |
| 1469 return g_list_length(playlist->entries); | |
| 1470 } | |
| 1471 | |
| 1472 gchar * | |
| 1473 playlist_get_info_text(Playlist *playlist) | |
| 1474 { | |
| 1475 gchar *text, *title, *numbers, *length; | |
| 1476 | |
| 1477 g_return_val_if_fail(playlist != NULL, NULL); | |
| 1478 | |
| 1479 PLAYLIST_LOCK(playlist->mutex); | |
| 1480 if (!playlist->position) { | |
| 1481 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1482 return NULL; | |
| 1483 } | |
| 1484 | |
| 1485 /* FIXME: there should not be a need to do additional conversion, | |
| 1486 * if playlist is properly maintained */ | |
| 1487 if (playlist->position->title) { | |
| 1488 title = str_to_utf8(playlist->position->title); | |
| 1489 } | |
| 1490 else { | |
| 1491 gchar *basename = g_path_get_basename(playlist->position->filename); | |
| 1492 title = filename_to_utf8(basename); | |
| 1493 g_free(basename); | |
| 1494 } | |
| 1495 | |
| 1496 /* | |
| 1497 * If the user don't want numbers in the playlist, don't | |
| 1498 * display them in other parts of XMMS | |
| 1499 */ | |
| 1500 | |
| 1501 if (cfg.show_numbers_in_pl) | |
| 1502 numbers = g_strdup_printf("%d. ", playlist_get_position_nolock(playlist) + 1); | |
| 1503 else | |
| 1504 numbers = g_strdup(""); | |
| 1505 | |
| 1506 if (playlist->position->length != -1) | |
| 1507 length = g_strdup_printf(" (%d:%-2.2d)", | |
| 1508 playlist->position->length / 60000, | |
| 1509 (playlist->position->length / 1000) % 60); | |
| 1510 else | |
| 1511 length = g_strdup(""); | |
| 1512 | |
| 1513 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1514 | |
| 1515 text = convert_title_text(g_strconcat(numbers, title, length, NULL)); | |
| 1516 | |
| 1517 g_free(numbers); | |
| 1518 g_free(title); | |
| 1519 g_free(length); | |
| 1520 | |
| 1521 return text; | |
| 1522 } | |
| 1523 | |
| 1524 gint | |
| 1525 playlist_get_current_length(Playlist * playlist) | |
| 1526 { | |
| 1527 gint len = 0; | |
| 1528 | |
| 1529 if (!playlist) | |
| 1530 return 0; | |
| 1531 | |
| 1532 PLAYLIST_LOCK(playlist->mutex); | |
| 1533 if (playlist->position) | |
| 1534 len = playlist->position->length; | |
| 1535 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1536 | |
| 1537 return len; | |
| 1538 } | |
| 1539 | |
| 1540 gboolean | |
| 1541 playlist_save(Playlist * playlist, const gchar * filename) | |
| 1542 { | |
| 1543 PlaylistContainer *plc = NULL; | |
| 1544 gchar *ext; | |
| 1545 | |
| 1546 g_return_val_if_fail(playlist != NULL, FALSE); | |
| 1547 g_return_val_if_fail(filename != NULL, FALSE); | |
| 1548 | |
| 1549 ext = strrchr(filename, '.') + 1; | |
| 1550 | |
| 1551 playlist_set_current_name(playlist, filename); | |
| 1552 | |
| 1553 if ((plc = playlist_container_find(ext)) == NULL) | |
| 1554 return FALSE; | |
| 1555 | |
| 1556 if (plc->plc_write == NULL) | |
| 1557 return FALSE; | |
| 1558 | |
| 1559 plc->plc_write(filename, 0); | |
| 1560 | |
| 1561 return TRUE; | |
| 1562 } | |
| 1563 | |
| 1564 gboolean | |
| 1565 playlist_load(Playlist * playlist, const gchar * filename) | |
| 1566 { | |
| 1567 gboolean ret = FALSE; | |
| 1568 g_return_val_if_fail(playlist != NULL, FALSE); | |
| 1569 | |
| 1570 playlist->loading_playlist = TRUE; | |
| 1571 ret = playlist_load_ins(playlist, filename, -1); | |
| 1572 playlist->loading_playlist = FALSE; | |
| 1573 | |
| 1574 return ret; | |
| 1575 } | |
| 1576 | |
| 1577 void | |
| 1578 playlist_load_ins_file(Playlist *playlist, | |
| 1579 const gchar * filename_p, | |
| 1580 const gchar * playlist_name, gint pos, | |
| 1581 const gchar * title, gint len) | |
| 1582 { | |
| 1583 gchar *filename; | |
| 1584 gchar *tmp, *path; | |
| 1585 InputPlugin *dec; /* for decoder cache */ | |
| 1586 | |
| 1587 g_return_if_fail(filename_p != NULL); | |
| 1588 g_return_if_fail(playlist != NULL); | |
| 1589 g_return_if_fail(playlist_name != NULL); | |
| 1590 | |
| 1591 filename = g_strchug(g_strdup(filename_p)); | |
| 1592 | |
| 1593 if(cfg.convert_slash) | |
| 1594 while ((tmp = strchr(filename, '\\')) != NULL) | |
| 1595 *tmp = '/'; | |
| 1596 | |
| 1597 if (filename[0] != '/' && !strstr(filename, "://")) { | |
| 1598 path = g_strdup(playlist_name); | |
| 1599 if ((tmp = strrchr(path, '/'))) | |
| 1600 *tmp = '\0'; | |
| 1601 else { | |
| 1602 if (playlist->loading_playlist != TRUE || cfg.playlist_detect == FALSE) | |
| 1603 dec = input_check_file(filename, FALSE); | |
| 1604 else | |
| 1605 dec = NULL; | |
| 1606 | |
| 1607 __playlist_ins_with_info(playlist, filename, pos, title, len, dec); | |
| 1608 return; | |
| 1609 } | |
| 1610 tmp = g_build_filename(path, filename, NULL); | |
| 1611 | |
| 1612 if (playlist->loading_playlist != TRUE && cfg.playlist_detect != TRUE) | |
| 1613 dec = input_check_file(tmp, FALSE); | |
| 1614 else | |
| 1615 dec = NULL; | |
| 1616 | |
| 1617 __playlist_ins_with_info(playlist, tmp, pos, title, len, dec); | |
| 1618 g_free(tmp); | |
| 1619 g_free(path); | |
| 1620 } | |
| 1621 else | |
| 1622 { | |
| 1623 if (playlist->loading_playlist != TRUE && cfg.playlist_detect != TRUE) | |
| 1624 dec = input_check_file(filename, FALSE); | |
| 1625 else | |
| 1626 dec = NULL; | |
| 1627 | |
| 1628 __playlist_ins_with_info(playlist, filename, pos, title, len, dec); | |
| 1629 } | |
| 1630 | |
| 1631 g_free(filename); | |
| 1632 } | |
| 1633 | |
| 1634 void | |
| 1635 playlist_load_ins_file_tuple(Playlist * playlist, | |
| 1636 const gchar * filename_p, | |
| 1637 const gchar * playlist_name, | |
| 1638 gint pos, | |
| 1639 TitleInput *tuple) | |
| 1640 { | |
| 1641 gchar *filename; | |
| 1642 gchar *tmp, *path; | |
| 1643 InputPlugin *dec; /* for decoder cache */ | |
| 1644 | |
| 1645 g_return_if_fail(filename_p != NULL); | |
| 1646 g_return_if_fail(playlist_name != NULL); | |
| 1647 g_return_if_fail(playlist != NULL); | |
| 1648 | |
| 1649 filename = g_strchug(g_strdup(filename_p)); | |
| 1650 | |
| 1651 while ((tmp = strchr(filename, '\\')) != NULL) | |
| 1652 *tmp = '/'; | |
| 1653 | |
| 1654 if (filename[0] != '/' && !strstr(filename, "://")) { | |
| 1655 path = g_strdup(playlist_name); | |
| 1656 if ((tmp = strrchr(path, '/'))) | |
| 1657 *tmp = '\0'; | |
| 1658 else { | |
| 1659 if (playlist->loading_playlist != TRUE || cfg.playlist_detect == FALSE) | |
| 1660 dec = input_check_file(filename, FALSE); | |
| 1661 else | |
| 1662 dec = NULL; | |
| 1663 | |
| 1664 __playlist_ins_with_info_tuple(playlist, filename, pos, tuple, dec); | |
| 1665 return; | |
| 1666 } | |
| 1667 tmp = g_build_filename(path, filename, NULL); | |
| 1668 | |
| 1669 if (playlist->loading_playlist != TRUE && cfg.playlist_detect != TRUE) | |
| 1670 dec = input_check_file(tmp, FALSE); | |
| 1671 else | |
| 1672 dec = NULL; | |
| 1673 | |
| 1674 __playlist_ins_with_info_tuple(playlist, tmp, pos, tuple, dec); | |
| 1675 g_free(tmp); | |
| 1676 g_free(path); | |
| 1677 } | |
| 1678 else | |
| 1679 { | |
| 1680 if (playlist->loading_playlist != TRUE && cfg.playlist_detect != TRUE) | |
| 1681 dec = input_check_file(filename, FALSE); | |
| 1682 else | |
| 1683 dec = NULL; | |
| 1684 | |
| 1685 __playlist_ins_with_info_tuple(playlist, filename, pos, tuple, dec); | |
| 1686 } | |
| 1687 | |
| 1688 g_free(filename); | |
| 1689 } | |
| 1690 | |
| 1691 static guint | |
| 1692 playlist_load_ins(Playlist * playlist, const gchar * filename, gint pos) | |
| 1693 { | |
| 1694 PlaylistContainer *plc; | |
| 1695 gchar *ext; | |
| 1696 | |
| 1697 g_return_val_if_fail(playlist != NULL, 0); | |
| 1698 g_return_val_if_fail(filename != NULL, 0); | |
| 1699 | |
| 1700 ext = strrchr(filename, '.') + 1; | |
| 1701 plc = playlist_container_find(ext); | |
| 1702 | |
| 1703 g_return_val_if_fail(plc != NULL, 0); | |
| 1704 g_return_val_if_fail(plc->plc_read != NULL, 0); | |
| 1705 | |
| 1706 plc->plc_read(filename, pos); | |
| 1707 | |
| 1708 playlist_generate_shuffle_list(playlist); | |
| 1709 playlistwin_update_list(playlist); | |
| 1710 | |
| 1711 return 1; | |
| 1712 } | |
| 1713 | |
| 1714 GList * | |
| 1715 get_playlist_nth(Playlist *playlist, guint nth) | |
| 1716 { | |
| 1717 g_warning("deprecated function get_playlist_nth() was called"); | |
| 1718 REQUIRE_LOCK(playlist->mutex); | |
| 1719 return g_list_nth(playlist->entries, nth); | |
| 1720 } | |
| 1721 | |
| 1722 gint | |
| 1723 playlist_get_position_nolock(Playlist *playlist) | |
| 1724 { | |
| 1725 if (playlist && playlist->position) | |
| 1726 return g_list_index(playlist->entries, playlist->position); | |
| 1727 return 0; | |
| 1728 } | |
| 1729 | |
| 1730 gint | |
| 1731 playlist_get_position(Playlist *playlist) | |
| 1732 { | |
| 1733 gint pos; | |
| 1734 | |
| 1735 PLAYLIST_LOCK(playlist->mutex); | |
| 1736 pos = playlist_get_position_nolock(playlist); | |
| 1737 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1738 | |
| 1739 return pos; | |
| 1740 } | |
| 1741 | |
| 1742 gchar * | |
| 1743 playlist_get_filename(Playlist *playlist, guint pos) | |
| 1744 { | |
| 1745 gchar *filename; | |
| 1746 PlaylistEntry *entry; | |
| 1747 GList *node; | |
| 1748 | |
| 1749 if (!playlist) | |
| 1750 return NULL; | |
| 1751 | |
| 1752 PLAYLIST_LOCK(playlist->mutex); | |
| 1753 node = g_list_nth(playlist->entries, pos); | |
| 1754 if (!node) { | |
| 1755 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1756 return NULL; | |
| 1757 } | |
| 1758 entry = node->data; | |
| 1759 | |
| 1760 filename = g_strdup(entry->filename); | |
| 1761 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1762 | |
| 1763 return filename; | |
| 1764 } | |
| 1765 | |
| 1766 gchar * | |
| 1767 playlist_get_songtitle(Playlist *playlist, guint pos) | |
| 1768 { | |
| 1769 gchar *title = NULL; | |
| 1770 PlaylistEntry *entry; | |
| 1771 GList *node; | |
| 1772 | |
| 1773 if (!playlist) | |
| 1774 return NULL; | |
| 1775 | |
| 1776 PLAYLIST_LOCK(playlist->mutex); | |
| 1777 | |
| 1778 if (!(node = g_list_nth(playlist->entries, pos))) { | |
| 1779 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1780 return NULL; | |
| 1781 } | |
| 1782 | |
| 1783 entry = node->data; | |
| 1784 | |
| 1785 /* FIXME: simplify this logic */ | |
| 1786 if ((entry->title == NULL && entry->length == -1) || | |
| 1787 (entry->tuple && entry->tuple->mtime != 0 && (entry->tuple->mtime == -1 || entry->tuple->mtime != playlist_get_mtime(entry->filename)))) | |
| 1788 { | |
| 1789 if (playlist_entry_get_info(entry)) | |
| 1790 title = entry->title; | |
| 1791 } | |
| 1792 else { | |
| 1793 title = entry->title; | |
| 1794 } | |
| 1795 | |
| 1796 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1797 | |
| 1798 if (!title) { | |
| 1799 title = g_path_get_basename(entry->filename); | |
| 1800 return str_replace(title, filename_to_utf8(title)); | |
| 1801 } | |
| 1802 | |
| 1803 return str_to_utf8(title); | |
| 1804 } | |
| 1805 | |
| 1806 TitleInput * | |
| 1807 playlist_get_tuple(Playlist *playlist, guint pos) | |
| 1808 { | |
| 1809 PlaylistEntry *entry; | |
| 1810 TitleInput *tuple = NULL; | |
| 1811 GList *node; | |
| 1812 | |
| 1813 if (!playlist) | |
| 1814 return NULL; | |
| 1815 | |
| 1816 PLAYLIST_LOCK(playlist->mutex); | |
| 1817 | |
| 1818 if (!(node = g_list_nth(playlist->entries, pos))) { | |
| 1819 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1820 return NULL; | |
| 1821 } | |
| 1822 | |
| 1823 entry = (PlaylistEntry *) node->data; | |
| 1824 | |
| 1825 tuple = entry->tuple; | |
| 1826 | |
| 1827 // if no tuple or tuple with old mtime, get new one. | |
| 1828 if (tuple == NULL || | |
| 1829 (entry->tuple && entry->tuple->mtime != 0 && (entry->tuple->mtime == -1 || entry->tuple->mtime != playlist_get_mtime(entry->filename)))) | |
| 1830 { | |
| 1831 playlist_entry_get_info(entry); | |
| 1832 tuple = entry->tuple; | |
| 1833 } | |
| 1834 | |
| 1835 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1836 | |
| 1837 return tuple; | |
| 1838 } | |
| 1839 | |
| 1840 gint | |
| 1841 playlist_get_songtime(Playlist *playlist, guint pos) | |
| 1842 { | |
| 1843 gint song_time = -1; | |
| 1844 PlaylistEntry *entry; | |
| 1845 GList *node; | |
| 1846 | |
| 1847 if (!playlist) | |
| 1848 return -1; | |
| 1849 | |
| 1850 PLAYLIST_LOCK(playlist->mutex); | |
| 1851 | |
| 1852 if (!(node = g_list_nth(playlist->entries, pos))) { | |
| 1853 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1854 return -1; | |
| 1855 } | |
| 1856 | |
| 1857 entry = node->data; | |
| 1858 if (entry->tuple == NULL || | |
| 1859 (entry->tuple->mtime != 0 && (entry->tuple->mtime == -1 || entry->tuple->mtime != playlist_get_mtime(entry->filename)))) { | |
| 1860 | |
| 1861 if (playlist_entry_get_info(entry)) | |
| 1862 song_time = entry->length; | |
| 1863 | |
| 1864 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1865 } | |
| 1866 else { | |
| 1867 song_time = entry->length; | |
| 1868 PLAYLIST_UNLOCK(playlist->mutex); | |
| 1869 } | |
| 1870 | |
| 1871 return song_time; | |
| 1872 } | |
| 1873 | |
| 1874 static gint | |
| 1875 playlist_compare_track(PlaylistEntry * a, | |
| 1876 PlaylistEntry * b) | |
| 1877 { | |
| 1878 g_return_val_if_fail(a != NULL, 0); | |
| 1879 g_return_val_if_fail(b != NULL, 0); | |
| 1880 | |
| 1881 g_return_val_if_fail(a->tuple != NULL, 0); | |
| 1882 g_return_val_if_fail(b->tuple != NULL, 0); | |
| 1883 | |
| 1884 return (a->tuple->track_number - b->tuple->track_number); | |
| 1885 } | |
| 1886 | |
| 1887 static gint | |
| 1888 playlist_compare_playlist(PlaylistEntry * a, | |
| 1889 PlaylistEntry * b) | |
| 1890 { | |
| 1891 const gchar *a_title = NULL, *b_title = NULL; | |
| 1892 | |
| 1893 g_return_val_if_fail(a != NULL, 0); | |
| 1894 g_return_val_if_fail(b != NULL, 0); | |
| 1895 | |
| 1896 if (a->title != NULL) | |
| 1897 a_title = a->title; | |
| 1898 else { | |
| 1899 if (strrchr(a->filename, '/')) | |
| 1900 a_title = strrchr(a->filename, '/') + 1; | |
| 1901 else | |
| 1902 a_title = a->filename; | |
| 1903 } | |
| 1904 | |
| 1905 if (b->title != NULL) | |
| 1906 b_title = b->title; | |
| 1907 else { | |
| 1908 if (strrchr(a->filename, '/')) | |
| 1909 b_title = strrchr(b->filename, '/') + 1; | |
| 1910 else | |
| 1911 b_title = b->filename; | |
| 1912 } | |
| 1913 | |
| 1914 return strcasecmp(a_title, b_title); | |
| 1915 } | |
| 1916 | |
| 1917 static gint | |
| 1918 playlist_compare_title(PlaylistEntry * a, | |
| 1919 PlaylistEntry * b) | |
| 1920 { | |
| 1921 const gchar *a_title = NULL, *b_title = NULL; | |
| 1922 | |
| 1923 g_return_val_if_fail(a != NULL, 0); | |
| 1924 g_return_val_if_fail(b != NULL, 0); | |
| 1925 | |
| 1926 if (a->tuple != NULL && a->tuple->track_name != NULL) | |
| 1927 a_title = a->tuple->track_name; | |
| 1928 if (b->tuple != NULL && b->tuple->track_name != NULL) | |
| 1929 b_title = b->tuple->track_name; | |
| 1930 | |
| 1931 if (a_title != NULL && b_title != NULL) | |
| 1932 return strcasecmp(a_title, b_title); | |
| 1933 | |
| 1934 if (a->title != NULL) | |
| 1935 a_title = a->title; | |
| 1936 else { | |
| 1937 if (strrchr(a->filename, '/')) | |
| 1938 a_title = strrchr(a->filename, '/') + 1; | |
| 1939 else | |
| 1940 a_title = a->filename; | |
| 1941 } | |
| 1942 | |
| 1943 if (b->title != NULL) | |
| 1944 b_title = b->title; | |
| 1945 else { | |
| 1946 if (strrchr(a->filename, '/')) | |
| 1947 b_title = strrchr(b->filename, '/') + 1; | |
| 1948 else | |
| 1949 b_title = b->filename; | |
| 1950 } | |
| 1951 | |
| 1952 return strcasecmp(a_title, b_title); | |
| 1953 } | |
| 1954 | |
| 1955 static gint | |
| 1956 playlist_compare_artist(PlaylistEntry * a, | |
| 1957 PlaylistEntry * b) | |
| 1958 { | |
| 1959 const gchar *a_artist = NULL, *b_artist = NULL; | |
| 1960 | |
| 1961 g_return_val_if_fail(a != NULL, 0); | |
| 1962 g_return_val_if_fail(b != NULL, 0); | |
| 1963 | |
| 1964 if (a->tuple != NULL) | |
| 1965 playlist_entry_get_info(a); | |
| 1966 | |
| 1967 if (b->tuple != NULL) | |
| 1968 playlist_entry_get_info(b); | |
| 1969 | |
| 1970 if (a->tuple != NULL && a->tuple->performer != NULL) | |
| 1971 a_artist = a->tuple->performer; | |
| 1972 if (b->tuple != NULL && b->tuple->performer != NULL) | |
| 1973 b_artist = b->tuple->performer; | |
| 1974 | |
| 1975 if (a_artist != NULL && b_artist != NULL) | |
| 1976 return strcasecmp(a_artist, b_artist); | |
| 1977 | |
| 1978 return 0; | |
| 1979 } | |
| 1980 | |
| 1981 static gint | |
| 1982 playlist_compare_filename(PlaylistEntry * a, | |
| 1983 PlaylistEntry * b) | |
| 1984 { | |
| 1985 gchar *a_filename, *b_filename; | |
| 1986 | |
| 1987 g_return_val_if_fail(a != NULL, 0); | |
| 1988 g_return_val_if_fail(b != NULL, 0); | |
| 1989 | |
| 1990 if (strrchr(a->filename, '/')) | |
| 1991 a_filename = strrchr(a->filename, '/') + 1; | |
| 1992 else | |
| 1993 a_filename = a->filename; | |
| 1994 | |
| 1995 if (strrchr(b->filename, '/')) | |
| 1996 b_filename = strrchr(b->filename, '/') + 1; | |
| 1997 else | |
| 1998 b_filename = b->filename; | |
| 1999 | |
| 2000 | |
| 2001 return strcasecmp(a_filename, b_filename); | |
| 2002 } | |
| 2003 | |
| 2004 static gint | |
| 2005 path_compare(const gchar * a, const gchar * b) | |
| 2006 { | |
| 2007 gchar *posa, *posb; | |
| 2008 gint len, ret; | |
| 2009 | |
| 2010 posa = strrchr(a, '/'); | |
| 2011 posb = strrchr(b, '/'); | |
| 2012 | |
| 2013 /* | |
| 2014 * Sort directories before files | |
| 2015 */ | |
| 2016 if (posa && posb && (posa - a != posb - b)) { | |
| 2017 if (posa - a > posb - b) { | |
| 2018 len = posb - b; | |
| 2019 ret = -1; | |
| 2020 } | |
| 2021 else { | |
| 2022 len = posa - a; | |
| 2023 ret = 1; | |
| 2024 } | |
| 2025 if (!strncasecmp(a, b, len)) | |
| 2026 return ret; | |
| 2027 } | |
| 2028 return strcasecmp(a, b); | |
| 2029 } | |
| 2030 | |
| 2031 static gint | |
| 2032 playlist_compare_path(PlaylistEntry * a, | |
| 2033 PlaylistEntry * b) | |
| 2034 { | |
| 2035 return path_compare(a->filename, b->filename); | |
| 2036 } | |
| 2037 | |
| 2038 | |
| 2039 static time_t | |
| 2040 playlist_get_mtime(const gchar *filename) | |
| 2041 { | |
| 2042 struct stat buf; | |
| 2043 gint rv; | |
| 2044 | |
| 2045 rv = stat(filename, &buf); | |
| 2046 | |
| 2047 if (rv == 0) { | |
| 2048 return buf.st_mtime; | |
| 2049 } else { | |
| 2050 return 0; //error | |
| 2051 } | |
| 2052 } | |
| 2053 | |
| 2054 | |
| 2055 static gint | |
| 2056 playlist_compare_date(PlaylistEntry * a, | |
| 2057 PlaylistEntry * b) | |
| 2058 { | |
| 2059 struct stat buf; | |
| 2060 time_t modtime; | |
| 2061 | |
| 2062 gint rv; | |
| 2063 | |
| 2064 | |
| 2065 rv = stat(a->filename, &buf); | |
| 2066 | |
| 2067 if (rv == 0) { | |
| 2068 modtime = buf.st_mtime; | |
| 2069 rv = stat(b->filename, &buf); | |
| 2070 | |
| 2071 if (stat(b->filename, &buf) == 0) { | |
| 2072 if (buf.st_mtime == modtime) | |
| 2073 return 0; | |
| 2074 else | |
| 2075 return (buf.st_mtime - modtime) > 0 ? -1 : 1; | |
| 2076 } | |
| 2077 else | |
| 2078 return -1; | |
| 2079 } | |
| 2080 else if (!lstat(b->filename, &buf)) | |
| 2081 return 1; | |
| 2082 else | |
| 2083 return playlist_compare_filename(a, b); | |
| 2084 } | |
| 2085 | |
| 2086 | |
| 2087 void | |
| 2088 playlist_sort(Playlist *playlist, PlaylistSortType type) | |
| 2089 { | |
| 2090 playlist_remove_dead_files(playlist); | |
| 2091 PLAYLIST_LOCK(playlist->mutex); | |
| 2092 playlist->entries = | |
| 2093 g_list_sort(playlist->entries, | |
| 2094 (GCompareFunc) playlist_compare_func_table[type]); | |
| 2095 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2096 } | |
| 2097 | |
| 2098 static GList * | |
| 2099 playlist_sort_selected_generic(GList * list, GCompareFunc cmpfunc) | |
| 2100 { | |
| 2101 GList *list1, *list2; | |
| 2102 GList *tmp_list = NULL; | |
| 2103 GList *index_list = NULL; | |
| 2104 | |
| 2105 /* | |
| 2106 * We take all the selected entries out of the playlist, | |
| 2107 * sorts them, and then put them back in again. | |
| 2108 */ | |
| 2109 | |
| 2110 list1 = g_list_last(list); | |
| 2111 | |
| 2112 while (list1) { | |
| 2113 list2 = g_list_previous(list1); | |
| 2114 if (PLAYLIST_ENTRY(list1->data)->selected) { | |
| 2115 gpointer idx; | |
| 2116 idx = GINT_TO_POINTER(g_list_position(list, list1)); | |
| 2117 index_list = g_list_prepend(index_list, idx); | |
| 2118 list = g_list_remove_link(list, list1); | |
| 2119 tmp_list = g_list_concat(list1, tmp_list); | |
| 2120 } | |
| 2121 list1 = list2; | |
| 2122 } | |
| 2123 | |
| 2124 tmp_list = g_list_sort(tmp_list, cmpfunc); | |
| 2125 list1 = tmp_list; | |
| 2126 list2 = index_list; | |
| 2127 | |
| 2128 while (list2) { | |
| 2129 if (!list1) { | |
| 2130 g_critical(G_STRLOC ": Error during list sorting. " | |
| 2131 "Possibly dropped some playlist-entries."); | |
| 2132 break; | |
| 2133 } | |
| 2134 | |
| 2135 list = g_list_insert(list, list1->data, GPOINTER_TO_INT(list2->data)); | |
| 2136 | |
| 2137 list2 = g_list_next(list2); | |
| 2138 list1 = g_list_next(list1); | |
| 2139 } | |
| 2140 | |
| 2141 g_list_free(index_list); | |
| 2142 g_list_free(tmp_list); | |
| 2143 | |
| 2144 return list; | |
| 2145 } | |
| 2146 | |
| 2147 void | |
| 2148 playlist_sort_selected(Playlist *playlist, PlaylistSortType type) | |
| 2149 { | |
| 2150 PLAYLIST_LOCK(playlist->mutex); | |
| 2151 playlist->entries = playlist_sort_selected_generic(playlist->entries, (GCompareFunc) | |
| 2152 playlist_compare_func_table | |
| 2153 [type]); | |
| 2154 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2155 } | |
| 2156 | |
| 2157 void | |
| 2158 playlist_reverse(Playlist *playlist) | |
| 2159 { | |
| 2160 PLAYLIST_LOCK(playlist->mutex); | |
| 2161 playlist->entries = g_list_reverse(playlist->entries); | |
| 2162 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2163 } | |
| 2164 | |
| 2165 static GList * | |
| 2166 playlist_shuffle_list(Playlist *playlist, GList * list) | |
| 2167 { | |
| 2168 /* | |
| 2169 * Note that this doesn't make a copy of the original list. | |
| 2170 * The pointer to the original list is not valid after this | |
| 2171 * fuction is run. | |
| 2172 */ | |
| 2173 gint len = g_list_length(list); | |
| 2174 gint i, j; | |
| 2175 GList *node, **ptrs; | |
| 2176 | |
| 2177 if (!playlist) | |
| 2178 return NULL; | |
| 2179 | |
| 2180 REQUIRE_LOCK(playlist->mutex); | |
| 2181 | |
| 2182 if (!len) | |
| 2183 return NULL; | |
| 2184 | |
| 2185 ptrs = g_new(GList *, len); | |
| 2186 | |
| 2187 for (node = list, i = 0; i < len; node = g_list_next(node), i++) | |
| 2188 ptrs[i] = node; | |
| 2189 | |
| 2190 j = g_random_int_range(0, len); | |
| 2191 list = ptrs[j]; | |
| 2192 ptrs[j]->next = NULL; | |
| 2193 ptrs[j] = ptrs[0]; | |
| 2194 | |
| 2195 for (i = 1; i < len; i++) { | |
| 2196 j = g_random_int_range(0, len - i); | |
| 2197 list->prev = ptrs[i + j]; | |
| 2198 ptrs[i + j]->next = list; | |
| 2199 list = ptrs[i + j]; | |
| 2200 ptrs[i + j] = ptrs[i]; | |
| 2201 } | |
| 2202 list->prev = NULL; | |
| 2203 | |
| 2204 g_free(ptrs); | |
| 2205 | |
| 2206 return list; | |
| 2207 } | |
| 2208 | |
| 2209 void | |
| 2210 playlist_random(Playlist *playlist) | |
| 2211 { | |
| 2212 PLAYLIST_LOCK(playlist->mutex); | |
| 2213 playlist->entries = playlist_shuffle_list(playlist, playlist->entries); | |
| 2214 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2215 } | |
| 2216 | |
| 2217 GList * | |
| 2218 playlist_get_selected(Playlist *playlist) | |
| 2219 { | |
| 2220 GList *node, *list = NULL; | |
| 2221 gint i = 0; | |
| 2222 | |
| 2223 PLAYLIST_LOCK(playlist->mutex); | |
| 2224 for (node = playlist->entries; node; node = g_list_next(node), i++) { | |
| 2225 PlaylistEntry *entry = node->data; | |
| 2226 if (entry->selected) | |
| 2227 list = g_list_prepend(list, GINT_TO_POINTER(i)); | |
| 2228 } | |
| 2229 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2230 return g_list_reverse(list); | |
| 2231 } | |
| 2232 | |
| 2233 void | |
| 2234 playlist_clear_selected(Playlist *playlist) | |
| 2235 { | |
| 2236 GList *node = NULL; | |
| 2237 gint i = 0; | |
| 2238 | |
| 2239 PLAYLIST_LOCK(playlist->mutex); | |
| 2240 for (node = playlist->entries; node; node = g_list_next(node), i++) { | |
| 2241 PLAYLIST_ENTRY(node->data)->selected = FALSE; | |
| 2242 } | |
| 2243 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2244 playlist_recalc_total_time(playlist); | |
| 2245 playlist_manager_update(); | |
| 2246 } | |
| 2247 | |
| 2248 gint | |
| 2249 playlist_get_num_selected(Playlist *playlist) | |
| 2250 { | |
| 2251 GList *node; | |
| 2252 gint num = 0; | |
| 2253 | |
| 2254 PLAYLIST_LOCK(playlist->mutex); | |
| 2255 for (node = playlist->entries; node; node = g_list_next(node)) { | |
| 2256 PlaylistEntry *entry = node->data; | |
| 2257 if (entry->selected) | |
| 2258 num++; | |
| 2259 } | |
| 2260 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2261 return num; | |
| 2262 } | |
| 2263 | |
| 2264 | |
| 2265 static void | |
| 2266 playlist_generate_shuffle_list(Playlist *playlist) | |
| 2267 { | |
| 2268 PLAYLIST_LOCK(playlist->mutex); | |
| 2269 playlist_generate_shuffle_list_nolock(playlist); | |
| 2270 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2271 } | |
| 2272 | |
| 2273 static void | |
| 2274 playlist_generate_shuffle_list_nolock(Playlist *playlist) | |
| 2275 { | |
| 2276 GList *node; | |
| 2277 gint numsongs; | |
| 2278 | |
| 2279 if (!cfg.shuffle || !playlist) | |
| 2280 return; | |
| 2281 | |
| 2282 REQUIRE_LOCK(playlist->mutex); | |
| 2283 | |
| 2284 if (playlist->shuffle) { | |
| 2285 g_list_free(playlist->shuffle); | |
| 2286 playlist->shuffle = NULL; | |
| 2287 } | |
| 2288 | |
| 2289 playlist->shuffle = playlist_shuffle_list(playlist, g_list_copy(playlist->entries)); | |
| 2290 numsongs = g_list_length(playlist->shuffle); | |
| 2291 | |
| 2292 if (playlist->position) { | |
| 2293 gint i = g_list_index(playlist->shuffle, playlist->position); | |
| 2294 node = g_list_nth(playlist->shuffle, i); | |
| 2295 playlist->shuffle = g_list_remove_link(playlist->shuffle, node); | |
| 2296 playlist->shuffle = g_list_prepend(playlist->shuffle, node->data); | |
| 2297 } | |
| 2298 } | |
| 2299 | |
| 2300 void | |
| 2301 playlist_fileinfo(Playlist *playlist, guint pos) | |
| 2302 { | |
| 2303 gchar *path = NULL; | |
| 2304 GList *node; | |
| 2305 PlaylistEntry *entry = NULL; | |
| 2306 TitleInput *tuple = NULL; | |
| 2307 | |
| 2308 PLAYLIST_LOCK(playlist->mutex); | |
| 2309 | |
| 2310 if ((node = g_list_nth(playlist->entries, pos))) | |
| 2311 { | |
| 2312 entry = node->data; | |
| 2313 tuple = entry->tuple; | |
| 2314 path = g_strdup(entry->filename); | |
| 2315 } | |
| 2316 | |
| 2317 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2318 | |
| 2319 /* No tuple? Try to set this entry up properly. --nenolod */ | |
| 2320 if (entry->tuple == NULL || entry->tuple->mtime == -1 || | |
| 2321 entry->tuple->mtime == 0 || entry->tuple->mtime != playlist_get_mtime(entry->filename)) | |
| 2322 { | |
| 2323 playlist_entry_get_info(entry); | |
| 2324 tuple = entry->tuple; | |
| 2325 } | |
| 2326 | |
| 2327 if (tuple != NULL) | |
| 2328 { | |
| 2329 if (entry->decoder == NULL) | |
| 2330 entry->decoder = input_check_file(entry->filename, FALSE); /* try to find a decoder */ | |
| 2331 | |
| 2332 if (entry->decoder != NULL && entry->decoder->file_info_box == NULL) | |
| 2333 fileinfo_show_for_tuple(tuple); | |
| 2334 else if (entry->decoder != NULL && entry->decoder->file_info_box != NULL) | |
| 2335 entry->decoder->file_info_box(path); | |
| 2336 else | |
| 2337 fileinfo_show_for_path(path); | |
| 2338 g_free(path); | |
| 2339 } | |
| 2340 else if (path != NULL) | |
| 2341 { | |
| 2342 if (entry != NULL && entry->decoder != NULL && entry->decoder->file_info_box != NULL) | |
| 2343 entry->decoder->file_info_box(path); | |
| 2344 else | |
| 2345 fileinfo_show_for_path(path); | |
| 2346 g_free(path); | |
| 2347 } | |
| 2348 } | |
| 2349 | |
| 2350 void | |
| 2351 playlist_fileinfo_current(Playlist *playlist) | |
| 2352 { | |
| 2353 gchar *path = NULL; | |
| 2354 TitleInput *tuple = NULL; | |
| 2355 | |
| 2356 PLAYLIST_LOCK(playlist->mutex); | |
| 2357 | |
| 2358 if (playlist->entries && playlist->position) | |
| 2359 { | |
| 2360 path = g_strdup(playlist->position->filename); | |
| 2361 if (( playlist->position->tuple == NULL ) || ( playlist->position->decoder == NULL )) | |
| 2362 playlist_entry_get_info(playlist->position); | |
| 2363 tuple = playlist->position->tuple; | |
| 2364 } | |
| 2365 | |
| 2366 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2367 | |
| 2368 if (tuple != NULL) | |
| 2369 { | |
| 2370 if (playlist->position->decoder != NULL && playlist->position->decoder->file_info_box == NULL) | |
| 2371 fileinfo_show_for_tuple(tuple); | |
| 2372 else if (playlist->position->decoder != NULL && playlist->position->decoder->file_info_box != NULL) | |
| 2373 playlist->position->decoder->file_info_box(path); | |
| 2374 else | |
| 2375 fileinfo_show_for_path(path); | |
| 2376 g_free(path); | |
| 2377 } | |
| 2378 else if (path != NULL) | |
| 2379 { | |
| 2380 if (playlist->position != NULL && playlist->position->decoder != NULL && playlist->position->decoder->file_info_box != NULL) | |
| 2381 playlist->position->decoder->file_info_box(path); | |
| 2382 else | |
| 2383 fileinfo_show_for_path(path); | |
| 2384 g_free(path); | |
| 2385 } | |
| 2386 } | |
| 2387 | |
| 2388 | |
| 2389 static gboolean | |
| 2390 playlist_get_info_is_going(void) | |
| 2391 { | |
| 2392 gboolean result; | |
| 2393 | |
| 2394 G_LOCK(playlist_get_info_going); | |
| 2395 result = playlist_get_info_going; | |
| 2396 G_UNLOCK(playlist_get_info_going); | |
| 2397 | |
| 2398 return result; | |
| 2399 } | |
| 2400 | |
| 2401 static gpointer | |
| 2402 playlist_get_info_func(gpointer arg) | |
| 2403 { | |
| 2404 GList *node; | |
| 2405 gboolean update_playlistwin = FALSE; | |
| 2406 gboolean update_mainwin = FALSE; | |
| 2407 | |
| 2408 while (playlist_get_info_is_going()) { | |
| 2409 PlaylistEntry *entry; | |
| 2410 Playlist *playlist = playlist_get_active(); | |
| 2411 | |
| 2412 // on_load | |
| 2413 if (cfg.use_pl_metadata && | |
| 2414 cfg.get_info_on_load && | |
| 2415 playlist_get_info_scan_active) { | |
| 2416 | |
| 2417 PLAYLIST_LOCK(playlist->mutex); | |
| 2418 for (node = playlist->entries; node; node = g_list_next(node)) { | |
| 2419 entry = node->data; | |
| 2420 | |
| 2421 if(entry->tuple && (entry->tuple->length > -1)) { | |
| 2422 update_playlistwin = TRUE; | |
| 2423 continue; | |
| 2424 } | |
| 2425 | |
| 2426 if (!playlist_entry_get_info(entry)) { | |
| 2427 if (g_list_index(playlist->entries, entry) == -1) | |
| 2428 /* Entry disappeared while we looked it up. | |
| 2429 Restart. */ | |
| 2430 node = playlist->entries; | |
| 2431 } | |
| 2432 else if ((entry->tuple != NULL || entry->title != NULL) && entry->length != -1) { | |
| 2433 update_playlistwin = TRUE; | |
| 2434 if (entry == playlist->position) | |
| 2435 update_mainwin = TRUE; | |
| 2436 break; | |
| 2437 } | |
| 2438 } | |
| 2439 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2440 | |
| 2441 if (!node) { | |
| 2442 g_mutex_lock(mutex_scan); | |
| 2443 playlist_get_info_scan_active = FALSE; | |
| 2444 g_mutex_unlock(mutex_scan); | |
| 2445 } | |
| 2446 } // on_load | |
| 2447 | |
| 2448 // on_demand | |
| 2449 else if (!cfg.get_info_on_load && | |
| 2450 cfg.get_info_on_demand && | |
| 2451 cfg.playlist_visible && | |
| 2452 !cfg.playlist_shaded && | |
| 2453 cfg.use_pl_metadata) { | |
| 2454 | |
| 2455 g_mutex_lock(mutex_scan); | |
| 2456 playlist_get_info_scan_active = FALSE; | |
| 2457 g_mutex_unlock(mutex_scan); | |
| 2458 | |
| 2459 PLAYLIST_LOCK(playlist->mutex); | |
| 2460 | |
| 2461 if (!playlist->entries) { | |
| 2462 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2463 } | |
| 2464 else { | |
| 2465 for (node = g_list_nth(playlist->entries, playlistwin_get_toprow()); | |
| 2466 node && playlistwin_item_visible(g_list_position(playlist->entries, node)); | |
| 2467 node = g_list_next(node)) { | |
| 2468 | |
| 2469 entry = node->data; | |
| 2470 | |
| 2471 if(entry->tuple && (entry->tuple->length > -1)) { | |
| 2472 update_playlistwin = TRUE; | |
| 2473 continue; | |
| 2474 } | |
| 2475 | |
| 2476 if (!playlist_entry_get_info(entry)) { | |
| 2477 if (g_list_index(playlist->entries, entry) == -1) | |
| 2478 /* Entry disapeared while we | |
| 2479 looked it up. Restart. */ | |
| 2480 node = | |
| 2481 g_list_nth(playlist->entries, | |
| 2482 playlistwin_get_toprow()); | |
| 2483 } | |
| 2484 else if ((entry->tuple != NULL || entry->title != NULL) && entry->length != -1) { | |
| 2485 update_playlistwin = TRUE; | |
| 2486 if (entry == playlist->position) | |
| 2487 update_mainwin = TRUE; | |
|
2545
610d85b8a22b
[svn] - on loading a playlist with tuple, invoke the scanner thread only if mtime is -1.
yaz
parents:
2504
diff
changeset
|
2488 // no need for break here since this iteration is very short. |
| 2313 | 2489 } |
| 2490 } | |
| 2491 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2492 } | |
| 2493 } // on_demand | |
| 2494 else if (cfg.get_info_on_demand && | |
| 2495 (!cfg.playlist_visible || cfg.playlist_shaded | |
| 2496 || !cfg.use_pl_metadata)) | |
| 2497 { | |
| 2498 g_mutex_lock(mutex_scan); | |
| 2499 playlist_get_info_scan_active = FALSE; | |
| 2500 g_mutex_unlock(mutex_scan); | |
| 2501 } | |
| 2502 else /* not on_demand and not on_load... | |
| 2503 NOTE: this shouldn't happen anymore, sanity check in bmp_config_load now */ | |
| 2504 { | |
| 2505 g_mutex_lock(mutex_scan); | |
| 2506 playlist_get_info_scan_active = FALSE; | |
| 2507 g_mutex_unlock(mutex_scan); | |
| 2508 } | |
| 2509 | |
| 2510 if (update_playlistwin) { | |
| 2511 playlistwin_update_list(playlist); | |
| 2512 update_playlistwin = FALSE; | |
| 2513 } | |
| 2514 | |
| 2515 if (update_mainwin) { | |
| 2516 mainwin_set_info_text(); | |
| 2517 update_mainwin = FALSE; | |
| 2518 } | |
| 2519 | |
| 2520 if (playlist_get_info_scan_active) { | |
| 2521 continue; | |
| 2522 } | |
| 2523 | |
| 2524 g_mutex_lock(mutex_scan); | |
| 2525 g_cond_wait(cond_scan, mutex_scan); | |
| 2526 g_mutex_unlock(mutex_scan); | |
| 2527 | |
| 2528 } // while | |
| 2529 | |
| 2530 g_thread_exit(NULL); | |
| 2531 return NULL; | |
| 2532 } | |
| 2533 | |
| 2534 void | |
| 2535 playlist_start_get_info_thread(void) | |
| 2536 { | |
| 2537 G_LOCK(playlist_get_info_going); | |
| 2538 playlist_get_info_going = TRUE; | |
| 2539 G_UNLOCK(playlist_get_info_going); | |
| 2540 | |
| 2541 playlist_get_info_thread = g_thread_create(playlist_get_info_func, | |
| 2542 NULL, TRUE, NULL); | |
| 2543 } | |
| 2544 | |
| 2545 void | |
| 2546 playlist_stop_get_info_thread(void) | |
| 2547 { | |
| 2548 G_LOCK(playlist_get_info_going); | |
| 2549 playlist_get_info_going = FALSE; | |
| 2550 G_UNLOCK(playlist_get_info_going); | |
| 2551 | |
| 2552 g_cond_broadcast(cond_scan); | |
| 2553 g_thread_join(playlist_get_info_thread); | |
| 2554 } | |
| 2555 | |
| 2556 void | |
| 2557 playlist_start_get_info_scan(void) | |
| 2558 { | |
| 2559 g_mutex_lock(mutex_scan); | |
| 2560 playlist_get_info_scan_active = TRUE; | |
| 2561 g_mutex_unlock(mutex_scan); | |
| 2562 g_cond_signal(cond_scan); | |
| 2563 } | |
| 2564 | |
| 2565 void | |
| 2566 playlist_remove_dead_files(Playlist *playlist) | |
| 2567 { | |
| 2568 GList *node, *next_node; | |
| 2569 | |
| 2570 PLAYLIST_LOCK(playlist->mutex); | |
| 2571 | |
| 2572 for (node = playlist->entries; node; node = next_node) { | |
| 2573 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data); | |
| 2574 next_node = g_list_next(node); | |
| 2575 | |
| 2576 if (!entry || !entry->filename) { | |
| 2577 g_message(G_STRLOC ": Playlist entry is invalid!"); | |
| 2578 continue; | |
| 2579 } | |
| 2580 | |
| 2581 /* FIXME: What about 'file:///'? */ | |
| 2582 /* Don't kill URLs */ | |
| 2583 if (strstr(entry->filename, "://")) | |
| 2584 continue; | |
| 2585 | |
| 2586 /* FIXME: Should test for readability */ | |
| 2587 if (vfs_file_test(entry->filename, G_FILE_TEST_EXISTS)) | |
| 2588 continue; | |
| 2589 | |
| 2590 if (entry == playlist->position) { | |
| 2591 /* Don't remove the currently playing song */ | |
| 2592 if (playback_get_playing()) | |
| 2593 continue; | |
| 2594 | |
| 2595 if (next_node) | |
| 2596 playlist->position = PLAYLIST_ENTRY(next_node->data); | |
| 2597 else | |
| 2598 playlist->position = NULL; | |
| 2599 } | |
| 2600 | |
| 2601 playlist_entry_free(entry); | |
| 2602 playlist->entries = g_list_delete_link(playlist->entries, node); | |
| 2603 } | |
| 2604 | |
| 2605 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2606 | |
| 2607 playlist_generate_shuffle_list(playlist); | |
| 2608 playlistwin_update_list(playlist); | |
| 2609 playlist_recalc_total_time(playlist); | |
| 2610 playlist_manager_update(); | |
| 2611 } | |
| 2612 | |
| 2613 | |
| 2614 static gint | |
| 2615 playlist_dupscmp_title(PlaylistEntry * a, | |
| 2616 PlaylistEntry * b) | |
| 2617 { | |
| 2618 const gchar *a_title, *b_title; | |
| 2619 | |
| 2620 g_return_val_if_fail(a != NULL, 0); | |
| 2621 g_return_val_if_fail(b != NULL, 0); | |
| 2622 | |
| 2623 if (a->title) | |
| 2624 a_title = a->title; | |
| 2625 else { | |
| 2626 if (strrchr(a->filename, '/')) | |
| 2627 a_title = strrchr(a->filename, '/') + 1; | |
| 2628 else | |
| 2629 a_title = a->filename; | |
| 2630 } | |
| 2631 | |
| 2632 if (b->title) | |
| 2633 b_title = b->title; | |
| 2634 else { | |
| 2635 if (strrchr(a->filename, '/')) | |
| 2636 b_title = strrchr(b->filename, '/') + 1; | |
| 2637 else | |
| 2638 b_title = b->filename; | |
| 2639 } | |
| 2640 | |
| 2641 return strcmp(a_title, b_title); | |
| 2642 } | |
| 2643 | |
| 2644 static gint | |
| 2645 playlist_dupscmp_filename(PlaylistEntry * a, | |
| 2646 PlaylistEntry * b ) | |
| 2647 { | |
| 2648 gchar *a_filename, *b_filename; | |
| 2649 | |
| 2650 g_return_val_if_fail(a != NULL, 0); | |
| 2651 g_return_val_if_fail(b != NULL, 0); | |
| 2652 | |
| 2653 if (strrchr(a->filename, '/')) | |
| 2654 a_filename = strrchr(a->filename, '/') + 1; | |
| 2655 else | |
| 2656 a_filename = a->filename; | |
| 2657 | |
| 2658 if (strrchr(b->filename, '/')) | |
| 2659 b_filename = strrchr(b->filename, '/') + 1; | |
| 2660 else | |
| 2661 b_filename = b->filename; | |
| 2662 | |
| 2663 return strcmp(a_filename, b_filename); | |
| 2664 } | |
| 2665 | |
| 2666 static gint | |
| 2667 playlist_dupscmp_path(PlaylistEntry * a, | |
| 2668 PlaylistEntry * b) | |
| 2669 { | |
| 2670 /* simply compare the entire filename string */ | |
| 2671 return strcmp(a->filename, b->filename); | |
| 2672 } | |
| 2673 | |
| 2674 void | |
| 2675 playlist_remove_duplicates(Playlist *playlist, PlaylistDupsType type) | |
| 2676 { | |
| 2677 GList *node, *next_node; | |
| 2678 GList *node_cmp, *next_node_cmp; | |
| 2679 gint (*dups_compare_func)(PlaylistEntry * , PlaylistEntry *); | |
| 2680 | |
| 2681 switch ( type ) | |
| 2682 { | |
| 2683 case PLAYLIST_DUPS_TITLE: | |
| 2684 dups_compare_func = playlist_dupscmp_title; | |
| 2685 break; | |
| 2686 case PLAYLIST_DUPS_PATH: | |
| 2687 dups_compare_func = playlist_dupscmp_path; | |
| 2688 break; | |
| 2689 case PLAYLIST_DUPS_FILENAME: | |
| 2690 default: | |
| 2691 dups_compare_func = playlist_dupscmp_filename; | |
| 2692 break; | |
| 2693 } | |
| 2694 | |
| 2695 PLAYLIST_LOCK(playlist->mutex); | |
| 2696 | |
| 2697 for (node = playlist->entries; node; node = next_node) { | |
| 2698 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data); | |
| 2699 next_node = g_list_next(node); | |
| 2700 | |
| 2701 if (!entry || !entry->filename) { | |
| 2702 g_message(G_STRLOC ": Playlist entry is invalid!"); | |
| 2703 continue; | |
| 2704 } | |
| 2705 | |
| 2706 for (node_cmp = next_node; node_cmp; node_cmp = next_node_cmp) { | |
| 2707 PlaylistEntry *entry_cmp = PLAYLIST_ENTRY(node_cmp->data); | |
| 2708 next_node_cmp = g_list_next(node_cmp); | |
| 2709 | |
| 2710 if (!entry_cmp || !entry_cmp->filename) { | |
| 2711 g_message(G_STRLOC ": Playlist entry is invalid!"); | |
| 2712 continue; | |
| 2713 } | |
| 2714 | |
| 2715 /* compare using the chosen dups_compare_func */ | |
| 2716 if ( !dups_compare_func( entry , entry_cmp ) ) { | |
| 2717 | |
| 2718 if (entry_cmp == playlist->position) { | |
| 2719 /* Don't remove the currently playing song */ | |
| 2720 if (playback_get_playing()) | |
| 2721 continue; | |
| 2722 | |
| 2723 if (next_node_cmp) | |
| 2724 playlist->position = PLAYLIST_ENTRY(next_node_cmp->data); | |
| 2725 else | |
| 2726 playlist->position = NULL; | |
| 2727 } | |
| 2728 | |
| 2729 /* check if this was the next item of the external | |
| 2730 loop; if true, replace it with the next of the next*/ | |
| 2731 if ( node_cmp == next_node ) | |
| 2732 next_node = g_list_next(next_node); | |
| 2733 | |
| 2734 playlist_entry_free(entry_cmp); | |
| 2735 playlist->entries = g_list_delete_link(playlist->entries, node_cmp); | |
| 2736 } | |
| 2737 } | |
| 2738 } | |
| 2739 | |
| 2740 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2741 | |
| 2742 playlistwin_update_list(playlist); | |
| 2743 playlist_recalc_total_time(playlist); | |
| 2744 | |
| 2745 playlist_manager_update(); | |
| 2746 } | |
| 2747 | |
| 2748 void | |
| 2749 playlist_get_total_time(Playlist * playlist, | |
| 2750 gulong * total_time, | |
| 2751 gulong * selection_time, | |
| 2752 gboolean * total_more, | |
| 2753 gboolean * selection_more) | |
| 2754 { | |
| 2755 PLAYLIST_LOCK(playlist->mutex); | |
| 2756 *total_time = playlist->pl_total_time; | |
| 2757 *selection_time = playlist->pl_selection_time; | |
| 2758 *total_more = playlist->pl_total_more; | |
| 2759 *selection_more = playlist->pl_selection_more; | |
| 2760 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2761 } | |
| 2762 | |
| 2763 | |
| 2764 static void | |
| 2765 playlist_recalc_total_time_nolock(Playlist *playlist) | |
| 2766 { | |
| 2767 GList *list; | |
| 2768 PlaylistEntry *entry; | |
| 2769 | |
| 2770 REQUIRE_LOCK(playlist->mutex); | |
| 2771 | |
| 2772 playlist->pl_total_time = 0; | |
| 2773 playlist->pl_selection_time = 0; | |
| 2774 playlist->pl_total_more = FALSE; | |
| 2775 playlist->pl_selection_more = FALSE; | |
| 2776 | |
| 2777 for (list = playlist->entries; list; list = g_list_next(list)) { | |
| 2778 entry = list->data; | |
| 2779 | |
| 2780 if (entry->length != -1) | |
| 2781 playlist->pl_total_time += entry->length / 1000; | |
| 2782 else | |
| 2783 playlist->pl_total_more = TRUE; | |
| 2784 | |
| 2785 if (entry->selected) { | |
| 2786 if (entry->length != -1) | |
| 2787 playlist->pl_selection_time += entry->length / 1000; | |
| 2788 else | |
| 2789 playlist->pl_selection_more = TRUE; | |
| 2790 } | |
| 2791 } | |
| 2792 } | |
| 2793 | |
| 2794 static void | |
| 2795 playlist_recalc_total_time(Playlist *playlist) | |
| 2796 { | |
| 2797 PLAYLIST_LOCK(playlist->mutex); | |
| 2798 playlist_recalc_total_time_nolock(playlist); | |
| 2799 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2800 } | |
| 2801 | |
| 2802 gint | |
| 2803 playlist_select_search( Playlist *playlist , TitleInput *tuple , gint action ) | |
| 2804 { | |
| 2805 GList *entry_list = NULL, *found_list = NULL, *sel_list = NULL; | |
| 2806 gboolean is_first_search = TRUE; | |
| 2807 gint num_of_entries_found = 0; | |
| 2808 | |
| 2809 #if defined(USE_REGEX_ONIGURUMA) | |
| 2810 /* set encoding for Oniguruma regex to UTF-8 */ | |
| 2811 reg_set_encoding( REG_POSIX_ENCODING_UTF8 ); | |
| 2812 onig_set_default_syntax( ONIG_SYNTAX_POSIX_BASIC ); | |
| 2813 #endif | |
| 2814 | |
| 2815 PLAYLIST_LOCK(playlist->mutex); | |
| 2816 | |
| 2817 if ( tuple->track_name != NULL ) | |
| 2818 { | |
| 2819 /* match by track_name */ | |
| 2820 const gchar *regex_pattern = tuple->track_name; | |
| 2821 regex_t regex; | |
| 2822 #if defined(USE_REGEX_PCRE) | |
| 2823 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 ) | |
| 2824 #else | |
| 2825 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 ) | |
| 2826 #endif | |
| 2827 { | |
| 2828 GList *tfound_list = NULL; | |
| 2829 if ( is_first_search == TRUE ) entry_list = playlist->entries; | |
| 2830 else entry_list = found_list; /* use found_list */ | |
| 2831 for ( ; entry_list ; entry_list = g_list_next(entry_list) ) | |
| 2832 { | |
| 2833 PlaylistEntry *entry = entry_list->data; | |
| 2834 if ( ( entry->tuple != NULL ) && ( entry->tuple->track_name != NULL ) && | |
| 2835 ( regexec( ®ex , entry->tuple->track_name , 0 , NULL , 0 ) == 0 ) ) | |
| 2836 { | |
| 2837 tfound_list = g_list_append( tfound_list , entry ); | |
| 2838 } | |
| 2839 } | |
| 2840 g_list_free( found_list ); /* wipe old found_list */ | |
| 2841 found_list = tfound_list; /* move tfound_list in found_list */ | |
| 2842 regfree( ®ex ); | |
| 2843 } | |
| 2844 is_first_search = FALSE; | |
| 2845 } | |
| 2846 | |
| 2847 if ( tuple->album_name != NULL ) | |
| 2848 { | |
| 2849 /* match by album_name */ | |
| 2850 const gchar *regex_pattern = tuple->album_name; | |
| 2851 regex_t regex; | |
| 2852 #if defined(USE_REGEX_PCRE) | |
| 2853 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 ) | |
| 2854 #else | |
| 2855 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 ) | |
| 2856 #endif | |
| 2857 { | |
| 2858 GList *tfound_list = NULL; | |
| 2859 if ( is_first_search == TRUE ) entry_list = playlist->entries; | |
| 2860 else entry_list = found_list; /* use found_list */ | |
| 2861 for ( ; entry_list ; entry_list = g_list_next(entry_list) ) | |
| 2862 { | |
| 2863 PlaylistEntry *entry = entry_list->data; | |
| 2864 if ( ( entry->tuple != NULL ) && ( entry->tuple->album_name != NULL ) && | |
| 2865 ( regexec( ®ex , entry->tuple->album_name , 0 , NULL , 0 ) == 0 ) ) | |
| 2866 { | |
| 2867 tfound_list = g_list_append( tfound_list , entry ); | |
| 2868 } | |
| 2869 } | |
| 2870 g_list_free( found_list ); /* wipe old found_list */ | |
| 2871 found_list = tfound_list; /* move tfound_list in found_list */ | |
| 2872 regfree( ®ex ); | |
| 2873 } | |
| 2874 is_first_search = FALSE; | |
| 2875 } | |
| 2876 | |
| 2877 if ( tuple->performer != NULL ) | |
| 2878 { | |
| 2879 /* match by performer */ | |
| 2880 const gchar *regex_pattern = tuple->performer; | |
| 2881 regex_t regex; | |
| 2882 #if defined(USE_REGEX_PCRE) | |
| 2883 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 ) | |
| 2884 #else | |
| 2885 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 ) | |
| 2886 #endif | |
| 2887 { | |
| 2888 GList *tfound_list = NULL; | |
| 2889 if ( is_first_search == TRUE ) entry_list = playlist->entries; | |
| 2890 else entry_list = found_list; /* use found_list */ | |
| 2891 for ( ; entry_list ; entry_list = g_list_next(entry_list) ) | |
| 2892 { | |
| 2893 PlaylistEntry *entry = entry_list->data; | |
| 2894 if ( ( entry->tuple != NULL ) && ( entry->tuple->performer != NULL ) && | |
| 2895 ( regexec( ®ex , entry->tuple->performer , 0 , NULL , 0 ) == 0 ) ) | |
| 2896 { | |
| 2897 tfound_list = g_list_append( tfound_list , entry ); | |
| 2898 } | |
| 2899 } | |
| 2900 g_list_free( found_list ); /* wipe old found_list */ | |
| 2901 found_list = tfound_list; /* move tfound_list in found_list */ | |
| 2902 regfree( ®ex ); | |
| 2903 } | |
| 2904 is_first_search = FALSE; | |
| 2905 } | |
| 2906 | |
| 2907 if ( tuple->file_name != NULL ) | |
| 2908 { | |
| 2909 /* match by file_name */ | |
| 2910 const gchar *regex_pattern = tuple->file_name; | |
| 2911 regex_t regex; | |
| 2912 #if defined(USE_REGEX_PCRE) | |
| 2913 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 ) | |
| 2914 #else | |
| 2915 if ( regcomp( ®ex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 ) | |
| 2916 #endif | |
| 2917 { | |
| 2918 GList *tfound_list = NULL; | |
| 2919 if ( is_first_search == TRUE ) entry_list = playlist->entries; | |
| 2920 else entry_list = found_list; /* use found_list */ | |
| 2921 for ( ; entry_list ; entry_list = g_list_next(entry_list) ) | |
| 2922 { | |
| 2923 PlaylistEntry *entry = entry_list->data; | |
| 2924 if ( ( entry->tuple != NULL ) && ( entry->tuple->file_name != NULL ) && | |
| 2925 ( regexec( ®ex , entry->tuple->file_name , 0 , NULL , 0 ) == 0 ) ) | |
| 2926 { | |
| 2927 tfound_list = g_list_append( tfound_list , entry ); | |
| 2928 } | |
| 2929 } | |
| 2930 g_list_free( found_list ); /* wipe old found_list */ | |
| 2931 found_list = tfound_list; /* move tfound_list in found_list */ | |
| 2932 regfree( ®ex ); | |
| 2933 } | |
| 2934 is_first_search = FALSE; | |
| 2935 } | |
| 2936 | |
| 2937 /* NOTE: action = 0 -> default behaviour, select all matching entries */ | |
| 2938 /* if some entries are still in found_list, those | |
| 2939 are what the user is searching for; select them */ | |
| 2940 for ( sel_list = found_list ; sel_list ; sel_list = g_list_next(sel_list) ) | |
| 2941 { | |
| 2942 PlaylistEntry *entry = sel_list->data; | |
| 2943 entry->selected = TRUE; | |
| 2944 num_of_entries_found++; | |
| 2945 } | |
| 2946 | |
| 2947 g_list_free( found_list ); | |
| 2948 | |
| 2949 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2950 playlist_recalc_total_time(playlist); | |
| 2951 | |
| 2952 return num_of_entries_found; | |
| 2953 } | |
| 2954 | |
| 2955 void | |
| 2956 playlist_select_all(Playlist *playlist, gboolean set) | |
| 2957 { | |
| 2958 GList *list; | |
| 2959 | |
| 2960 PLAYLIST_LOCK(playlist->mutex); | |
| 2961 | |
| 2962 for (list = playlist->entries; list; list = g_list_next(list)) { | |
| 2963 PlaylistEntry *entry = list->data; | |
| 2964 entry->selected = set; | |
| 2965 } | |
| 2966 | |
| 2967 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2968 playlist_recalc_total_time(playlist); | |
| 2969 } | |
| 2970 | |
| 2971 void | |
| 2972 playlist_select_invert_all(Playlist *playlist) | |
| 2973 { | |
| 2974 GList *list; | |
| 2975 | |
| 2976 PLAYLIST_LOCK(playlist->mutex); | |
| 2977 | |
| 2978 for (list = playlist->entries; list; list = g_list_next(list)) { | |
| 2979 PlaylistEntry *entry = list->data; | |
| 2980 entry->selected = !entry->selected; | |
| 2981 } | |
| 2982 | |
| 2983 PLAYLIST_UNLOCK(playlist->mutex); | |
| 2984 playlist_recalc_total_time(playlist); | |
| 2985 } | |
| 2986 | |
| 2987 gboolean | |
| 2988 playlist_select_invert(Playlist *playlist, guint pos) | |
| 2989 { | |
| 2990 GList *list; | |
| 2991 gboolean invert_ok = FALSE; | |
| 2992 | |
| 2993 PLAYLIST_LOCK(playlist->mutex); | |
| 2994 | |
| 2995 if ((list = g_list_nth(playlist->entries, pos))) { | |
| 2996 PlaylistEntry *entry = list->data; | |
| 2997 entry->selected = !entry->selected; | |
| 2998 invert_ok = TRUE; | |
| 2999 } | |
| 3000 | |
| 3001 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3002 playlist_recalc_total_time(playlist); | |
| 3003 | |
| 3004 return invert_ok; | |
| 3005 } | |
| 3006 | |
| 3007 | |
| 3008 void | |
| 3009 playlist_select_range(Playlist *playlist, gint min_pos, gint max_pos, gboolean select) | |
| 3010 { | |
| 3011 GList *list; | |
| 3012 gint i; | |
| 3013 | |
| 3014 if (min_pos > max_pos) | |
| 3015 SWAP(min_pos, max_pos); | |
| 3016 | |
| 3017 PLAYLIST_LOCK(playlist->mutex); | |
| 3018 | |
| 3019 list = g_list_nth(playlist->entries, min_pos); | |
| 3020 for (i = min_pos; i <= max_pos && list; i++) { | |
| 3021 PlaylistEntry *entry = list->data; | |
| 3022 entry->selected = select; | |
| 3023 list = g_list_next(list); | |
| 3024 } | |
| 3025 | |
| 3026 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3027 | |
| 3028 playlist_recalc_total_time(playlist); | |
| 3029 } | |
| 3030 | |
| 3031 gboolean | |
| 3032 playlist_read_info_selection(Playlist *playlist) | |
| 3033 { | |
| 3034 GList *node; | |
| 3035 gboolean retval = FALSE; | |
| 3036 | |
| 3037 PLAYLIST_LOCK(playlist->mutex); | |
| 3038 | |
| 3039 for (node = playlist->entries; node; node = g_list_next(node)) { | |
| 3040 PlaylistEntry *entry = node->data; | |
| 3041 if (!entry->selected) | |
| 3042 continue; | |
| 3043 | |
| 3044 retval = TRUE; | |
| 3045 | |
| 3046 str_replace_in(&entry->title, NULL); | |
| 3047 entry->length = -1; | |
| 3048 | |
| 3049 /* invalidate mtime to reread */ | |
| 3050 if (entry->tuple != NULL) | |
| 3051 entry->tuple->mtime = -1; /* -1 denotes "non-initialized". now 0 is for stream etc. yaz */ | |
| 3052 | |
| 3053 if (!playlist_entry_get_info(entry)) { | |
| 3054 if (g_list_index(playlist->entries, entry) == -1) | |
| 3055 /* Entry disappeared while we looked it up. Restart. */ | |
| 3056 node = playlist->entries; | |
| 3057 } | |
| 3058 } | |
| 3059 | |
| 3060 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3061 | |
| 3062 playlistwin_update_list(playlist); | |
| 3063 playlist_recalc_total_time(playlist); | |
| 3064 | |
| 3065 return retval; | |
| 3066 } | |
| 3067 | |
| 3068 void | |
| 3069 playlist_read_info(Playlist *playlist, guint pos) | |
| 3070 { | |
| 3071 GList *node; | |
| 3072 | |
| 3073 PLAYLIST_LOCK(playlist->mutex); | |
| 3074 | |
| 3075 if ((node = g_list_nth(playlist->entries, pos))) { | |
| 3076 PlaylistEntry *entry = node->data; | |
| 3077 str_replace_in(&entry->title, NULL); | |
| 3078 entry->length = -1; | |
| 3079 playlist_entry_get_info(entry); | |
| 3080 } | |
| 3081 | |
| 3082 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3083 | |
| 3084 playlistwin_update_list(playlist); | |
| 3085 playlist_recalc_total_time(playlist); | |
| 3086 } | |
| 3087 | |
| 3088 Playlist * | |
| 3089 playlist_get_active(void) | |
| 3090 { | |
| 3091 if (playlists_iter != NULL) | |
| 3092 return (Playlist *) playlists_iter->data; | |
| 3093 | |
| 3094 return (Playlist *) playlists->data; | |
| 3095 } | |
| 3096 | |
| 3097 void | |
| 3098 playlist_set_shuffle(gboolean shuffle) | |
| 3099 { | |
| 3100 Playlist *playlist = playlist_get_active(); | |
| 3101 if (!playlist) | |
| 3102 return; | |
| 3103 | |
| 3104 PLAYLIST_LOCK(playlist->mutex); | |
| 3105 | |
| 3106 playlist_position_before_jump = NULL; | |
| 3107 | |
| 3108 cfg.shuffle = shuffle; | |
| 3109 playlist_generate_shuffle_list_nolock(playlist); | |
| 3110 | |
| 3111 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3112 } | |
| 3113 | |
| 3114 Playlist * | |
| 3115 playlist_new(void) | |
| 3116 { | |
| 3117 Playlist *playlist = g_new0(Playlist, 1); | |
| 3118 playlist->mutex = g_mutex_new(); | |
| 3119 playlist->loading_playlist = FALSE; | |
| 3120 | |
| 3121 playlist_set_current_name(playlist, NULL); | |
| 3122 playlist_clear(playlist); | |
| 3123 | |
| 3124 return playlist; | |
| 3125 } | |
| 3126 | |
| 3127 void | |
| 3128 playlist_free(Playlist *playlist) | |
| 3129 { | |
| 3130 g_mutex_free( playlist->mutex ); | |
| 3131 g_free( playlist ); | |
| 3132 return; | |
| 3133 } | |
| 3134 | |
| 3135 Playlist * | |
| 3136 playlist_new_from_selected(void) | |
| 3137 { | |
| 3138 Playlist *newpl = playlist_new(); | |
| 3139 Playlist *playlist = playlist_get_active(); | |
| 3140 GList *list = playlist_get_selected(playlist); | |
| 3141 | |
| 3142 playlist_add_playlist( newpl ); | |
| 3143 | |
| 3144 PLAYLIST_LOCK(playlist->mutex); | |
| 3145 | |
| 3146 while ( list != NULL ) | |
| 3147 { | |
| 3148 PlaylistEntry *entry = g_list_nth_data(playlist->entries, GPOINTER_TO_INT(list->data)); | |
| 3149 if ( entry->filename != NULL ) /* paranoid? oh well... */ | |
| 3150 playlist_add( newpl , entry->filename ); | |
| 3151 list = g_list_next(list); | |
| 3152 } | |
| 3153 | |
| 3154 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3155 | |
| 3156 playlist_recalc_total_time(newpl); | |
| 3157 playlistwin_update_list(playlist); | |
| 3158 | |
| 3159 return newpl; | |
| 3160 } | |
| 3161 | |
| 3162 const gchar * | |
| 3163 playlist_get_filename_to_play(Playlist *playlist) | |
| 3164 { | |
| 3165 const gchar *filename = NULL; | |
| 3166 | |
| 3167 if (!playlist) | |
| 3168 return NULL; | |
| 3169 | |
| 3170 PLAYLIST_LOCK(playlist->mutex); | |
| 3171 | |
| 3172 if (!playlist->position) { | |
| 3173 if (cfg.shuffle) | |
| 3174 playlist->position = playlist->shuffle->data; | |
| 3175 else | |
| 3176 playlist->position = playlist->entries->data; | |
| 3177 } | |
| 3178 | |
| 3179 filename = playlist->position->filename; | |
| 3180 | |
| 3181 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3182 | |
| 3183 return filename; | |
| 3184 } | |
| 3185 | |
| 3186 PlaylistEntry * | |
| 3187 playlist_get_entry_to_play(Playlist *playlist) | |
| 3188 { | |
| 3189 if (!playlist) | |
| 3190 return NULL; | |
| 3191 | |
| 3192 PLAYLIST_LOCK(playlist->mutex); | |
| 3193 | |
| 3194 if (!playlist->position) { | |
| 3195 if (cfg.shuffle) | |
| 3196 playlist->position = playlist->shuffle->data; | |
| 3197 else | |
| 3198 playlist->position = playlist->entries->data; | |
| 3199 } | |
| 3200 | |
| 3201 PLAYLIST_UNLOCK(playlist->mutex); | |
| 3202 | |
| 3203 return playlist->position; | |
| 3204 } |
