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