0
|
1 /* BMP (C) GPL 2003 $top_src_dir/AUTHORS
|
|
2 *
|
|
3 * based on:
|
|
4 *
|
|
5 * XMMS - Cross-platform multimedia player
|
|
6 * Copyright (C) 1998-2003 Peter Alm, Mikael Alm, Olle Hallnas,
|
|
7 * Thomas Nilsson and 4Front Technologies
|
|
8 * Copyright (C) 1999-2003 Haavard Kvaalen
|
|
9 *
|
|
10 *
|
|
11 * This program is free software; you can redistribute it and/or modify
|
|
12 * it under the terms of the GNU General Public License as published by
|
|
13 * the Free Software Foundation; either version 2 of the License, or
|
|
14 * (at your option) any later version.
|
|
15 *
|
|
16 * This program is distributed in the hope that it will be useful,
|
|
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 * GNU General Public License for more details.
|
|
20 *
|
|
21 * You should have received a copy of the GNU General Public License
|
|
22 * along with this program; if not, write to the Free Software
|
|
23 * Foundation, Inc., 59 Tmple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
24 */
|
|
25
|
|
26 #ifdef HAVE_CONFIG_H
|
|
27 # include "config.h"
|
|
28 #endif
|
|
29
|
|
30 #include "playlist.h"
|
|
31
|
|
32 #include <glib.h>
|
|
33 #include <glib/gprintf.h>
|
|
34 #include <stdlib.h>
|
|
35 #include <string.h>
|
|
36 #include <time.h>
|
|
37
|
|
38 #include <unistd.h>
|
|
39 #include <sys/types.h>
|
|
40 #include <sys/stat.h>
|
|
41 #include <sys/errno.h>
|
|
42
|
|
43 #include "input.h"
|
|
44 #include "main.h"
|
|
45 #include "mainwin.h"
|
|
46 #include "libaudacious/util.h"
|
|
47 #include "libaudacious/configdb.h"
|
|
48 #include "libaudacious/vfs.h"
|
|
49 #include "equalizer.h"
|
284
|
50 #include "libaudcore/playback.h"
|
0
|
51 #include "playlist.h"
|
|
52 #include "playlistwin.h"
|
|
53 #include "playlist_list.h"
|
|
54 #include "skin.h"
|
|
55 #include "urldecode.h"
|
|
56 #include "util.h"
|
|
57
|
|
58 #include "debug.h"
|
|
59
|
|
60 typedef gint (*PlaylistCompareFunc) (const PlaylistEntry * a, const PlaylistEntry * b);
|
|
61 typedef void (*PlaylistSaveFunc) (FILE * file);
|
|
62
|
|
63 PlaylistEntry *playlist_position;
|
|
64 G_LOCK_DEFINE(playlist);
|
|
65
|
|
66 /* NOTE: match the order listed in PlaylistFormat enum */
|
|
67 static const gchar *playlist_format_suffixes[] = {
|
|
68 ".m3u", ".pls", NULL
|
|
69 };
|
|
70
|
|
71 static GList *playlist = NULL;
|
|
72 static GList *shuffle_list = NULL;
|
|
73 static GList *queued_list = NULL;
|
|
74
|
|
75
|
|
76 G_LOCK_DEFINE(playlist_get_info_going);
|
|
77
|
|
78 static gchar *playlist_current_name = NULL;
|
|
79
|
|
80 static gboolean playlist_get_info_scan_active = FALSE;
|
|
81 static gboolean playlist_get_info_going = FALSE;
|
|
82 static GThread *playlist_get_info_thread;
|
|
83
|
|
84
|
|
85 static gint path_compare(const gchar * a, const gchar * b);
|
|
86 static gint playlist_compare_path(const PlaylistEntry * a, const PlaylistEntry * b);
|
|
87 static gint playlist_compare_filename(const PlaylistEntry * a, const PlaylistEntry * b);
|
|
88 static gint playlist_compare_title(const PlaylistEntry * a, const PlaylistEntry * b);
|
|
89 static gint playlist_compare_date(const PlaylistEntry * a, const PlaylistEntry * b);
|
|
90
|
|
91 static PlaylistCompareFunc playlist_compare_func_table[] = {
|
|
92 playlist_compare_path,
|
|
93 playlist_compare_filename,
|
|
94 playlist_compare_title,
|
|
95 playlist_compare_date
|
|
96 };
|
|
97
|
|
98 static void playlist_save_m3u(FILE * file);
|
|
99 static void playlist_save_pls(FILE * file);
|
|
100
|
|
101 static PlaylistSaveFunc playlist_save_func_table[] = {
|
|
102 playlist_save_m3u,
|
|
103 playlist_save_pls
|
|
104 };
|
|
105
|
|
106
|
|
107 static guint playlist_load_ins(const gchar * filename, gint pos);
|
|
108
|
|
109 static void playlist_load_ins_file(const gchar * filename,
|
|
110 const gchar * playlist_name, gint pos,
|
|
111 const gchar * title, gint len);
|
|
112
|
|
113 static void playlist_generate_shuffle_list(void);
|
|
114 static void playlist_generate_shuffle_list_nolock(void);
|
|
115
|
|
116 static void playlist_recalc_total_time_nolock(void);
|
|
117 static void playlist_recalc_total_time(void);
|
|
118
|
|
119
|
|
120 PlaylistEntry *
|
|
121 playlist_entry_new(const gchar * filename,
|
|
122 const gchar * title,
|
|
123 const gint length)
|
|
124 {
|
|
125 PlaylistEntry *entry;
|
|
126
|
|
127 entry = g_new0(PlaylistEntry, 1);
|
|
128 entry->filename = g_strdup(filename);
|
|
129 entry->title = str_to_utf8(title);
|
|
130 entry->length = length;
|
|
131 entry->selected = FALSE;
|
|
132
|
|
133 return entry;
|
|
134 }
|
|
135
|
|
136 void
|
|
137 playlist_entry_free(PlaylistEntry * entry)
|
|
138 {
|
|
139 if (!entry)
|
|
140 return;
|
|
141
|
|
142 g_free(entry->filename);
|
|
143 g_free(entry->title);
|
|
144 g_free(entry);
|
|
145 }
|
|
146
|
|
147 static gboolean
|
|
148 playlist_entry_get_info(PlaylistEntry * entry)
|
|
149 {
|
|
150 gchar *title = NULL;
|
|
151 gint length = -1;
|
|
152
|
|
153 g_return_val_if_fail(entry != NULL, FALSE);
|
|
154
|
|
155 input_get_song_info(entry->filename, &title, &length);
|
|
156 if (!title && length == -1)
|
|
157 return FALSE;
|
|
158
|
|
159 /* entry is still around */
|
|
160 entry->title = title;
|
|
161 entry->length = length;
|
|
162
|
|
163 return TRUE;
|
|
164 }
|
|
165
|
|
166
|
|
167 const gchar *
|
|
168 playlist_get_current_name(void)
|
|
169 {
|
|
170 return playlist_current_name;
|
|
171 }
|
|
172
|
|
173 gboolean
|
|
174 playlist_set_current_name(const gchar * filename)
|
|
175 {
|
|
176 g_free(playlist_current_name);
|
|
177
|
|
178 if (!filename) {
|
|
179 playlist_current_name = NULL;
|
|
180 return FALSE;
|
|
181 }
|
|
182
|
|
183 playlist_current_name = g_strdup(filename);
|
|
184 return TRUE;
|
|
185 }
|
|
186
|
|
187 static GList *
|
|
188 find_playlist_position_list(void)
|
|
189 {
|
|
190 REQUIRE_STATIC_LOCK(playlist);
|
|
191
|
|
192 if (!playlist_position) {
|
|
193 if (cfg.shuffle)
|
|
194 return shuffle_list;
|
|
195 else
|
|
196 return playlist;
|
|
197 }
|
|
198
|
|
199 if (cfg.shuffle)
|
|
200 return g_list_find(shuffle_list, playlist_position);
|
|
201 else
|
|
202 return g_list_find(playlist, playlist_position);
|
|
203 }
|
|
204
|
|
205 static void
|
|
206 play_queued(void)
|
|
207 {
|
|
208 GList *tmp = queued_list;
|
|
209
|
|
210 REQUIRE_STATIC_LOCK(playlist);
|
|
211
|
|
212 playlist_position = queued_list->data;
|
|
213 queued_list = g_list_remove_link(queued_list, queued_list);
|
|
214 g_list_free_1(tmp);
|
|
215 }
|
|
216
|
|
217 void
|
|
218 playlist_clear(void)
|
|
219 {
|
|
220 if (bmp_playback_get_playing())
|
|
221 bmp_playback_stop();
|
|
222
|
|
223 PLAYLIST_LOCK();
|
|
224
|
|
225 if (playlist) {
|
|
226 g_list_foreach(playlist, (GFunc) playlist_entry_free, NULL);
|
|
227 g_list_free(playlist);
|
|
228
|
|
229 playlist = NULL;
|
|
230 playlist_position = NULL;
|
|
231 }
|
|
232
|
|
233 PLAYLIST_UNLOCK();
|
|
234
|
|
235 playlist_generate_shuffle_list();
|
|
236 playlistwin_update_list();
|
|
237 playlist_recalc_total_time();
|
|
238 }
|
|
239
|
|
240 void
|
|
241 playlist_delete_node(GList * node, gboolean * set_info_text,
|
|
242 gboolean * restart_playing)
|
|
243 {
|
|
244 PlaylistEntry *entry;
|
|
245 GList *playing_song = NULL;
|
|
246
|
|
247 REQUIRE_STATIC_LOCK(playlist);
|
|
248
|
|
249 /* We call g_list_find manually here because we don't want an item
|
|
250 * in the shuffle_list */
|
|
251
|
|
252 if (playlist_position)
|
|
253 playing_song = g_list_find(playlist, playlist_position);
|
|
254
|
|
255 entry = PLAYLIST_ENTRY(node->data);
|
|
256
|
|
257 if (playing_song == node) {
|
|
258 *set_info_text = TRUE;
|
|
259
|
|
260 if (bmp_playback_get_playing()) {
|
|
261 PLAYLIST_UNLOCK();
|
|
262 bmp_playback_stop();
|
|
263 PLAYLIST_LOCK();
|
|
264 *restart_playing = TRUE;
|
|
265 }
|
|
266
|
|
267 playing_song = find_playlist_position_list();
|
|
268
|
|
269 if (g_list_next(playing_song))
|
|
270 playlist_position = g_list_next(playing_song)->data;
|
|
271 else if (g_list_previous(playing_song))
|
|
272 playlist_position = g_list_previous(playing_song)->data;
|
|
273 else
|
|
274 playlist_position = NULL;
|
|
275
|
|
276 /* Make sure the entry did not disappear under us */
|
|
277 if (g_list_index(playlist_get(), entry) == -1)
|
|
278 return;
|
|
279
|
|
280 }
|
|
281 else if (g_list_position(playlist, playing_song) >
|
|
282 g_list_position(playlist, node)) {
|
|
283 *set_info_text = TRUE;
|
|
284 }
|
|
285
|
|
286 shuffle_list = g_list_remove(shuffle_list, entry);
|
|
287 playlist = g_list_remove_link(playlist, node);
|
|
288 playlist_entry_free(entry);
|
|
289 g_list_free_1(node);
|
|
290
|
|
291 playlist_recalc_total_time_nolock();
|
|
292 }
|
|
293
|
|
294 void
|
|
295 playlist_delete_index(guint pos)
|
|
296 {
|
|
297 gboolean restart_playing = FALSE, set_info_text = FALSE;
|
|
298 GList *node;
|
|
299
|
|
300 PLAYLIST_LOCK();
|
|
301
|
|
302 if (!playlist) {
|
|
303 PLAYLIST_UNLOCK();
|
|
304 return;
|
|
305 }
|
|
306
|
|
307 node = g_list_nth(playlist, pos);
|
|
308
|
|
309 if (!node) {
|
|
310 PLAYLIST_UNLOCK();
|
|
311 return;
|
|
312 }
|
|
313
|
|
314 playlist_delete_node(node, &set_info_text, &restart_playing);
|
|
315
|
|
316 PLAYLIST_UNLOCK();
|
|
317
|
|
318 playlist_recalc_total_time();
|
|
319
|
|
320 playlistwin_update_list();
|
|
321 if (restart_playing) {
|
|
322 if (playlist_position) {
|
|
323 bmp_playback_initiate();
|
|
324 }
|
|
325 else {
|
|
326 mainwin_clear_song_info();
|
|
327 }
|
|
328 }
|
|
329 else if (set_info_text) {
|
|
330 mainwin_set_info_text();
|
|
331 }
|
|
332 }
|
|
333
|
|
334 void
|
|
335 playlist_delete_filenames(GList * filenames)
|
|
336 {
|
|
337 GList *node, *fnode;
|
|
338 gboolean set_info_text = FALSE, restart_playing = FALSE;
|
|
339
|
|
340 PLAYLIST_LOCK();
|
|
341
|
|
342 for (fnode = filenames; fnode; fnode = g_list_next(fnode)) {
|
|
343 node = playlist;
|
|
344
|
|
345 while (node) {
|
|
346 GList *next = g_list_next(node);
|
|
347 PlaylistEntry *entry = node->data;
|
|
348
|
|
349 if (!strcmp(entry->filename, fnode->data))
|
|
350 playlist_delete_node(node, &set_info_text, &restart_playing);
|
|
351
|
|
352 node = next;
|
|
353 }
|
|
354 }
|
|
355
|
|
356 playlist_recalc_total_time();
|
|
357 PLAYLIST_UNLOCK();
|
|
358
|
|
359 playlistwin_update_list();
|
|
360
|
|
361 if (restart_playing) {
|
|
362 if (playlist_position) {
|
|
363 bmp_playback_initiate();
|
|
364 }
|
|
365 else {
|
|
366 mainwin_clear_song_info();
|
|
367 }
|
|
368 }
|
|
369 else if (set_info_text) {
|
|
370 mainwin_set_info_text();
|
|
371 }
|
|
372
|
|
373 }
|
|
374
|
|
375 void
|
|
376 playlist_delete(gboolean crop)
|
|
377 {
|
|
378 gboolean restart_playing = FALSE, set_info_text = FALSE;
|
|
379 GList *node, *next_node;
|
|
380 PlaylistEntry *entry;
|
|
381
|
|
382 PLAYLIST_LOCK();
|
|
383
|
|
384 node = playlist;
|
|
385
|
|
386 while (node) {
|
|
387 entry = PLAYLIST_ENTRY(node->data);
|
|
388
|
|
389 next_node = g_list_next(node);
|
|
390
|
|
391 if ((entry->selected && !crop) || (!entry->selected && crop)) {
|
|
392 playlist_delete_node(node, &set_info_text, &restart_playing);
|
|
393 }
|
|
394
|
|
395 node = next_node;
|
|
396 }
|
|
397
|
|
398 PLAYLIST_UNLOCK();
|
|
399
|
|
400 playlist_recalc_total_time();
|
|
401
|
|
402 if (set_info_text) {
|
|
403 mainwin_set_info_text();
|
|
404 }
|
|
405
|
|
406 if (restart_playing) {
|
|
407 if (playlist_position) {
|
|
408 bmp_playback_initiate();
|
|
409 }
|
|
410 else {
|
|
411 mainwin_clear_song_info();
|
|
412 }
|
|
413 }
|
|
414
|
|
415 playlistwin_update_list();
|
|
416 }
|
|
417
|
|
418 static void
|
|
419 __playlist_ins_with_info(const gchar * filename,
|
|
420 gint pos,
|
|
421 const gchar * title,
|
|
422 gint len)
|
|
423 {
|
|
424 g_return_if_fail(filename != NULL);
|
|
425
|
|
426 PLAYLIST_LOCK();
|
|
427 playlist = g_list_insert(playlist,
|
|
428 playlist_entry_new(filename, title, len),
|
|
429 pos);
|
|
430 PLAYLIST_UNLOCK();
|
|
431
|
|
432 playlist_get_info_scan_active = TRUE;
|
|
433 }
|
|
434
|
|
435 static void
|
|
436 __playlist_ins(const gchar * filename, gint pos)
|
|
437 {
|
|
438 __playlist_ins_with_info(filename, pos, NULL, -1);
|
|
439 playlist_recalc_total_time();
|
|
440 }
|
|
441
|
|
442
|
|
443 PlaylistFormat
|
|
444 playlist_format_get_from_name(const gchar * filename)
|
|
445 {
|
|
446 int i;
|
|
447
|
|
448 for (i = 0; i < PLAYLIST_FORMAT_COUNT; i++)
|
|
449 {
|
|
450 if (str_has_suffix_nocase(filename, playlist_format_suffixes[i]))
|
|
451 return i;
|
|
452 }
|
|
453
|
|
454 return PLAYLIST_FORMAT_UNKNOWN;
|
|
455 }
|
|
456
|
|
457 gboolean
|
|
458 is_playlist_name(const gchar * filename)
|
|
459 {
|
|
460 g_return_val_if_fail(filename != NULL, FALSE);
|
|
461 return playlist_format_get_from_name(filename) != PLAYLIST_FORMAT_UNKNOWN;
|
|
462 }
|
|
463
|
|
464
|
|
465 gboolean
|
|
466 playlist_ins(const gchar * filename, gint pos)
|
|
467 {
|
|
468 gchar buf[64], *p;
|
|
469 gint r;
|
|
470 VFSFile *file;
|
|
471
|
|
472 if (is_playlist_name(filename)) {
|
|
473 playlist_load_ins(filename, pos);
|
|
474 return TRUE;
|
|
475 }
|
|
476
|
|
477 if (input_check_file(filename, TRUE)) {
|
|
478 __playlist_ins(filename, pos);
|
|
479 playlist_generate_shuffle_list();
|
|
480 playlistwin_update_list();
|
|
481 return TRUE;
|
|
482 }
|
|
483
|
|
484 /* Some files (typically produced by some cgi-scripts) don't have
|
|
485 * the correct extension. Try to recognize these files by looking
|
|
486 * at their content. We only check for http entries since it does
|
|
487 * not make sense to have file entries in a playlist fetched from
|
|
488 * the net. */
|
|
489
|
|
490 /* Some strange people put fifo's with the .mp3 extension, so we
|
|
491 * need to make sure it's a real file (otherwise fread() may block
|
|
492 * and stall the entire program) */
|
|
493
|
|
494 /* FIXME: bah, FIFOs actually pass this regular file test */
|
|
495 if (!vfs_file_test(filename, G_FILE_TEST_IS_REGULAR))
|
|
496 return FALSE;
|
|
497
|
|
498 if (!(file = vfs_fopen(filename, "rb")))
|
|
499 return FALSE;
|
|
500
|
|
501 r = vfs_fread(buf, 1, sizeof(buf), file);
|
|
502 vfs_fclose(file);
|
|
503
|
|
504 for (p = buf; r-- > 0 && (*p == '\r' || *p == '\n'); p++);
|
|
505
|
|
506 if (r > 5 && str_has_prefix_nocase(p, "http:")) {
|
|
507 playlist_load_ins(filename, pos);
|
|
508 return TRUE;
|
|
509 }
|
|
510
|
|
511 return FALSE;
|
|
512 }
|
|
513
|
|
514 /* FIXME: The next few functions are specific to Unix
|
|
515 * filesystems. Either abstract it away, or don't even bother checking
|
|
516 * at such low level */
|
|
517
|
|
518 typedef struct {
|
|
519 dev_t dev;
|
|
520 ino_t ino;
|
|
521 } DeviceInode;
|
|
522
|
|
523 static DeviceInode *
|
|
524 devino_new(dev_t device,
|
|
525 ino_t inode)
|
|
526 {
|
|
527 DeviceInode *devino = g_new0(DeviceInode, 1);
|
|
528
|
|
529 if (devino)
|
|
530 {
|
|
531 devino->dev = device;
|
|
532 devino->ino = inode;
|
|
533 }
|
|
534
|
|
535 return devino;
|
|
536 }
|
|
537
|
|
538 static guint
|
|
539 devino_hash(gconstpointer key)
|
|
540 {
|
|
541 const DeviceInode *d = key;
|
|
542 return d->ino;
|
|
543 }
|
|
544
|
|
545 static gint
|
|
546 devino_compare(gconstpointer a,
|
|
547 gconstpointer b)
|
|
548 {
|
|
549 const DeviceInode *da = a, *db = b;
|
|
550 return (da->dev == db->dev && da->ino == db->ino);
|
|
551 }
|
|
552
|
|
553 static gboolean
|
|
554 devino_destroy(gpointer key,
|
|
555 gpointer value,
|
|
556 gpointer data)
|
|
557 {
|
|
558 g_free(key);
|
|
559 return TRUE;
|
|
560 }
|
|
561
|
|
562 static gboolean
|
|
563 file_is_hidden(const gchar * filename)
|
|
564 {
|
|
565 // FIXME: remove the const cast
|
|
566 g_return_val_if_fail(filename != NULL, FALSE);
|
|
567 return (g_basename((gchar *) filename)[0] == '.');
|
|
568 }
|
|
569
|
|
570 static GList *
|
|
571 playlist_dir_find_files(const gchar * path,
|
|
572 gboolean background,
|
|
573 GHashTable * htab)
|
|
574 {
|
|
575 GDir *dir;
|
|
576 GList *list = NULL, *ilist;
|
|
577 const gchar *dir_entry;
|
|
578
|
|
579 struct stat statbuf;
|
|
580 DeviceInode *devino;
|
|
581
|
|
582 if (!g_file_test(path, G_FILE_TEST_IS_DIR))
|
|
583 return NULL;
|
|
584
|
|
585 stat(path, &statbuf);
|
|
586 devino = devino_new(statbuf.st_dev, statbuf.st_ino);
|
|
587
|
|
588 if (g_hash_table_lookup(htab, devino)) {
|
|
589 g_free(devino);
|
|
590 return NULL;
|
|
591 }
|
|
592
|
|
593 g_hash_table_insert(htab, devino, GINT_TO_POINTER(1));
|
|
594
|
|
595 if ((ilist = input_scan_dir(path))) {
|
|
596 GList *node;
|
|
597 for (node = ilist; node; node = g_list_next(node)) {
|
|
598 gchar *name = g_build_filename(path, node->data, NULL);
|
|
599 list = g_list_prepend(list, name);
|
|
600 g_free(node->data);
|
|
601 }
|
|
602 g_list_free(ilist);
|
|
603 return list;
|
|
604 }
|
|
605
|
|
606 if (!(dir = g_dir_open(path, 0, NULL)))
|
|
607 return NULL;
|
|
608
|
|
609 while ((dir_entry = g_dir_read_name(dir))) {
|
|
610 gchar *filename;
|
|
611
|
|
612 if (file_is_hidden(dir_entry))
|
|
613 continue;
|
|
614
|
|
615 filename = g_build_filename(path, dir_entry, NULL);
|
|
616
|
|
617 if (g_file_test(filename, G_FILE_TEST_IS_DIR)) {
|
|
618 GList *sub;
|
|
619 sub = playlist_dir_find_files(filename, background, htab);
|
|
620 g_free(filename);
|
|
621 list = g_list_concat(list, sub);
|
|
622 }
|
|
623 else if (input_check_file(filename, TRUE))
|
|
624 list = g_list_prepend(list, filename);
|
|
625 else
|
|
626 g_free(filename);
|
|
627
|
|
628 while (background && gtk_events_pending())
|
|
629 gtk_main_iteration();
|
|
630 }
|
|
631 g_dir_close(dir);
|
|
632
|
|
633 return list;
|
|
634 }
|
|
635
|
|
636 gboolean
|
|
637 playlist_add(const gchar * filename)
|
|
638 {
|
|
639 return playlist_ins(filename, -1);
|
|
640 }
|
|
641
|
|
642 guint
|
|
643 playlist_add_dir(const gchar * directory)
|
|
644 {
|
|
645 return playlist_ins_dir(directory, -1, TRUE);
|
|
646 }
|
|
647
|
|
648 guint
|
|
649 playlist_add_url(const gchar * url)
|
|
650 {
|
|
651 return playlist_ins_url(url, -1);
|
|
652 }
|
|
653
|
|
654 guint
|
|
655 playlist_ins_dir(const gchar * path,
|
|
656 gint pos,
|
|
657 gboolean background)
|
|
658 {
|
|
659 guint entries = 0;
|
|
660 GList *list, *node;
|
|
661 GHashTable *htab;
|
|
662
|
|
663 htab = g_hash_table_new(devino_hash, devino_compare);
|
|
664
|
|
665 list = playlist_dir_find_files(path, background, htab);
|
|
666 list = g_list_sort(list, (GCompareFunc) path_compare);
|
|
667
|
|
668 g_hash_table_foreach_remove(htab, devino_destroy, NULL);
|
|
669
|
|
670 for (node = list; node; node = g_list_next(node)) {
|
|
671 __playlist_ins(node->data, pos);
|
|
672 g_free(node->data);
|
|
673 entries++;
|
|
674 if (pos >= 0)
|
|
675 pos++;
|
|
676 }
|
|
677
|
|
678 g_list_free(list);
|
|
679
|
|
680 playlist_recalc_total_time();
|
|
681 playlist_generate_shuffle_list();
|
|
682 playlistwin_update_list();
|
|
683 return entries;
|
|
684 }
|
|
685
|
|
686 guint
|
|
687 playlist_ins_url(const gchar * string,
|
|
688 gint pos)
|
|
689 {
|
|
690 gchar *tmp;
|
|
691 gint i = 1, entries = 0;
|
|
692 gboolean first = TRUE;
|
|
693 guint firstpos = 0;
|
|
694 gboolean success = FALSE;
|
|
695 gchar *decoded = NULL;
|
|
696
|
|
697 g_return_val_if_fail(string != NULL, 0);
|
|
698
|
|
699 playlistwin_update_list();
|
|
700
|
|
701 while (*string) {
|
|
702 GList *node;
|
|
703 tmp = strchr(string, '\n');
|
|
704 if (tmp) {
|
|
705 if (*(tmp - 1) == '\r')
|
|
706 *(tmp - 1) = '\0';
|
|
707 *tmp = '\0';
|
|
708 }
|
|
709
|
|
710 if (!(decoded = xmms_urldecode_path(string)))
|
|
711 decoded = g_strdup(string);
|
|
712
|
|
713 if (g_file_test(decoded, G_FILE_TEST_IS_DIR)) {
|
|
714 i = playlist_ins_dir(decoded, pos, FALSE);
|
|
715 }
|
|
716 else {
|
|
717 if (is_playlist_name(decoded)) {
|
|
718 i = playlist_load_ins(decoded, pos);
|
|
719 }
|
|
720 else {
|
|
721 success = playlist_ins(decoded, pos);
|
|
722 i = 1;
|
|
723 }
|
|
724 }
|
|
725
|
|
726 g_free(decoded);
|
|
727
|
|
728 PLAYLIST_LOCK();
|
|
729 node = g_list_nth(playlist_get(), pos);
|
|
730 PLAYLIST_UNLOCK();
|
|
731
|
|
732 entries += i;
|
|
733
|
|
734 if (first) {
|
|
735 first = FALSE;
|
|
736 firstpos = pos;
|
|
737 }
|
|
738
|
|
739 if (pos >= 0)
|
|
740 pos += i;
|
|
741 if (!tmp)
|
|
742 break;
|
|
743
|
|
744 string = tmp + 1;
|
|
745 }
|
|
746
|
|
747 playlist_recalc_total_time();
|
|
748 playlist_generate_shuffle_list();
|
|
749 playlistwin_update_list();
|
|
750
|
|
751 return entries;
|
|
752 }
|
|
753
|
|
754 void
|
|
755 playlist_set_info(const gchar * title, gint length, gint rate,
|
|
756 gint freq, gint nch)
|
|
757 {
|
|
758 PLAYLIST_LOCK();
|
|
759
|
|
760 if (playlist_position) {
|
|
761 g_free(playlist_position->title);
|
|
762 playlist_position->title = g_strdup(title);
|
|
763 playlist_position->length = length;
|
|
764 }
|
|
765
|
|
766 PLAYLIST_UNLOCK();
|
|
767
|
|
768 playlist_recalc_total_time();
|
|
769
|
|
770 mainwin_set_song_info(rate, freq, nch);
|
|
771 }
|
|
772
|
|
773 void
|
|
774 playlist_check_pos_current(void)
|
|
775 {
|
|
776 gint pos, row, bottom;
|
|
777
|
|
778 PLAYLIST_LOCK();
|
|
779 if (!playlist || !playlist_position || !playlistwin_list) {
|
|
780 PLAYLIST_UNLOCK();
|
|
781 return;
|
|
782 }
|
|
783
|
|
784 pos = g_list_index(playlist, playlist_position);
|
|
785
|
|
786 if (playlistwin_item_visible(pos)) {
|
|
787 PLAYLIST_UNLOCK();
|
|
788 return;
|
|
789 }
|
|
790
|
|
791 bottom = MAX(0, playlist_get_length_nolock() -
|
|
792 playlistwin_list->pl_num_visible);
|
|
793 row = CLAMP(pos - playlistwin_list->pl_num_visible / 2, 0, bottom);
|
|
794 PLAYLIST_UNLOCK();
|
|
795 playlistwin_set_toprow(row);
|
|
796 }
|
|
797
|
|
798 void
|
|
799 playlist_next(void)
|
|
800 {
|
|
801 GList *plist_pos_list;
|
|
802 gboolean restart_playing = FALSE;
|
|
803
|
|
804 PLAYLIST_LOCK();
|
|
805 if (!playlist) {
|
|
806 PLAYLIST_UNLOCK();
|
|
807 return;
|
|
808 }
|
|
809
|
|
810 plist_pos_list = find_playlist_position_list();
|
|
811
|
|
812 if (!cfg.repeat && !g_list_next(plist_pos_list)) {
|
|
813 PLAYLIST_UNLOCK();
|
|
814 return;
|
|
815 }
|
|
816
|
|
817 if (bmp_playback_get_playing()) {
|
|
818 /* We need to stop before changing playlist_position */
|
|
819 PLAYLIST_UNLOCK();
|
|
820 bmp_playback_stop();
|
|
821 PLAYLIST_LOCK();
|
|
822 restart_playing = TRUE;
|
|
823 }
|
|
824
|
|
825 plist_pos_list = find_playlist_position_list();
|
|
826 if (queued_list)
|
|
827 play_queued();
|
|
828 else if (g_list_next(plist_pos_list))
|
|
829 playlist_position = g_list_next(plist_pos_list)->data;
|
|
830 else if (cfg.repeat) {
|
|
831 playlist_position = NULL;
|
|
832 playlist_generate_shuffle_list_nolock();
|
|
833 if (cfg.shuffle)
|
|
834 playlist_position = shuffle_list->data;
|
|
835 else
|
|
836 playlist_position = playlist->data;
|
|
837 }
|
|
838 PLAYLIST_UNLOCK();
|
|
839 playlist_check_pos_current();
|
|
840
|
|
841 if (restart_playing)
|
|
842 bmp_playback_initiate();
|
|
843 else {
|
|
844 mainwin_set_info_text();
|
|
845 playlistwin_update_list();
|
|
846 }
|
|
847 }
|
|
848
|
|
849 void
|
|
850 playlist_prev(void)
|
|
851 {
|
|
852 GList *plist_pos_list;
|
|
853 gboolean restart_playing = FALSE;
|
|
854
|
|
855 PLAYLIST_LOCK();
|
|
856 if (!playlist) {
|
|
857 PLAYLIST_UNLOCK();
|
|
858 return;
|
|
859 }
|
|
860
|
|
861 plist_pos_list = find_playlist_position_list();
|
|
862
|
|
863 if (!cfg.repeat && !g_list_previous(plist_pos_list)) {
|
|
864 PLAYLIST_UNLOCK();
|
|
865 return;
|
|
866 }
|
|
867
|
|
868 if (bmp_playback_get_playing()) {
|
|
869 /* We need to stop before changing playlist_position */
|
|
870 PLAYLIST_UNLOCK();
|
|
871 bmp_playback_stop();
|
|
872 PLAYLIST_LOCK();
|
|
873 restart_playing = TRUE;
|
|
874 }
|
|
875
|
|
876 plist_pos_list = find_playlist_position_list();
|
|
877 if (g_list_previous(plist_pos_list)) {
|
|
878 playlist_position = g_list_previous(plist_pos_list)->data;
|
|
879 }
|
|
880 else if (cfg.repeat) {
|
|
881 GList *node;
|
|
882 playlist_position = NULL;
|
|
883 playlist_generate_shuffle_list_nolock();
|
|
884 if (cfg.shuffle)
|
|
885 node = g_list_last(shuffle_list);
|
|
886 else
|
|
887 node = g_list_last(playlist);
|
|
888 if (node)
|
|
889 playlist_position = node->data;
|
|
890 }
|
|
891
|
|
892 PLAYLIST_UNLOCK();
|
|
893
|
|
894 playlist_check_pos_current();
|
|
895
|
|
896 if (restart_playing)
|
|
897 bmp_playback_initiate();
|
|
898 else {
|
|
899 mainwin_set_info_text();
|
|
900 playlistwin_update_list();
|
|
901 }
|
|
902 }
|
|
903
|
|
904 void
|
|
905 playlist_queue(void)
|
|
906 {
|
|
907 GList *list = playlist_get_selected();
|
|
908 GList *it = list;
|
|
909
|
|
910 PLAYLIST_LOCK();
|
|
911
|
|
912 while (it) {
|
|
913 GList *next = g_list_next(it);
|
|
914 GList *tmp;
|
|
915
|
|
916 it->data = g_list_nth_data(playlist, GPOINTER_TO_INT(it->data));
|
|
917 if ((tmp = g_list_find(queued_list, it->data))) {
|
|
918 queued_list = g_list_remove_link(queued_list, tmp);
|
|
919 g_list_free_1(tmp);
|
|
920 list = g_list_remove_link(list, it);
|
|
921 g_list_free_1(it);
|
|
922 }
|
|
923
|
|
924 it = next;
|
|
925 }
|
|
926
|
|
927 queued_list = g_list_concat(queued_list, list);
|
|
928
|
|
929 PLAYLIST_UNLOCK();
|
|
930
|
|
931 playlist_recalc_total_time();
|
|
932 playlistwin_update_list();
|
|
933 }
|
|
934
|
|
935 void
|
|
936 playlist_queue_position(guint pos)
|
|
937 {
|
|
938 GList *tmp;
|
|
939 PlaylistEntry *entry;
|
|
940
|
|
941 PLAYLIST_LOCK();
|
|
942 entry = g_list_nth_data(playlist, pos);
|
|
943 if ((tmp = g_list_find(queued_list, entry))) {
|
|
944 queued_list = g_list_remove_link(queued_list, tmp);
|
|
945 g_list_free_1(tmp);
|
|
946 }
|
|
947 else
|
|
948 queued_list = g_list_append(queued_list, entry);
|
|
949 PLAYLIST_UNLOCK();
|
|
950
|
|
951 playlist_recalc_total_time();
|
|
952 playlistwin_update_list();
|
|
953 }
|
|
954
|
|
955 gboolean
|
|
956 playlist_is_position_queued(guint pos)
|
|
957 {
|
|
958 PlaylistEntry *entry;
|
|
959 GList *tmp;
|
|
960
|
|
961 PLAYLIST_LOCK();
|
|
962 entry = g_list_nth_data(playlist, pos);
|
|
963 tmp = g_list_find(queued_list, entry);
|
|
964 PLAYLIST_UNLOCK();
|
|
965
|
|
966 return tmp != NULL;
|
|
967 }
|
|
968
|
|
969 void
|
|
970 playlist_clear_queue(void)
|
|
971 {
|
|
972 PLAYLIST_LOCK();
|
|
973 g_list_free(queued_list);
|
|
974 queued_list = NULL;
|
|
975 PLAYLIST_UNLOCK();
|
|
976
|
|
977 playlist_recalc_total_time();
|
|
978 playlistwin_update_list();
|
|
979 }
|
|
980
|
|
981 void
|
|
982 playlist_queue_remove(guint pos)
|
|
983 {
|
|
984 void *entry;
|
|
985
|
|
986 PLAYLIST_LOCK();
|
|
987 entry = g_list_nth_data(playlist, pos);
|
|
988 queued_list = g_list_remove(queued_list, entry);
|
|
989 PLAYLIST_UNLOCK();
|
|
990
|
|
991 playlistwin_update_list();
|
|
992 }
|
|
993
|
|
994 gint
|
|
995 playlist_get_queue_position(PlaylistEntry * entry)
|
|
996 {
|
|
997 return g_list_index(queued_list, entry);
|
|
998 }
|
|
999
|
|
1000 void
|
|
1001 playlist_set_position(guint pos)
|
|
1002 {
|
|
1003 GList *node;
|
|
1004 gboolean restart_playing = FALSE;
|
|
1005
|
|
1006 PLAYLIST_LOCK();
|
|
1007 if (!playlist) {
|
|
1008 PLAYLIST_UNLOCK();
|
|
1009 return;
|
|
1010 }
|
|
1011
|
|
1012 node = g_list_nth(playlist, pos);
|
|
1013 if (!node) {
|
|
1014 PLAYLIST_UNLOCK();
|
|
1015 return;
|
|
1016 }
|
|
1017
|
|
1018 if (bmp_playback_get_playing()) {
|
|
1019 /* We need to stop before changing playlist_position */
|
|
1020 PLAYLIST_UNLOCK();
|
|
1021 bmp_playback_stop();
|
|
1022 PLAYLIST_LOCK();
|
|
1023 restart_playing = TRUE;
|
|
1024 }
|
|
1025
|
|
1026 playlist_position = node->data;
|
|
1027 PLAYLIST_UNLOCK();
|
|
1028 playlist_check_pos_current();
|
|
1029
|
|
1030 if (restart_playing)
|
|
1031 bmp_playback_initiate();
|
|
1032 else {
|
|
1033 mainwin_set_info_text();
|
|
1034 playlistwin_update_list();
|
|
1035 }
|
|
1036
|
|
1037 /*
|
|
1038 * Regenerate the shuffle list when the user set a position
|
|
1039 * manually
|
|
1040 */
|
|
1041 playlist_generate_shuffle_list();
|
|
1042 playlist_recalc_total_time();
|
|
1043 }
|
|
1044
|
|
1045 void
|
|
1046 playlist_eof_reached(void)
|
|
1047 {
|
|
1048 GList *plist_pos_list;
|
|
1049
|
|
1050 bmp_playback_stop();
|
|
1051
|
|
1052 PLAYLIST_LOCK();
|
|
1053 plist_pos_list = find_playlist_position_list();
|
|
1054
|
|
1055 if (cfg.no_playlist_advance) {
|
|
1056 PLAYLIST_UNLOCK();
|
|
1057 mainwin_clear_song_info();
|
|
1058 if (cfg.repeat)
|
|
1059 bmp_playback_initiate();
|
|
1060 return;
|
|
1061 }
|
|
1062
|
|
1063 if (queued_list) {
|
|
1064 play_queued();
|
|
1065 }
|
|
1066 else if (!g_list_next(plist_pos_list)) {
|
|
1067 if (cfg.shuffle) {
|
|
1068 playlist_position = NULL;
|
|
1069 playlist_generate_shuffle_list_nolock();
|
|
1070 }
|
|
1071 else
|
|
1072 playlist_position = playlist->data;
|
|
1073
|
|
1074 if (!cfg.repeat) {
|
|
1075 PLAYLIST_UNLOCK();
|
|
1076 mainwin_clear_song_info();
|
|
1077 mainwin_set_info_text();
|
|
1078 return;
|
|
1079 }
|
|
1080 }
|
|
1081 else
|
|
1082 playlist_position = g_list_next(plist_pos_list)->data;
|
|
1083
|
|
1084 PLAYLIST_UNLOCK();
|
|
1085
|
|
1086 playlist_check_pos_current();
|
|
1087 bmp_playback_initiate();
|
|
1088 mainwin_set_info_text();
|
|
1089 playlistwin_update_list();
|
|
1090 }
|
|
1091
|
|
1092 gint
|
|
1093 playlist_get_length(void)
|
|
1094 {
|
|
1095 gint retval;
|
|
1096
|
|
1097 PLAYLIST_LOCK();
|
|
1098 retval = playlist_get_length_nolock();
|
|
1099 PLAYLIST_UNLOCK();
|
|
1100
|
|
1101 return retval;
|
|
1102 }
|
|
1103
|
|
1104 gint
|
|
1105 playlist_queue_get_length(void)
|
|
1106 {
|
|
1107 gint length;
|
|
1108
|
|
1109 PLAYLIST_LOCK();
|
|
1110 length = g_list_length(queued_list);
|
|
1111 PLAYLIST_UNLOCK();
|
|
1112
|
|
1113 return length;
|
|
1114 }
|
|
1115
|
|
1116 gint
|
|
1117 playlist_get_length_nolock(void)
|
|
1118 {
|
|
1119 REQUIRE_STATIC_LOCK(playlist);
|
|
1120 return g_list_length(playlist);
|
|
1121 }
|
|
1122
|
|
1123 gchar *
|
|
1124 playlist_get_info_text(void)
|
|
1125 {
|
|
1126 gchar *text, *title, *numbers, *length;
|
|
1127
|
|
1128 PLAYLIST_LOCK();
|
|
1129 if (!playlist_position) {
|
|
1130 PLAYLIST_UNLOCK();
|
|
1131 return NULL;
|
|
1132 }
|
|
1133
|
|
1134 /* FIXME: there should not be a need to do additional conversion,
|
|
1135 * if playlist is properly maintained */
|
|
1136 if (playlist_position->title) {
|
|
1137 title = str_to_utf8(playlist_position->title);
|
|
1138 }
|
|
1139 else {
|
|
1140 gchar *basename = g_path_get_basename(playlist_position->filename);
|
|
1141 title = filename_to_utf8(basename);
|
|
1142 g_free(basename);
|
|
1143 }
|
|
1144
|
|
1145 /*
|
|
1146 * If the user don't want numbers in the playlist, don't
|
|
1147 * display them in other parts of XMMS
|
|
1148 */
|
|
1149
|
|
1150 if (cfg.show_numbers_in_pl)
|
|
1151 numbers = g_strdup_printf("%d. ", playlist_get_position_nolock() + 1);
|
|
1152 else
|
|
1153 numbers = g_strdup("");
|
|
1154
|
|
1155 if (playlist_position->length != -1)
|
|
1156 length = g_strdup_printf(" (%d:%-2.2d)",
|
|
1157 playlist_position->length / 60000,
|
|
1158 (playlist_position->length / 1000) % 60);
|
|
1159 else
|
|
1160 length = g_strdup("");
|
|
1161
|
|
1162 PLAYLIST_UNLOCK();
|
|
1163
|
|
1164 text = convert_title_text(g_strconcat(numbers, title, length, NULL));
|
|
1165
|
|
1166 g_free(numbers);
|
|
1167 g_free(title);
|
|
1168 g_free(length);
|
|
1169
|
|
1170 return text;
|
|
1171 }
|
|
1172
|
|
1173 gint
|
|
1174 playlist_get_current_length(void)
|
|
1175 {
|
|
1176 gint len = 0;
|
|
1177
|
|
1178 PLAYLIST_LOCK();
|
|
1179 if (playlist && playlist_position)
|
|
1180 len = playlist_position->length;
|
|
1181 PLAYLIST_UNLOCK();
|
|
1182
|
|
1183 return len;
|
|
1184 }
|
|
1185
|
|
1186 static void
|
|
1187 playlist_save_m3u(FILE * file)
|
|
1188 {
|
|
1189 GList *node;
|
|
1190
|
|
1191 g_return_if_fail(file != NULL);
|
|
1192
|
|
1193 if (cfg.use_pl_metadata)
|
|
1194 g_fprintf(file, "#EXTM3U\n");
|
|
1195
|
|
1196 PLAYLIST_LOCK();
|
|
1197
|
|
1198 for (node = playlist; node; node = g_list_next(node)) {
|
|
1199 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
|
|
1200
|
|
1201 if (entry->title && cfg.use_pl_metadata) {
|
|
1202 gint seconds;
|
|
1203
|
|
1204 if (entry->length > 0)
|
|
1205 seconds = (entry->length) / 1000;
|
|
1206 else
|
|
1207 seconds = -1;
|
|
1208
|
|
1209 g_fprintf(file, "#EXTINF:%d,%s\n", seconds, entry->title);
|
|
1210 }
|
|
1211
|
|
1212 g_fprintf(file, "%s\n", entry->filename);
|
|
1213 }
|
|
1214
|
|
1215 PLAYLIST_UNLOCK();
|
|
1216 }
|
|
1217
|
|
1218 static void
|
|
1219 playlist_save_pls(FILE * file)
|
|
1220 {
|
|
1221 GList *node;
|
|
1222
|
|
1223 g_return_if_fail(file != NULL);
|
|
1224
|
|
1225 g_fprintf(file, "[playlist]\n");
|
|
1226 g_fprintf(file, "NumberOfEntries=%d\n", playlist_get_length());
|
|
1227
|
|
1228 PLAYLIST_LOCK();
|
|
1229
|
|
1230 for (node = playlist; node; node = g_list_next(node)) {
|
|
1231 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
|
|
1232
|
|
1233 g_fprintf(file, "File%d=%s\n", g_list_position(playlist, node) + 1,
|
|
1234 entry->filename);
|
|
1235 }
|
|
1236
|
|
1237 PLAYLIST_UNLOCK();
|
|
1238 }
|
|
1239
|
|
1240 gboolean
|
|
1241 playlist_save(const gchar * filename,
|
|
1242 PlaylistFormat format)
|
|
1243 {
|
|
1244 FILE *file;
|
|
1245
|
|
1246 g_return_val_if_fail(filename != NULL, FALSE);
|
|
1247
|
|
1248 playlist_set_current_name(filename);
|
|
1249
|
|
1250 if ((file = fopen(filename, "w")) == NULL)
|
|
1251 return FALSE;
|
|
1252
|
|
1253 playlist_save_func_table[format](file);
|
|
1254
|
|
1255 return (fclose(file) == 0);
|
|
1256 }
|
|
1257
|
|
1258 gboolean
|
|
1259 playlist_load(const gchar * filename)
|
|
1260 {
|
|
1261 return playlist_load_ins(filename, -1);
|
|
1262 }
|
|
1263
|
|
1264
|
|
1265 static void
|
|
1266 playlist_load_ins_file(const gchar * filename_p,
|
|
1267 const gchar * playlist_name, gint pos,
|
|
1268 const gchar * title, gint len)
|
|
1269 {
|
|
1270 gchar *filename;
|
|
1271 gchar *tmp, *path;
|
|
1272
|
|
1273 g_return_if_fail(filename_p != NULL);
|
|
1274 g_return_if_fail(playlist_name != NULL);
|
|
1275
|
|
1276 filename = g_strstrip(g_strdup(filename_p));
|
|
1277
|
|
1278 if (cfg.use_backslash_as_dir_delimiter) {
|
|
1279 while ((tmp = strchr(filename, '\\')) != NULL)
|
|
1280 *tmp = '/';
|
|
1281 }
|
|
1282
|
|
1283 if (filename[0] != '/' && !strstr(filename, "://")) {
|
|
1284 path = g_strdup(playlist_name);
|
|
1285 if ((tmp = strrchr(path, '/')))
|
|
1286 *tmp = '\0';
|
|
1287 else {
|
|
1288 __playlist_ins_with_info(filename, pos, title, len);
|
|
1289 return;
|
|
1290 }
|
|
1291 tmp = g_build_filename(path, filename, NULL);
|
|
1292 __playlist_ins_with_info(tmp, pos, title, len);
|
|
1293 g_free(tmp);
|
|
1294 g_free(path);
|
|
1295 }
|
|
1296 else
|
|
1297 __playlist_ins_with_info(filename, pos, title, len);
|
|
1298
|
|
1299 g_free(filename);
|
|
1300 }
|
|
1301
|
|
1302 static void
|
|
1303 parse_extm3u_info(const gchar * info, gchar ** title, gint * length)
|
|
1304 {
|
|
1305 gchar *str;
|
|
1306
|
|
1307 g_return_if_fail(info != NULL);
|
|
1308 g_return_if_fail(title != NULL);
|
|
1309 g_return_if_fail(length != NULL);
|
|
1310
|
|
1311 *title = NULL;
|
|
1312 *length = -1;
|
|
1313
|
|
1314 if (!str_has_prefix_nocase(info, "#EXTINF:")) {
|
|
1315 g_message("Invalid m3u metadata (%s)", info);
|
|
1316 return;
|
|
1317 }
|
|
1318
|
|
1319 info += 8;
|
|
1320
|
|
1321 *length = atoi(info);
|
|
1322 if (*length <= 0)
|
|
1323 *length = -1;
|
|
1324 else
|
|
1325 *length *= 1000;
|
|
1326
|
|
1327 if ((str = strchr(info, ','))) {
|
|
1328 *title = g_strstrip(g_strdup(str + 1));
|
|
1329 if (strlen(*title) < 1) {
|
|
1330 g_free(*title);
|
|
1331 *title = NULL;
|
|
1332 }
|
|
1333 }
|
|
1334 }
|
|
1335
|
|
1336 static guint
|
|
1337 playlist_load_pls(const gchar * filename, gint pos)
|
|
1338 {
|
|
1339 guint i, count, added_count = 0;
|
|
1340 gchar key[10];
|
|
1341 gchar *line;
|
|
1342
|
|
1343 g_return_val_if_fail(filename != NULL, 0);
|
|
1344
|
|
1345 if (!str_has_suffix_nocase(filename, ".pls"))
|
|
1346 return 0;
|
|
1347
|
|
1348 if (!(line = read_ini_string(filename, "playlist", "NumberOfEntries")))
|
|
1349 return 0;
|
|
1350
|
|
1351 count = atoi(line);
|
|
1352 g_free(line);
|
|
1353
|
|
1354 for (i = 1; i <= count; i++) {
|
|
1355 g_snprintf(key, sizeof(key), "File%d", i);
|
|
1356 if ((line = read_ini_string(filename, "playlist", key))) {
|
|
1357 playlist_load_ins_file(line, filename, pos, NULL, -1);
|
|
1358 added_count++;
|
|
1359
|
|
1360 if (pos >= 0)
|
|
1361 pos++;
|
|
1362
|
|
1363 g_free(line);
|
|
1364 }
|
|
1365 }
|
|
1366
|
|
1367 playlist_generate_shuffle_list();
|
|
1368 playlistwin_update_list();
|
|
1369
|
|
1370 return added_count;
|
|
1371 }
|
|
1372
|
|
1373 static guint
|
|
1374 playlist_load_m3u(const gchar * filename, gint pos)
|
|
1375 {
|
|
1376 FILE *file;
|
|
1377 gchar *line;
|
|
1378 gchar *ext_info = NULL, *ext_title = NULL;
|
|
1379 gsize line_len = 1024;
|
|
1380 gint ext_len = -1;
|
|
1381 gboolean is_extm3u = FALSE;
|
|
1382 guint added_count = 0;
|
|
1383
|
|
1384 if (!(file = fopen(filename, "r")))
|
|
1385 return 0;
|
|
1386
|
|
1387 line = g_malloc(line_len);
|
|
1388 while (fgets(line, line_len, file)) {
|
|
1389 while (strlen(line) == line_len - 1 && line[strlen(line) - 1] != '\n') {
|
|
1390 line_len += 1024;
|
|
1391 line = g_realloc(line, line_len);
|
|
1392 fgets(&line[strlen(line)], 1024, file);
|
|
1393 }
|
|
1394
|
|
1395 while (line[strlen(line) - 1] == '\r' ||
|
|
1396 line[strlen(line) - 1] == '\n')
|
|
1397 line[strlen(line) - 1] = '\0';
|
|
1398
|
|
1399 if (str_has_prefix_nocase(line, "#EXTM3U")) {
|
|
1400 is_extm3u = TRUE;
|
|
1401 continue;
|
|
1402 }
|
|
1403
|
|
1404 if (is_extm3u && str_has_prefix_nocase(line, "#EXTINF:")) {
|
|
1405 str_replace_in(&ext_info, g_strdup(line));
|
|
1406 continue;
|
|
1407 }
|
|
1408
|
|
1409 if (line[0] == '#' || strlen(line) == 0) {
|
|
1410 if (ext_info) {
|
|
1411 g_free(ext_info);
|
|
1412 ext_info = NULL;
|
|
1413 }
|
|
1414 continue;
|
|
1415 }
|
|
1416
|
|
1417 if (is_extm3u) {
|
|
1418 if (cfg.use_pl_metadata && ext_info)
|
|
1419 parse_extm3u_info(ext_info, &ext_title, &ext_len);
|
|
1420 g_free(ext_info);
|
|
1421 ext_info = NULL;
|
|
1422 }
|
|
1423
|
|
1424 playlist_load_ins_file(line, filename, pos, ext_title, ext_len);
|
|
1425
|
|
1426 str_replace_in(&ext_title, NULL);
|
|
1427 ext_len = -1;
|
|
1428
|
|
1429 added_count++;
|
|
1430 if (pos >= 0)
|
|
1431 pos++;
|
|
1432 }
|
|
1433
|
|
1434 fclose(file);
|
|
1435 g_free(line);
|
|
1436
|
|
1437 playlist_generate_shuffle_list();
|
|
1438 playlistwin_update_list();
|
|
1439
|
|
1440 if (g_ascii_strcasecmp(filename, BMP_PLAYLIST_BASENAME))
|
|
1441 playlist_set_current_name(NULL);
|
|
1442 else
|
|
1443 playlist_set_current_name(filename);
|
|
1444
|
|
1445 return added_count;
|
|
1446 }
|
|
1447
|
|
1448 static guint
|
|
1449 playlist_load_ins(const gchar * filename, gint pos)
|
|
1450 {
|
|
1451 guint added_count;
|
|
1452
|
|
1453 g_return_val_if_fail(filename != NULL, 0);
|
|
1454
|
|
1455 /* .pls ? */
|
|
1456 if ((added_count = playlist_load_pls(filename, pos)) > 0)
|
|
1457 return added_count;
|
|
1458
|
|
1459 /* Assume .m3u */
|
|
1460 return playlist_load_m3u(filename, pos);
|
|
1461 }
|
|
1462
|
|
1463 GList *
|
|
1464 get_playlist_nth(guint nth)
|
|
1465 {
|
|
1466 REQUIRE_STATIC_LOCK(playlist);
|
|
1467 return g_list_nth(playlist, nth);
|
|
1468 }
|
|
1469
|
|
1470
|
|
1471 GList *
|
|
1472 playlist_get(void)
|
|
1473 {
|
|
1474 REQUIRE_STATIC_LOCK(playlist);
|
|
1475 return playlist;
|
|
1476 }
|
|
1477
|
|
1478 gint
|
|
1479 playlist_get_position_nolock(void)
|
|
1480 {
|
|
1481 REQUIRE_STATIC_LOCK(playlist);
|
|
1482
|
|
1483 if (playlist && playlist_position)
|
|
1484 return g_list_index(playlist, playlist_position);
|
|
1485 return 0;
|
|
1486 }
|
|
1487
|
|
1488 gint
|
|
1489 playlist_get_position(void)
|
|
1490 {
|
|
1491 gint pos;
|
|
1492
|
|
1493 PLAYLIST_LOCK();
|
|
1494 pos = playlist_get_position_nolock();
|
|
1495 PLAYLIST_UNLOCK();
|
|
1496
|
|
1497 return pos;
|
|
1498 }
|
|
1499
|
|
1500 gchar *
|
|
1501 playlist_get_filename(guint pos)
|
|
1502 {
|
|
1503 gchar *filename;
|
|
1504 PlaylistEntry *entry;
|
|
1505 GList *node;
|
|
1506
|
|
1507 PLAYLIST_LOCK();
|
|
1508 if (!playlist) {
|
|
1509 PLAYLIST_UNLOCK();
|
|
1510 return NULL;
|
|
1511 }
|
|
1512 node = g_list_nth(playlist, pos);
|
|
1513 if (!node) {
|
|
1514 PLAYLIST_UNLOCK();
|
|
1515 return NULL;
|
|
1516 }
|
|
1517 entry = node->data;
|
|
1518
|
|
1519 filename = g_strdup(entry->filename);
|
|
1520 PLAYLIST_UNLOCK();
|
|
1521
|
|
1522 return filename;
|
|
1523 }
|
|
1524
|
|
1525 gchar *
|
|
1526 playlist_get_songtitle(guint pos)
|
|
1527 {
|
|
1528 gchar *title = NULL;
|
|
1529 PlaylistEntry *entry;
|
|
1530 GList *node;
|
|
1531
|
|
1532 PLAYLIST_LOCK();
|
|
1533
|
|
1534 if (!playlist) {
|
|
1535 PLAYLIST_UNLOCK();
|
|
1536 return NULL;
|
|
1537 }
|
|
1538
|
|
1539 if (!(node = g_list_nth(playlist, pos))) {
|
|
1540 PLAYLIST_UNLOCK();
|
|
1541 return NULL;
|
|
1542 }
|
|
1543
|
|
1544 entry = node->data;
|
|
1545
|
|
1546 /* FIXME: simplify this logic */
|
|
1547 if (!entry->title && entry->length == -1) {
|
|
1548 if (playlist_entry_get_info(entry))
|
|
1549 title = entry->title;
|
|
1550 }
|
|
1551 else {
|
|
1552 title = entry->title;
|
|
1553 }
|
|
1554
|
|
1555 PLAYLIST_UNLOCK();
|
|
1556
|
|
1557 if (!title) {
|
|
1558 title = g_path_get_basename(entry->filename);
|
|
1559 return str_replace(title, filename_to_utf8(title));
|
|
1560 }
|
|
1561
|
|
1562 return str_to_utf8(title);
|
|
1563 }
|
|
1564
|
|
1565 gint
|
|
1566 playlist_get_songtime(guint pos)
|
|
1567 {
|
|
1568 gint song_time = -1;
|
|
1569 PlaylistEntry *entry;
|
|
1570 GList *node;
|
|
1571
|
|
1572 PLAYLIST_LOCK();
|
|
1573
|
|
1574 if (!playlist) {
|
|
1575 PLAYLIST_UNLOCK();
|
|
1576 return -1;
|
|
1577 }
|
|
1578
|
|
1579 if (!(node = g_list_nth(playlist, pos))) {
|
|
1580 PLAYLIST_UNLOCK();
|
|
1581 return -1;
|
|
1582 }
|
|
1583
|
|
1584 entry = node->data;
|
|
1585
|
|
1586 if (!entry->title && entry->length == -1) {
|
|
1587 if (playlist_entry_get_info(entry))
|
|
1588 song_time = entry->length;
|
|
1589
|
|
1590 PLAYLIST_UNLOCK();
|
|
1591 }
|
|
1592 else {
|
|
1593 song_time = entry->length;
|
|
1594 PLAYLIST_UNLOCK();
|
|
1595 }
|
|
1596
|
|
1597 return song_time;
|
|
1598 }
|
|
1599
|
|
1600 static gint
|
|
1601 playlist_compare_title(const PlaylistEntry * a,
|
|
1602 const PlaylistEntry * b)
|
|
1603 {
|
|
1604 const gchar *a_title, *b_title;
|
|
1605
|
|
1606 g_return_val_if_fail(a != NULL, 0);
|
|
1607 g_return_val_if_fail(b != NULL, 0);
|
|
1608
|
|
1609 if (a->title)
|
|
1610 a_title = a->title;
|
|
1611 else {
|
|
1612 if (strrchr(a->filename, '/'))
|
|
1613 a_title = strrchr(a->filename, '/') + 1;
|
|
1614 else
|
|
1615 a_title = a->filename;
|
|
1616 }
|
|
1617
|
|
1618 if (b->title)
|
|
1619 b_title = b->title;
|
|
1620 else {
|
|
1621 if (strrchr(a->filename, '/'))
|
|
1622 b_title = strrchr(b->filename, '/') + 1;
|
|
1623 else
|
|
1624 b_title = b->filename;
|
|
1625 }
|
|
1626
|
|
1627 return strcasecmp(a_title, b_title);
|
|
1628 }
|
|
1629
|
|
1630 static gint
|
|
1631 playlist_compare_filename(const PlaylistEntry * a,
|
|
1632 const PlaylistEntry * b)
|
|
1633 {
|
|
1634 gchar *a_filename, *b_filename;
|
|
1635
|
|
1636 g_return_val_if_fail(a != NULL, 0);
|
|
1637 g_return_val_if_fail(b != NULL, 0);
|
|
1638
|
|
1639 if (strrchr(a->filename, '/'))
|
|
1640 a_filename = strrchr(a->filename, '/') + 1;
|
|
1641 else
|
|
1642 a_filename = a->filename;
|
|
1643
|
|
1644 if (strrchr(b->filename, '/'))
|
|
1645 b_filename = strrchr(b->filename, '/') + 1;
|
|
1646 else
|
|
1647 b_filename = b->filename;
|
|
1648
|
|
1649
|
|
1650 return strcasecmp(a_filename, b_filename);
|
|
1651 }
|
|
1652
|
|
1653 static gint
|
|
1654 path_compare(const gchar * a, const gchar * b)
|
|
1655 {
|
|
1656 gchar *posa, *posb;
|
|
1657 gint len, ret;
|
|
1658
|
|
1659 posa = strrchr(a, '/');
|
|
1660 posb = strrchr(b, '/');
|
|
1661
|
|
1662 /*
|
|
1663 * Sort directories before files
|
|
1664 */
|
|
1665 if (posa && posb && (posa - a != posb - b)) {
|
|
1666 if (posa - a > posb - b) {
|
|
1667 len = posb - b;
|
|
1668 ret = -1;
|
|
1669 }
|
|
1670 else {
|
|
1671 len = posa - a;
|
|
1672 ret = 1;
|
|
1673 }
|
|
1674 if (!strncasecmp(a, b, len))
|
|
1675 return ret;
|
|
1676 }
|
|
1677 return strcasecmp(a, b);
|
|
1678 }
|
|
1679
|
|
1680 static gint
|
|
1681 playlist_compare_path(const PlaylistEntry * a,
|
|
1682 const PlaylistEntry * b)
|
|
1683 {
|
|
1684 return path_compare(a->filename, b->filename);
|
|
1685 }
|
|
1686
|
|
1687 static gint
|
|
1688 playlist_compare_date(const PlaylistEntry * a,
|
|
1689 const PlaylistEntry * b)
|
|
1690 {
|
|
1691 struct stat buf;
|
|
1692 time_t modtime;
|
|
1693
|
|
1694 gint rv;
|
|
1695
|
|
1696
|
|
1697 rv = stat(a->filename, &buf);
|
|
1698
|
|
1699 if (rv == 0) {
|
|
1700 modtime = buf.st_mtime;
|
|
1701 rv = stat(b->filename, &buf);
|
|
1702
|
|
1703 if (stat(b->filename, &buf) == 0) {
|
|
1704 if (buf.st_mtime == modtime)
|
|
1705 return 0;
|
|
1706 else
|
|
1707 return (buf.st_mtime - modtime) > 0 ? -1 : 1;
|
|
1708 }
|
|
1709 else
|
|
1710 return -1;
|
|
1711 }
|
|
1712 else if (!lstat(b->filename, &buf))
|
|
1713 return 1;
|
|
1714 else
|
|
1715 return playlist_compare_filename(a, b);
|
|
1716 }
|
|
1717
|
|
1718
|
|
1719 void
|
|
1720 playlist_sort(PlaylistSortType type)
|
|
1721 {
|
|
1722 playlist_remove_dead_files();
|
|
1723 PLAYLIST_LOCK();
|
|
1724 playlist =
|
|
1725 g_list_sort(playlist,
|
|
1726 (GCompareFunc) playlist_compare_func_table[type]);
|
|
1727 PLAYLIST_UNLOCK();
|
|
1728 }
|
|
1729
|
|
1730 static GList *
|
|
1731 playlist_sort_selected_generic(GList * list, GCompareFunc cmpfunc)
|
|
1732 {
|
|
1733 GList *list1, *list2;
|
|
1734 GList *tmp_list = NULL;
|
|
1735 GList *index_list = NULL;
|
|
1736
|
|
1737 /*
|
|
1738 * We take all the selected entries out of the playlist,
|
|
1739 * sorts them, and then put them back in again.
|
|
1740 */
|
|
1741
|
|
1742 list1 = g_list_last(list);
|
|
1743
|
|
1744 while (list1) {
|
|
1745 list2 = g_list_previous(list1);
|
|
1746 if (PLAYLIST_ENTRY(list1->data)->selected) {
|
|
1747 gpointer idx;
|
|
1748 idx = GINT_TO_POINTER(g_list_position(list, list1));
|
|
1749 index_list = g_list_prepend(index_list, idx);
|
|
1750 list = g_list_remove_link(list, list1);
|
|
1751 tmp_list = g_list_concat(list1, tmp_list);
|
|
1752 }
|
|
1753 list1 = list2;
|
|
1754 }
|
|
1755
|
|
1756 tmp_list = g_list_sort(tmp_list, cmpfunc);
|
|
1757 list1 = tmp_list;
|
|
1758 list2 = index_list;
|
|
1759
|
|
1760 while (list2) {
|
|
1761 if (!list1) {
|
|
1762 g_critical(G_STRLOC ": Error during list sorting. "
|
|
1763 "Possibly dropped some playlist-entries.");
|
|
1764 break;
|
|
1765 }
|
|
1766
|
|
1767 list = g_list_insert(list, list1->data, GPOINTER_TO_INT(list2->data));
|
|
1768
|
|
1769 list2 = g_list_next(list2);
|
|
1770 list1 = g_list_next(list1);
|
|
1771 }
|
|
1772
|
|
1773 g_list_free(index_list);
|
|
1774 g_list_free(tmp_list);
|
|
1775
|
|
1776 return list;
|
|
1777 }
|
|
1778
|
|
1779 void
|
|
1780 playlist_sort_selected(PlaylistSortType type)
|
|
1781 {
|
|
1782 PLAYLIST_LOCK();
|
|
1783 playlist = playlist_sort_selected_generic(playlist, (GCompareFunc)
|
|
1784 playlist_compare_func_table
|
|
1785 [type]);
|
|
1786 PLAYLIST_UNLOCK();
|
|
1787 }
|
|
1788
|
|
1789 void
|
|
1790 playlist_reverse(void)
|
|
1791 {
|
|
1792 PLAYLIST_LOCK();
|
|
1793 playlist = g_list_reverse(playlist);
|
|
1794 PLAYLIST_UNLOCK();
|
|
1795 }
|
|
1796
|
|
1797 static GList *
|
|
1798 playlist_shuffle_list(GList * list)
|
|
1799 {
|
|
1800 /*
|
|
1801 * Note that this doesn't make a copy of the original list.
|
|
1802 * The pointer to the original list is not valid after this
|
|
1803 * fuction is run.
|
|
1804 */
|
|
1805 gint len = g_list_length(list);
|
|
1806 gint i, j;
|
|
1807 GList *node, **ptrs;
|
|
1808
|
|
1809 REQUIRE_STATIC_LOCK(playlist);
|
|
1810
|
|
1811 if (!len)
|
|
1812 return NULL;
|
|
1813
|
|
1814 ptrs = g_new(GList *, len);
|
|
1815
|
|
1816 for (node = list, i = 0; i < len; node = g_list_next(node), i++)
|
|
1817 ptrs[i] = node;
|
|
1818
|
|
1819 j = g_random_int_range(0, len);
|
|
1820 list = ptrs[j];
|
|
1821 ptrs[j]->next = NULL;
|
|
1822 ptrs[j] = ptrs[0];
|
|
1823
|
|
1824 for (i = 1; i < len; i++) {
|
|
1825 j = g_random_int_range(0, len - i);
|
|
1826 list->prev = ptrs[i + j];
|
|
1827 ptrs[i + j]->next = list;
|
|
1828 list = ptrs[i + j];
|
|
1829 ptrs[i + j] = ptrs[i];
|
|
1830 }
|
|
1831 list->prev = NULL;
|
|
1832
|
|
1833 g_free(ptrs);
|
|
1834
|
|
1835 return list;
|
|
1836 }
|
|
1837
|
|
1838 void
|
|
1839 playlist_random(void)
|
|
1840 {
|
|
1841 PLAYLIST_LOCK();
|
|
1842 playlist = playlist_shuffle_list(playlist);
|
|
1843 PLAYLIST_UNLOCK();
|
|
1844 }
|
|
1845
|
|
1846 GList *
|
|
1847 playlist_get_selected(void)
|
|
1848 {
|
|
1849 GList *node, *list = NULL;
|
|
1850 gint i = 0;
|
|
1851
|
|
1852 PLAYLIST_LOCK();
|
|
1853 for (node = playlist_get(); node; node = g_list_next(node), i++) {
|
|
1854 PlaylistEntry *entry = node->data;
|
|
1855 if (entry->selected)
|
|
1856 list = g_list_prepend(list, GINT_TO_POINTER(i));
|
|
1857 }
|
|
1858 PLAYLIST_UNLOCK();
|
|
1859 return g_list_reverse(list);
|
|
1860 }
|
|
1861
|
|
1862 void
|
|
1863 playlist_clear_selected(void)
|
|
1864 {
|
|
1865 GList *node = NULL;
|
|
1866 gint i = 0;
|
|
1867
|
|
1868 PLAYLIST_LOCK();
|
|
1869 for (node = playlist_get(); node; node = g_list_next(node), i++) {
|
|
1870 PLAYLIST_ENTRY(node->data)->selected = FALSE;
|
|
1871 }
|
|
1872 PLAYLIST_UNLOCK();
|
|
1873 playlist_recalc_total_time();
|
|
1874 }
|
|
1875
|
|
1876 gint
|
|
1877 playlist_get_num_selected(void)
|
|
1878 {
|
|
1879 GList *node;
|
|
1880 gint num = 0;
|
|
1881
|
|
1882 PLAYLIST_LOCK();
|
|
1883 for (node = playlist_get(); node; node = g_list_next(node)) {
|
|
1884 PlaylistEntry *entry = node->data;
|
|
1885 if (entry->selected)
|
|
1886 num++;
|
|
1887 }
|
|
1888 PLAYLIST_UNLOCK();
|
|
1889 return num;
|
|
1890 }
|
|
1891
|
|
1892
|
|
1893 static void
|
|
1894 playlist_generate_shuffle_list(void)
|
|
1895 {
|
|
1896 PLAYLIST_LOCK();
|
|
1897 playlist_generate_shuffle_list_nolock();
|
|
1898 PLAYLIST_UNLOCK();
|
|
1899 }
|
|
1900
|
|
1901 static void
|
|
1902 playlist_generate_shuffle_list_nolock(void)
|
|
1903 {
|
|
1904 GList *node;
|
|
1905 gint numsongs;
|
|
1906
|
|
1907 REQUIRE_STATIC_LOCK(playlist);
|
|
1908
|
|
1909 if (shuffle_list) {
|
|
1910 g_list_free(shuffle_list);
|
|
1911 shuffle_list = NULL;
|
|
1912 }
|
|
1913
|
|
1914 if (!cfg.shuffle || !playlist)
|
|
1915 return;
|
|
1916
|
|
1917 shuffle_list = playlist_shuffle_list(g_list_copy(playlist));
|
|
1918 numsongs = g_list_length(shuffle_list);
|
|
1919
|
|
1920 if (playlist_position) {
|
|
1921 gint i = g_list_index(shuffle_list, playlist_position);
|
|
1922 node = g_list_nth(shuffle_list, i);
|
|
1923 shuffle_list = g_list_remove_link(shuffle_list, node);
|
|
1924 shuffle_list = g_list_prepend(shuffle_list, node->data);
|
|
1925 }
|
|
1926 }
|
|
1927
|
|
1928 void
|
|
1929 playlist_fileinfo(guint pos)
|
|
1930 {
|
|
1931 gchar *path = NULL;
|
|
1932 GList *node;
|
|
1933
|
|
1934 PLAYLIST_LOCK();
|
|
1935 if ((node = g_list_nth(playlist_get(), pos))) {
|
|
1936 PlaylistEntry *entry = node->data;
|
|
1937 path = g_strdup(entry->filename);
|
|
1938 }
|
|
1939 PLAYLIST_UNLOCK();
|
|
1940 if (path) {
|
|
1941 input_file_info_box(path);
|
|
1942 g_free(path);
|
|
1943 }
|
|
1944 }
|
|
1945
|
|
1946 void
|
|
1947 playlist_fileinfo_current(void)
|
|
1948 {
|
|
1949 gchar *path = NULL;
|
|
1950
|
|
1951 PLAYLIST_LOCK();
|
|
1952 if (playlist_get() && playlist_position)
|
|
1953 path = g_strdup(playlist_position->filename);
|
|
1954 PLAYLIST_UNLOCK();
|
|
1955
|
|
1956 if (path) {
|
|
1957 input_file_info_box(path);
|
|
1958 g_free(path);
|
|
1959 }
|
|
1960 }
|
|
1961
|
|
1962
|
|
1963 static gboolean
|
|
1964 playlist_get_info_is_going(void)
|
|
1965 {
|
|
1966 gboolean result;
|
|
1967
|
|
1968 G_LOCK(playlist_get_info_going);
|
|
1969 result = playlist_get_info_going;
|
|
1970 G_UNLOCK(playlist_get_info_going);
|
|
1971
|
|
1972 return result;
|
|
1973 }
|
|
1974
|
|
1975 static gpointer
|
|
1976 playlist_get_info_func(gpointer arg)
|
|
1977 {
|
|
1978 GList *node;
|
|
1979 gboolean update_playlistwin = FALSE;
|
|
1980 gboolean update_mainwin = FALSE;
|
|
1981
|
|
1982 while (playlist_get_info_is_going()) {
|
|
1983 PlaylistEntry *entry;
|
|
1984
|
|
1985 if (cfg.use_pl_metadata &&
|
|
1986 cfg.get_info_on_load &&
|
|
1987 playlist_get_info_scan_active) {
|
|
1988
|
|
1989 PLAYLIST_LOCK();
|
|
1990 for (node = playlist_get(); node; node = g_list_next(node)) {
|
|
1991 entry = node->data;
|
|
1992
|
|
1993 if (entry->title || entry->length != -1)
|
|
1994 continue;
|
|
1995
|
|
1996 if (!playlist_entry_get_info(entry)) {
|
|
1997 if (g_list_index(playlist_get(), entry) == -1)
|
|
1998 /* Entry disappeared while we looked it up.
|
|
1999 Restart. */
|
|
2000 node = playlist_get();
|
|
2001 }
|
|
2002 else if (entry->title || entry->length != -1) {
|
|
2003 update_playlistwin = TRUE;
|
|
2004 if (entry == playlist_position)
|
|
2005 update_mainwin = TRUE;
|
|
2006 break;
|
|
2007 }
|
|
2008 }
|
|
2009 PLAYLIST_UNLOCK();
|
|
2010
|
|
2011 if (!node)
|
|
2012 playlist_get_info_scan_active = FALSE;
|
|
2013 }
|
|
2014 else if (!cfg.get_info_on_load &&
|
|
2015 cfg.get_info_on_demand &&
|
|
2016 cfg.playlist_visible &&
|
|
2017 !cfg.playlist_shaded &&
|
|
2018 cfg.use_pl_metadata) {
|
|
2019
|
|
2020 gboolean found = FALSE;
|
|
2021
|
|
2022 PLAYLIST_LOCK();
|
|
2023
|
|
2024 if (!playlist_get()) {
|
|
2025 PLAYLIST_UNLOCK();
|
|
2026 g_usleep(1000000);
|
|
2027 continue;
|
|
2028 }
|
|
2029
|
|
2030 for (node =
|
|
2031 g_list_nth(playlist_get(), playlistwin_get_toprow());
|
|
2032 node
|
|
2033 &&
|
|
2034 playlistwin_item_visible(g_list_position
|
|
2035 (playlist_get(), node));
|
|
2036 node = g_list_next(node)) {
|
|
2037 entry = node->data;
|
|
2038 if (entry->title || entry->length != -1)
|
|
2039 continue;
|
|
2040
|
|
2041 if (!playlist_entry_get_info(entry)) {
|
|
2042 if (g_list_index(playlist_get(), entry) == -1)
|
|
2043 /* Entry disapeared while we
|
|
2044 looked it up. Restart. */
|
|
2045 node =
|
|
2046 g_list_nth(playlist_get(),
|
|
2047 playlistwin_get_toprow());
|
|
2048 }
|
|
2049 else if (entry->title || entry->length != -1) {
|
|
2050 update_playlistwin = TRUE;
|
|
2051 if (entry == playlist_position)
|
|
2052 update_mainwin = TRUE;
|
|
2053 found = TRUE;
|
|
2054 break;
|
|
2055 }
|
|
2056 }
|
|
2057
|
|
2058 PLAYLIST_UNLOCK();
|
|
2059
|
|
2060 if (!found) {
|
|
2061 g_usleep(500000);
|
|
2062 continue;
|
|
2063 }
|
|
2064 }
|
|
2065 else
|
|
2066 g_usleep(500000);
|
|
2067
|
|
2068 if (update_playlistwin) {
|
|
2069 playlistwin_update_list();
|
|
2070 update_playlistwin = FALSE;
|
|
2071 }
|
|
2072
|
|
2073 if (update_mainwin) {
|
|
2074 mainwin_set_info_text();
|
|
2075 update_mainwin = FALSE;
|
|
2076 }
|
|
2077 }
|
|
2078
|
|
2079 g_thread_exit(NULL);
|
|
2080 return NULL;
|
|
2081 }
|
|
2082
|
|
2083 void
|
|
2084 playlist_start_get_info_thread(void)
|
|
2085 {
|
|
2086 playlist_get_info_going = TRUE;
|
|
2087 playlist_get_info_thread = g_thread_create(playlist_get_info_func,
|
|
2088 NULL, TRUE, NULL);
|
|
2089 }
|
|
2090
|
|
2091 void
|
|
2092 playlist_stop_get_info_thread(void)
|
|
2093 {
|
|
2094 G_LOCK(playlist_get_info_going);
|
|
2095 playlist_get_info_going = FALSE;
|
|
2096 G_UNLOCK(playlist_get_info_going);
|
|
2097 g_thread_join(playlist_get_info_thread);
|
|
2098 }
|
|
2099
|
|
2100 void
|
|
2101 playlist_start_get_info_scan(void)
|
|
2102 {
|
|
2103 playlist_get_info_scan_active = TRUE;
|
|
2104 }
|
|
2105
|
|
2106 void
|
|
2107 playlist_remove_dead_files(void)
|
|
2108 {
|
|
2109 GList *node, *next_node;
|
|
2110
|
|
2111 PLAYLIST_LOCK();
|
|
2112
|
|
2113 for (node = playlist; node; node = next_node) {
|
|
2114 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
|
|
2115 next_node = g_list_next(node);
|
|
2116
|
|
2117 if (!entry || !entry->filename) {
|
|
2118 g_message(G_STRLOC ": Playlist entry is invalid!");
|
|
2119 continue;
|
|
2120 }
|
|
2121
|
|
2122 /* FIXME: What about 'file:///'? */
|
|
2123 /* Don't kill URLs */
|
|
2124 if (strstr(entry->filename, "://"))
|
|
2125 continue;
|
|
2126
|
|
2127 /* FIXME: Should test for readability */
|
|
2128 if (vfs_file_test(entry->filename, G_FILE_TEST_EXISTS))
|
|
2129 continue;
|
|
2130
|
|
2131 if (entry == playlist_position) {
|
|
2132 /* Don't remove the currently playing song */
|
|
2133 if (bmp_playback_get_playing())
|
|
2134 continue;
|
|
2135
|
|
2136 if (next_node)
|
|
2137 playlist_position = PLAYLIST_ENTRY(next_node->data);
|
|
2138 else
|
|
2139 playlist_position = NULL;
|
|
2140 }
|
|
2141
|
|
2142 playlist_entry_free(entry);
|
|
2143 playlist = g_list_delete_link(playlist, node);
|
|
2144 }
|
|
2145
|
|
2146 PLAYLIST_UNLOCK();
|
|
2147
|
|
2148 playlist_generate_shuffle_list();
|
|
2149 playlistwin_update_list();
|
|
2150 playlist_recalc_total_time();
|
|
2151 }
|
|
2152
|
|
2153 static gulong pl_total_time = 0, pl_selection_time = 0;
|
|
2154 static gboolean pl_total_more = FALSE, pl_selection_more = FALSE;
|
|
2155
|
|
2156 void
|
|
2157 playlist_get_total_time(gulong * total_time,
|
|
2158 gulong * selection_time,
|
|
2159 gboolean * total_more,
|
|
2160 gboolean * selection_more)
|
|
2161 {
|
|
2162 PLAYLIST_LOCK();
|
|
2163 *total_time = pl_total_time;
|
|
2164 *selection_time = pl_selection_time;
|
|
2165 *total_more = pl_total_more;
|
|
2166 *selection_more = pl_selection_more;
|
|
2167 PLAYLIST_UNLOCK();
|
|
2168 }
|
|
2169
|
|
2170
|
|
2171 static void
|
|
2172 playlist_recalc_total_time_nolock(void)
|
|
2173 {
|
|
2174 GList *list;
|
|
2175 PlaylistEntry *entry;
|
|
2176
|
|
2177 REQUIRE_STATIC_LOCK(playlist);
|
|
2178
|
|
2179 pl_total_time = 0;
|
|
2180 pl_selection_time = 0;
|
|
2181 pl_total_more = FALSE;
|
|
2182 pl_selection_more = FALSE;
|
|
2183
|
|
2184 for (list = playlist_get(); list; list = g_list_next(list)) {
|
|
2185 entry = list->data;
|
|
2186
|
|
2187 if (entry->length != -1)
|
|
2188 pl_total_time += entry->length / 1000;
|
|
2189 else
|
|
2190 pl_total_more = TRUE;
|
|
2191
|
|
2192 if (entry->selected) {
|
|
2193 if (entry->length != -1)
|
|
2194 pl_selection_time += entry->length / 1000;
|
|
2195 else
|
|
2196 pl_selection_more = TRUE;
|
|
2197 }
|
|
2198 }
|
|
2199 }
|
|
2200
|
|
2201 static void
|
|
2202 playlist_recalc_total_time(void)
|
|
2203 {
|
|
2204 PLAYLIST_LOCK();
|
|
2205 playlist_recalc_total_time_nolock();
|
|
2206 PLAYLIST_UNLOCK();
|
|
2207 }
|
|
2208
|
|
2209
|
|
2210 void
|
|
2211 playlist_select_all(gboolean set)
|
|
2212 {
|
|
2213 GList *list;
|
|
2214
|
|
2215 PLAYLIST_LOCK();
|
|
2216
|
|
2217 for (list = playlist_get(); list; list = g_list_next(list)) {
|
|
2218 PlaylistEntry *entry = list->data;
|
|
2219 entry->selected = set;
|
|
2220 }
|
|
2221
|
|
2222 PLAYLIST_UNLOCK();
|
|
2223 playlist_recalc_total_time();
|
|
2224 }
|
|
2225
|
|
2226 void
|
|
2227 playlist_select_invert_all(void)
|
|
2228 {
|
|
2229 GList *list;
|
|
2230
|
|
2231 PLAYLIST_LOCK();
|
|
2232
|
|
2233 for (list = playlist_get(); list; list = g_list_next(list)) {
|
|
2234 PlaylistEntry *entry = list->data;
|
|
2235 entry->selected = !entry->selected;
|
|
2236 }
|
|
2237
|
|
2238 PLAYLIST_UNLOCK();
|
|
2239 playlist_recalc_total_time();
|
|
2240 }
|
|
2241
|
|
2242 gboolean
|
|
2243 playlist_select_invert(guint pos)
|
|
2244 {
|
|
2245 GList *list;
|
|
2246 gboolean invert_ok = FALSE;
|
|
2247
|
|
2248 PLAYLIST_LOCK();
|
|
2249
|
|
2250 if ((list = g_list_nth(playlist_get(), pos))) {
|
|
2251 PlaylistEntry *entry = list->data;
|
|
2252 entry->selected = !entry->selected;
|
|
2253 invert_ok = TRUE;
|
|
2254 }
|
|
2255
|
|
2256 PLAYLIST_UNLOCK();
|
|
2257 playlist_recalc_total_time();
|
|
2258
|
|
2259 return invert_ok;
|
|
2260 }
|
|
2261
|
|
2262
|
|
2263 void
|
|
2264 playlist_select_range(gint min_pos, gint max_pos, gboolean select)
|
|
2265 {
|
|
2266 GList *list;
|
|
2267 gint i;
|
|
2268
|
|
2269 if (min_pos > max_pos)
|
|
2270 SWAP(min_pos, max_pos);
|
|
2271
|
|
2272 PLAYLIST_LOCK();
|
|
2273
|
|
2274 list = g_list_nth(playlist_get(), min_pos);
|
|
2275 for (i = min_pos; i <= max_pos && list; i++) {
|
|
2276 PlaylistEntry *entry = list->data;
|
|
2277 entry->selected = select;
|
|
2278 list = g_list_next(list);
|
|
2279 }
|
|
2280
|
|
2281 PLAYLIST_UNLOCK();
|
|
2282
|
|
2283 playlist_recalc_total_time();
|
|
2284 }
|
|
2285
|
|
2286 gboolean
|
|
2287 playlist_read_info_selection(void)
|
|
2288 {
|
|
2289 GList *node;
|
|
2290 gboolean retval = FALSE;
|
|
2291
|
|
2292 PLAYLIST_LOCK();
|
|
2293
|
|
2294 for (node = playlist_get(); node; node = g_list_next(node)) {
|
|
2295 PlaylistEntry *entry = node->data;
|
|
2296 if (!entry->selected)
|
|
2297 continue;
|
|
2298
|
|
2299 retval = TRUE;
|
|
2300
|
|
2301 str_replace_in(&entry->title, NULL);
|
|
2302 entry->length = -1;
|
|
2303
|
|
2304 if (!playlist_entry_get_info(entry)) {
|
|
2305 if (g_list_index(playlist_get(), entry) == -1)
|
|
2306 /* Entry disappeared while we looked it up. Restart. */
|
|
2307 node = playlist_get();
|
|
2308 }
|
|
2309 }
|
|
2310
|
|
2311 PLAYLIST_UNLOCK();
|
|
2312
|
|
2313 playlistwin_update_list();
|
|
2314 playlist_recalc_total_time();
|
|
2315
|
|
2316 return retval;
|
|
2317 }
|
|
2318
|
|
2319 void
|
|
2320 playlist_read_info(guint pos)
|
|
2321 {
|
|
2322 GList *node;
|
|
2323
|
|
2324 PLAYLIST_LOCK();
|
|
2325
|
|
2326 if ((node = g_list_nth(playlist_get(), pos))) {
|
|
2327 PlaylistEntry *entry = node->data;
|
|
2328 str_replace_in(&entry->title, NULL);
|
|
2329 entry->length = -1;
|
|
2330 playlist_entry_get_info(entry);
|
|
2331 }
|
|
2332
|
|
2333 PLAYLIST_UNLOCK();
|
|
2334
|
|
2335 playlistwin_update_list();
|
|
2336 playlist_recalc_total_time();
|
|
2337 }
|
|
2338
|
|
2339 void
|
|
2340 playlist_set_shuffle(gboolean shuffle)
|
|
2341 {
|
|
2342 PLAYLIST_LOCK();
|
|
2343
|
|
2344 cfg.shuffle = shuffle;
|
|
2345 playlist_generate_shuffle_list_nolock();
|
|
2346
|
|
2347 PLAYLIST_UNLOCK();
|
|
2348 }
|
|
2349
|
|
2350 void
|
|
2351 playlist_new(void)
|
|
2352 {
|
|
2353 playlist_set_current_name(NULL);
|
|
2354 playlist_clear();
|
|
2355 mainwin_clear_song_info();
|
|
2356 mainwin_set_info_text();
|
|
2357 }
|
|
2358
|
|
2359
|
|
2360 const gchar *
|
|
2361 playlist_get_filename_to_play(void)
|
|
2362 {
|
|
2363 const gchar *filename = NULL;
|
|
2364
|
|
2365 PLAYLIST_LOCK();
|
|
2366
|
|
2367 if (playlist) {
|
|
2368 if (!playlist_position) {
|
|
2369 if (cfg.shuffle)
|
|
2370 playlist_position = shuffle_list->data;
|
|
2371 else
|
|
2372 playlist_position = playlist->data;
|
|
2373 }
|
|
2374
|
|
2375 filename = playlist_position->filename;
|
|
2376 }
|
|
2377
|
|
2378 PLAYLIST_UNLOCK();
|
|
2379
|
|
2380 return filename;
|
|
2381 }
|