comparison src/audacious/playlist.c @ 2313:3149d4b1a9a9 trunk

[svn] - objective-make autodepend fixes - move all sourcecode into src/ and adjust Makefiles accordingly
author nenolod
date Fri, 12 Jan 2007 11:43:40 -0800
parents
children d88558b0de0a
comparison
equal deleted inserted replaced
2312:e1a5a66fb9cc 2313:3149d4b1a9a9
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( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
2781 #else
2782 if ( regcomp( &regex , 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( &regex , 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( &regex );
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( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
2811 #else
2812 if ( regcomp( &regex , 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( &regex , 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( &regex );
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( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
2841 #else
2842 if ( regcomp( &regex , 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( &regex , 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( &regex );
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( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
2871 #else
2872 if ( regcomp( &regex , 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( &regex , 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( &regex );
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 }