* Fixes in playback.c
[kugel-rb.git] / apps / playlist.c
blobe5afe2f8d56ece8d2cd439075db5c0b292d6df9c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by wavey@wavey.org
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 Dynamic playlist design (based on design originally proposed by ricII)
25 There are two files associated with a dynamic playlist:
26 1. Playlist file : This file contains the initial songs in the playlist.
27 The file is created by the user and stored on the hard
28 drive. NOTE: If we are playing the contents of a
29 directory, there will be no playlist file.
30 2. Control file : This file is automatically created when a playlist is
31 started and contains all the commands done to it.
33 The first non-comment line in a control file must begin with
34 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
35 DIR is the directory where the playlist is located and FILE is the
36 playlist filename. For dirplay, FILE will be empty. An empty playlist
37 will have both entries as null.
39 Control file commands:
40 a. Add track (A:<position>:<last position>:<path to track>)
41 - Insert a track at the specified position in the current
42 playlist. Last position is used to specify where last insertion
43 occurred.
44 b. Queue track (Q:<position>:<last position>:<path to track>)
45 - Queue a track at the specified position in the current
46 playlist. Queued tracks differ from added tracks in that they
47 are deleted from the playlist as soon as they are played and
48 they are not saved to disk as part of the playlist.
49 c. Delete track (D:<position>)
50 - Delete track from specified position in the current playlist.
51 d. Shuffle playlist (S:<seed>:<index>)
52 - Shuffle entire playlist with specified seed. The index
53 identifies the first index in the newly shuffled playlist
54 (needed for repeat mode).
55 e. Unshuffle playlist (U:<index>)
56 - Unshuffle entire playlist. The index identifies the first index
57 in the newly unshuffled playlist.
58 f. Reset last insert position (R)
59 - Needed so that insertions work properly after resume
61 Resume:
62 The only resume info that needs to be saved is the current index in the
63 playlist and the position in the track. When resuming, all the commands
64 in the control file will be reapplied so that the playlist indices are
65 exactly the same as before shutdown. To avoid unnecessary disk
66 accesses, the shuffle mode settings are also saved in settings and only
67 flushed to disk when required.
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <ctype.h>
73 #include "string-extra.h"
74 #include "playlist.h"
75 #include "ata_idle_notify.h"
76 #include "file.h"
77 #include "action.h"
78 #include "dir.h"
79 #include "debug.h"
80 #include "audio.h"
81 #include "lcd.h"
82 #include "kernel.h"
83 #include "settings.h"
84 #include "status.h"
85 #include "applimits.h"
86 #include "screens.h"
87 #include "core_alloc.h"
88 #include "misc.h"
89 #include "filefuncs.h"
90 #include "button.h"
91 #include "filetree.h"
92 #include "abrepeat.h"
93 #include "thread.h"
94 #include "usb.h"
95 #include "filetypes.h"
96 #ifdef HAVE_LCD_BITMAP
97 #include "icons.h"
98 #endif
99 #include "system.h"
101 #include "lang.h"
102 #include "talk.h"
103 #include "splash.h"
104 #include "rbunicode.h"
105 #include "root_menu.h"
106 #include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
108 #define PLAYLIST_CONTROL_FILE_VERSION 2
111 Each playlist index has a flag associated with it which identifies what
112 type of track it is. These flags are stored in the 4 high order bits of
113 the index.
115 NOTE: This limits the playlist file size to a max of 256M.
117 Bits 31-30:
118 00 = Playlist track
119 01 = Track was prepended into playlist
120 10 = Track was inserted into playlist
121 11 = Track was appended into playlist
122 Bit 29:
123 0 = Added track
124 1 = Queued track
125 Bit 28:
126 0 = Track entry is valid
127 1 = Track does not exist on disk and should be skipped
129 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
130 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
131 #define PLAYLIST_QUEUE_MASK 0x20000000
133 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
134 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
135 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
137 #define PLAYLIST_QUEUED 0x20000000
138 #define PLAYLIST_SKIPPED 0x10000000
140 struct directory_search_context {
141 struct playlist_info* playlist;
142 int position;
143 bool queue;
144 int count;
147 static struct playlist_info current_playlist;
149 static void empty_playlist(struct playlist_info* playlist, bool resume);
150 static void new_playlist(struct playlist_info* playlist, const char *dir,
151 const char *file);
152 static void create_control(struct playlist_info* playlist);
153 static int check_control(struct playlist_info* playlist);
154 static int recreate_control(struct playlist_info* playlist);
155 static void update_playlist_filename(struct playlist_info* playlist,
156 const char *dir, const char *file);
157 static int add_indices_to_playlist(struct playlist_info* playlist,
158 char* buffer, size_t buflen);
159 static int add_track_to_playlist(struct playlist_info* playlist,
160 const char *filename, int position,
161 bool queue, int seek_pos);
162 static int directory_search_callback(char* filename, void* context);
163 static int remove_track_from_playlist(struct playlist_info* playlist,
164 int position, bool write);
165 static int randomise_playlist(struct playlist_info* playlist,
166 unsigned int seed, bool start_current,
167 bool write);
168 static int sort_playlist(struct playlist_info* playlist, bool start_current,
169 bool write);
170 static int get_next_index(const struct playlist_info* playlist, int steps,
171 int repeat_mode);
172 static void find_and_set_playlist_index(struct playlist_info* playlist,
173 unsigned int seek);
174 static int compare(const void* p1, const void* p2);
175 static int get_filename(struct playlist_info* playlist, int index, int seek,
176 bool control_file, char *buf, int buf_length);
177 static int get_next_directory(char *dir);
178 static int get_next_dir(char *dir, bool is_forward, bool recursion);
179 static int get_previous_directory(char *dir);
180 static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
181 static int format_track_path(char *dest, char *src, int buf_length, int max,
182 const char *dir);
183 static void display_playlist_count(int count, const unsigned char *fmt,
184 bool final);
185 static void display_buffer_full(void);
186 static int flush_cached_control(struct playlist_info* playlist);
187 static int update_control(struct playlist_info* playlist,
188 enum playlist_command command, int i1, int i2,
189 const char* s1, const char* s2, void* data);
190 static void sync_control(struct playlist_info* playlist, bool force);
191 static int rotate_index(const struct playlist_info* playlist, int index);
193 #ifdef HAVE_DIRCACHE
194 #define PLAYLIST_LOAD_POINTERS 1
196 static struct event_queue playlist_queue SHAREDBSS_ATTR;
197 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
198 static const char playlist_thread_name[] = "playlist cachectrl";
199 #endif
201 static struct mutex current_playlist_mutex SHAREDBSS_ATTR;
202 static struct mutex created_playlist_mutex SHAREDBSS_ATTR;
204 /* Check if the filename suggests M3U or M3U8 format. */
205 static bool is_m3u8(const char* filename)
207 int len = strlen(filename);
209 /* Default to M3U8 unless explicitly told otherwise. */
210 return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
214 /* Convert a filename in an M3U playlist to UTF-8.
216 * buf - the filename to convert; can contain more than one line from the
217 * playlist.
218 * buf_len - amount of buf that is used.
219 * buf_max - total size of buf.
220 * temp - temporary conversion buffer, at least buf_max bytes.
222 * Returns the length of the converted filename.
224 static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
226 int i = 0;
227 char* dest;
229 /* Locate EOL. */
230 while ((buf[i] != '\n') && (buf[i] != '\r') && (i < buf_len))
232 i++;
235 /* Work back killing white space. */
236 while ((i > 0) && isspace(buf[i - 1]))
238 i--;
241 buf_len = i;
242 dest = temp;
244 /* Convert char by char, so as to not overflow temp (iso_decode should
245 * preferably handle this). No more than 4 bytes should be generated for
246 * each input char.
248 for (i = 0; i < buf_len && dest < (temp + buf_max - 4); i++)
250 dest = iso_decode(&buf[i], dest, -1, 1);
253 *dest = 0;
254 strcpy(buf, temp);
255 return dest - temp;
259 * remove any files and indices associated with the playlist
261 static void empty_playlist(struct playlist_info* playlist, bool resume)
263 playlist->filename[0] = '\0';
264 playlist->utf8 = true;
266 if(playlist->fd >= 0)
267 /* If there is an already open playlist, close it. */
268 close(playlist->fd);
269 playlist->fd = -1;
271 if(playlist->control_fd >= 0)
272 close(playlist->control_fd);
273 playlist->control_fd = -1;
274 playlist->control_created = false;
276 playlist->in_ram = false;
278 if (playlist->buffer)
279 playlist->buffer[0] = 0;
281 playlist->buffer_end_pos = 0;
283 playlist->index = 0;
284 playlist->first_index = 0;
285 playlist->amount = 0;
286 playlist->last_insert_pos = -1;
287 playlist->seed = 0;
288 playlist->shuffle_modified = false;
289 playlist->deleted = false;
290 playlist->num_inserted_tracks = 0;
291 playlist->started = false;
293 playlist->num_cached = 0;
294 playlist->pending_control_sync = false;
296 if (!resume && playlist->current)
298 /* start with fresh playlist control file when starting new
299 playlist */
300 create_control(playlist);
305 * Initialize a new playlist for viewing/editing/playing. dir is the
306 * directory where the playlist is located and file is the filename.
308 static void new_playlist(struct playlist_info* playlist, const char *dir,
309 const char *file)
311 const char *fileused = file;
312 const char *dirused = dir;
313 empty_playlist(playlist, false);
315 if (!fileused)
317 fileused = "";
319 if (dirused && playlist->current) /* !current cannot be in_ram */
320 playlist->in_ram = true;
321 else
322 dirused = ""; /* empty playlist */
325 update_playlist_filename(playlist, dirused, fileused);
327 if (playlist->control_fd >= 0)
329 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
330 PLAYLIST_CONTROL_FILE_VERSION, -1, dirused, fileused, NULL);
331 sync_control(playlist, false);
336 * create control file for playlist
338 static void create_control(struct playlist_info* playlist)
340 playlist->control_fd = open(playlist->control_filename,
341 O_CREAT|O_RDWR|O_TRUNC, 0666);
342 if (playlist->control_fd < 0)
344 if (check_rockboxdir())
346 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
347 splashf(HZ*2, (unsigned char *)"%s (%d)",
348 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
349 playlist->control_fd);
351 playlist->control_created = false;
353 else
355 playlist->control_created = true;
360 * validate the control file. This may include creating/initializing it if
361 * necessary;
363 static int check_control(struct playlist_info* playlist)
365 if (!playlist->control_created)
367 create_control(playlist);
369 if (playlist->control_fd >= 0)
371 char* dir = playlist->filename;
372 char* file = playlist->filename+playlist->dirlen;
373 char c = playlist->filename[playlist->dirlen-1];
375 playlist->filename[playlist->dirlen-1] = '\0';
377 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
378 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
379 sync_control(playlist, false);
380 playlist->filename[playlist->dirlen-1] = c;
384 if (playlist->control_fd < 0)
385 return -1;
387 return 0;
391 * recreate the control file based on current playlist entries
393 static int recreate_control(struct playlist_info* playlist)
395 char temp_file[MAX_PATH+1];
396 int temp_fd = -1;
397 int i;
398 int result = 0;
400 if(playlist->control_fd >= 0)
402 char* dir = playlist->filename;
403 char* file = playlist->filename+playlist->dirlen;
404 char c = playlist->filename[playlist->dirlen-1];
406 close(playlist->control_fd);
408 snprintf(temp_file, sizeof(temp_file), "%s_temp",
409 playlist->control_filename);
411 if (rename(playlist->control_filename, temp_file) < 0)
412 return -1;
414 temp_fd = open(temp_file, O_RDONLY);
415 if (temp_fd < 0)
416 return -1;
418 playlist->control_fd = open(playlist->control_filename,
419 O_CREAT|O_RDWR|O_TRUNC, 0666);
420 if (playlist->control_fd < 0)
421 return -1;
423 playlist->filename[playlist->dirlen-1] = '\0';
425 /* cannot call update_control() because of mutex */
426 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
427 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
429 playlist->filename[playlist->dirlen-1] = c;
431 if (result < 0)
433 close(temp_fd);
434 return result;
438 playlist->seed = 0;
439 playlist->shuffle_modified = false;
440 playlist->deleted = false;
441 playlist->num_inserted_tracks = 0;
443 for (i=0; i<playlist->amount; i++)
445 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
447 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
448 char inserted_file[MAX_PATH+1];
450 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
451 SEEK_SET);
452 read_line(temp_fd, inserted_file, sizeof(inserted_file));
454 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
455 queue?'Q':'A', i, playlist->last_insert_pos);
456 if (result > 0)
458 /* save the position in file where name is written */
459 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
461 result = fdprintf(playlist->control_fd, "%s\n",
462 inserted_file);
464 playlist->indices[i] =
465 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
468 if (result < 0)
469 break;
471 playlist->num_inserted_tracks++;
475 close(temp_fd);
476 remove(temp_file);
477 fsync(playlist->control_fd);
479 if (result < 0)
480 return result;
482 return 0;
486 * store directory and name of playlist file
488 static void update_playlist_filename(struct playlist_info* playlist,
489 const char *dir, const char *file)
491 char *sep="";
492 int dirlen = strlen(dir);
494 playlist->utf8 = is_m3u8(file);
496 /* If the dir does not end in trailing slash, we use a separator.
497 Otherwise we don't. */
498 if('/' != dir[dirlen-1])
500 sep="/";
501 dirlen++;
504 playlist->dirlen = dirlen;
506 snprintf(playlist->filename, sizeof(playlist->filename),
507 "%s%s%s", dir, sep, file);
511 * calculate track offsets within a playlist file
513 static int add_indices_to_playlist(struct playlist_info* playlist,
514 char* buffer, size_t buflen)
516 unsigned int nread;
517 unsigned int i = 0;
518 unsigned int count = 0;
519 bool store_index;
520 unsigned char *p;
521 int result = 0;
523 if(-1 == playlist->fd)
524 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
525 if(playlist->fd < 0)
526 return -1; /* failure */
527 if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
528 playlist->utf8 = true; /* Override any earlier indication. */
530 splash(0, ID2P(LANG_WAIT));
532 if (!buffer)
534 /* use mp3 buffer for maximum load speed */
535 audio_stop();
536 buffer = audio_get_buffer(false, &buflen);
539 store_index = true;
541 while(1)
543 nread = read(playlist->fd, buffer, buflen);
544 /* Terminate on EOF */
545 if(nread <= 0)
546 break;
548 p = (unsigned char *)buffer;
550 for(count=0; count < nread; count++,p++) {
552 /* Are we on a new line? */
553 if((*p == '\n') || (*p == '\r'))
555 store_index = true;
557 else if(store_index)
559 store_index = false;
561 if(*p != '#')
563 if ( playlist->amount >= playlist->max_playlist_size ) {
564 display_buffer_full();
565 result = -1;
566 goto exit;
569 /* Store a new entry */
570 playlist->indices[ playlist->amount ] = i+count;
571 #ifdef HAVE_DIRCACHE
572 if (playlist->filenames)
573 playlist->filenames[ playlist->amount ] = -1;
574 #endif
575 playlist->amount++;
580 i+= count;
583 exit:
584 #ifdef HAVE_DIRCACHE
585 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
586 #endif
588 return result;
592 * Utility function to create a new playlist, fill it with the next or
593 * previous directory, shuffle it if needed, and start playback.
594 * If play_last is true and direction zero or negative, start playing
595 * the last file in the directory, otherwise start playing the first.
597 static int create_and_play_dir(int direction, bool play_last)
599 char dir[MAX_PATH + 1];
600 int res;
601 int index = -1;
603 if(direction > 0)
604 res = get_next_directory(dir);
605 else
606 res = get_previous_directory(dir);
608 if (!res)
610 if (playlist_create(dir, NULL) != -1)
612 ft_build_playlist(tree_get_context(), 0);
614 if (global_settings.playlist_shuffle)
615 playlist_shuffle(current_tick, -1);
617 if (play_last && direction <= 0)
618 index = current_playlist.amount - 1;
619 else
620 index = 0;
622 #if (CONFIG_CODEC == SWCODEC)
623 current_playlist.started = true;
624 #else
625 playlist_start(index, 0);
626 #endif
629 /* we've overwritten the dircache when getting the next/previous dir,
630 so the tree browser context will need to be reloaded */
631 reload_directory();
634 return index;
638 * Removes all tracks, from the playlist, leaving the presently playing
639 * track queued.
641 int playlist_remove_all_tracks(struct playlist_info *playlist)
643 int result;
645 if (playlist == NULL)
646 playlist = &current_playlist;
648 while (playlist->index > 0)
649 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
650 return result;
652 while (playlist->amount > 1)
653 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
654 return result;
656 if (playlist->amount == 1) {
657 playlist->indices[0] |= PLAYLIST_QUEUED;
660 return 0;
665 * Add track to playlist at specified position. There are seven special
666 * positions that can be specified:
667 * PLAYLIST_PREPEND - Add track at beginning of playlist
668 * PLAYLIST_INSERT - Add track after current song. NOTE: If
669 * there are already inserted tracks then track
670 * is added to the end of the insertion list
671 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
672 * matter what other tracks have been inserted
673 * PLAYLIST_INSERT_LAST - Add track to end of playlist
674 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
675 * current playing track and end of playlist
676 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
677 * the playlist.
678 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
679 * and inster this track at the end.
681 static int add_track_to_playlist(struct playlist_info* playlist,
682 const char *filename, int position,
683 bool queue, int seek_pos)
685 int insert_position, orig_position;
686 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
687 int i;
689 insert_position = orig_position = position;
691 if (playlist->amount >= playlist->max_playlist_size)
693 display_buffer_full();
694 return -1;
697 switch (position)
699 case PLAYLIST_PREPEND:
700 position = insert_position = playlist->first_index;
701 break;
702 case PLAYLIST_INSERT:
703 /* if there are already inserted tracks then add track to end of
704 insertion list else add after current playing track */
705 if (playlist->last_insert_pos >= 0 &&
706 playlist->last_insert_pos < playlist->amount &&
707 (playlist->indices[playlist->last_insert_pos]&
708 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
709 position = insert_position = playlist->last_insert_pos+1;
710 else if (playlist->amount > 0)
711 position = insert_position = playlist->index + 1;
712 else
713 position = insert_position = 0;
715 playlist->last_insert_pos = position;
716 break;
717 case PLAYLIST_INSERT_FIRST:
718 if (playlist->amount > 0)
719 position = insert_position = playlist->index + 1;
720 else
721 position = insert_position = 0;
723 playlist->last_insert_pos = position;
724 break;
725 case PLAYLIST_INSERT_LAST:
726 if (playlist->first_index > 0)
727 position = insert_position = playlist->first_index;
728 else
729 position = insert_position = playlist->amount;
731 playlist->last_insert_pos = position;
732 break;
733 case PLAYLIST_INSERT_SHUFFLED:
735 if (playlist->started)
737 int offset;
738 int n = playlist->amount -
739 rotate_index(playlist, playlist->index);
741 if (n > 0)
742 offset = rand() % n;
743 else
744 offset = 0;
746 position = playlist->index + offset + 1;
747 if (position >= playlist->amount)
748 position -= playlist->amount;
750 insert_position = position;
752 else
753 position = insert_position = (rand() % (playlist->amount+1));
754 break;
756 case PLAYLIST_INSERT_LAST_SHUFFLED:
758 position = insert_position = playlist->last_shuffled_start +
759 rand() % (playlist->amount - playlist->last_shuffled_start + 1);
760 break;
762 case PLAYLIST_REPLACE:
763 if (playlist_remove_all_tracks(playlist) < 0)
764 return -1;
766 playlist->last_insert_pos = position = insert_position = playlist->index + 1;
767 break;
770 if (queue)
771 flags |= PLAYLIST_QUEUED;
773 /* shift indices so that track can be added */
774 for (i=playlist->amount; i>insert_position; i--)
776 playlist->indices[i] = playlist->indices[i-1];
777 #ifdef HAVE_DIRCACHE
778 if (playlist->filenames)
779 playlist->filenames[i] = playlist->filenames[i-1];
780 #endif
783 /* update stored indices if needed */
785 if (orig_position < 0)
787 if (playlist->amount > 0 && insert_position <= playlist->index &&
788 playlist->started)
789 playlist->index++;
791 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
792 orig_position != PLAYLIST_PREPEND && playlist->started)
793 playlist->first_index++;
796 if (insert_position < playlist->last_insert_pos ||
797 (insert_position == playlist->last_insert_pos && position < 0))
798 playlist->last_insert_pos++;
800 if (seek_pos < 0 && playlist->control_fd >= 0)
802 int result = update_control(playlist,
803 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
804 playlist->last_insert_pos, filename, NULL, &seek_pos);
806 if (result < 0)
807 return result;
810 playlist->indices[insert_position] = flags | seek_pos;
812 #ifdef HAVE_DIRCACHE
813 if (playlist->filenames)
814 playlist->filenames[insert_position] = -1;
815 #endif
817 playlist->amount++;
818 playlist->num_inserted_tracks++;
820 return insert_position;
824 * Callback for playlist_directory_tracksearch to insert track into
825 * playlist.
827 static int directory_search_callback(char* filename, void* context)
829 struct directory_search_context* c =
830 (struct directory_search_context*) context;
831 int insert_pos;
833 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
834 c->queue, -1);
836 if (insert_pos < 0)
837 return -1;
839 (c->count)++;
841 /* Make sure tracks are inserted in correct order if user requests
842 INSERT_FIRST */
843 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
844 c->position = insert_pos + 1;
846 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
848 unsigned char* count_str;
850 if (c->queue)
851 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
852 else
853 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
855 display_playlist_count(c->count, count_str, false);
857 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
858 (audio_status() & AUDIO_STATUS_PLAY) &&
859 c->playlist->started)
860 audio_flush_and_reload_tracks();
863 return 0;
867 * remove track at specified position
869 static int remove_track_from_playlist(struct playlist_info* playlist,
870 int position, bool write)
872 int i;
873 bool inserted;
875 if (playlist->amount <= 0)
876 return -1;
878 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
880 /* shift indices now that track has been removed */
881 for (i=position; i<playlist->amount; i++)
883 playlist->indices[i] = playlist->indices[i+1];
884 #ifdef HAVE_DIRCACHE
885 if (playlist->filenames)
886 playlist->filenames[i] = playlist->filenames[i+1];
887 #endif
890 playlist->amount--;
892 if (inserted)
893 playlist->num_inserted_tracks--;
894 else
895 playlist->deleted = true;
897 /* update stored indices if needed */
898 if (position < playlist->index)
899 playlist->index--;
901 if (position < playlist->first_index)
903 playlist->first_index--;
906 if (position <= playlist->last_insert_pos)
907 playlist->last_insert_pos--;
909 if (write && playlist->control_fd >= 0)
911 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
912 position, -1, NULL, NULL, NULL);
914 if (result < 0)
915 return result;
917 sync_control(playlist, false);
920 return 0;
924 * randomly rearrange the array of indices for the playlist. If start_current
925 * is true then update the index to the new index of the current playing track
927 static int randomise_playlist(struct playlist_info* playlist,
928 unsigned int seed, bool start_current,
929 bool write)
931 int count;
932 int candidate;
933 long store;
934 unsigned int current = playlist->indices[playlist->index];
936 /* seed 0 is used to identify sorted playlist for resume purposes */
937 if (seed == 0)
938 seed = 1;
940 /* seed with the given seed */
941 srand(seed);
943 /* randomise entire indices list */
944 for(count = playlist->amount - 1; count >= 0; count--)
946 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
947 candidate = rand() % (count + 1);
949 /* now swap the values at the 'count' and 'candidate' positions */
950 store = playlist->indices[candidate];
951 playlist->indices[candidate] = playlist->indices[count];
952 playlist->indices[count] = store;
953 #ifdef HAVE_DIRCACHE
954 if (playlist->filenames)
956 store = playlist->filenames[candidate];
957 playlist->filenames[candidate] = playlist->filenames[count];
958 playlist->filenames[count] = store;
960 #endif
963 if (start_current)
964 find_and_set_playlist_index(playlist, current);
966 /* indices have been moved so last insert position is no longer valid */
967 playlist->last_insert_pos = -1;
969 playlist->seed = seed;
970 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
971 playlist->shuffle_modified = true;
973 if (write)
975 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
976 playlist->first_index, NULL, NULL, NULL);
979 return 0;
983 * Sort the array of indices for the playlist. If start_current is true then
984 * set the index to the new index of the current song.
985 * Also while going to unshuffled mode set the first_index to 0.
987 static int sort_playlist(struct playlist_info* playlist, bool start_current,
988 bool write)
990 unsigned int current = playlist->indices[playlist->index];
992 if (playlist->amount > 0)
993 qsort(playlist->indices, playlist->amount,
994 sizeof(playlist->indices[0]), compare);
996 #ifdef HAVE_DIRCACHE
997 /** We need to re-check the song names from disk because qsort can't
998 * sort two arrays at once :/
999 * FIXME: Please implement a better way to do this. */
1000 memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int));
1001 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1002 #endif
1004 if (start_current)
1005 find_and_set_playlist_index(playlist, current);
1007 /* indices have been moved so last insert position is no longer valid */
1008 playlist->last_insert_pos = -1;
1010 if (!playlist->num_inserted_tracks && !playlist->deleted)
1011 playlist->shuffle_modified = false;
1012 if (write && playlist->control_fd >= 0)
1014 playlist->first_index = 0;
1015 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1016 playlist->first_index, -1, NULL, NULL, NULL);
1019 return 0;
1022 /* Calculate how many steps we have to really step when skipping entries
1023 * marked as bad.
1025 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1027 int i, count, direction;
1028 int index;
1029 int stepped_count = 0;
1031 if (steps < 0)
1033 direction = -1;
1034 count = -steps;
1036 else
1038 direction = 1;
1039 count = steps;
1042 index = playlist->index;
1043 i = 0;
1044 do {
1045 /* Boundary check */
1046 if (index < 0)
1047 index += playlist->amount;
1048 if (index >= playlist->amount)
1049 index -= playlist->amount;
1051 /* Check if we found a bad entry. */
1052 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1054 steps += direction;
1055 /* Are all entries bad? */
1056 if (stepped_count++ > playlist->amount)
1057 break ;
1059 else
1060 i++;
1062 index += direction;
1063 } while (i <= count);
1065 return steps;
1068 /* Marks the index of the track to be skipped that is "steps" away from
1069 * current playing track.
1071 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1073 int index;
1075 if (playlist == NULL)
1076 playlist = &current_playlist;
1078 /* need to account for already skipped tracks */
1079 steps = calculate_step_count(playlist, steps);
1081 index = playlist->index + steps;
1082 if (index < 0)
1083 index += playlist->amount;
1084 else if (index >= playlist->amount)
1085 index -= playlist->amount;
1087 playlist->indices[index] |= PLAYLIST_SKIPPED;
1091 * returns the index of the track that is "steps" away from current playing
1092 * track.
1094 static int get_next_index(const struct playlist_info* playlist, int steps,
1095 int repeat_mode)
1097 int current_index = playlist->index;
1098 int next_index = -1;
1100 if (playlist->amount <= 0)
1101 return -1;
1103 if (repeat_mode == -1)
1104 repeat_mode = global_settings.repeat_mode;
1106 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1107 repeat_mode = REPEAT_ALL;
1109 steps = calculate_step_count(playlist, steps);
1110 switch (repeat_mode)
1112 case REPEAT_SHUFFLE:
1113 /* Treat repeat shuffle just like repeat off. At end of playlist,
1114 play will be resumed in playlist_next() */
1115 case REPEAT_OFF:
1117 current_index = rotate_index(playlist, current_index);
1118 next_index = current_index+steps;
1119 if ((next_index < 0) || (next_index >= playlist->amount))
1120 next_index = -1;
1121 else
1122 next_index = (next_index+playlist->first_index) %
1123 playlist->amount;
1125 break;
1128 case REPEAT_ONE:
1129 #ifdef AB_REPEAT_ENABLE
1130 case REPEAT_AB:
1131 #endif
1132 next_index = current_index;
1133 break;
1135 case REPEAT_ALL:
1136 default:
1138 next_index = (current_index+steps) % playlist->amount;
1139 while (next_index < 0)
1140 next_index += playlist->amount;
1142 if (steps >= playlist->amount)
1144 int i, index;
1146 index = next_index;
1147 next_index = -1;
1149 /* second time around so skip the queued files */
1150 for (i=0; i<playlist->amount; i++)
1152 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1153 index = (index+1) % playlist->amount;
1154 else
1156 next_index = index;
1157 break;
1161 break;
1165 /* No luck if the whole playlist was bad. */
1166 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1167 return -1;
1169 return next_index;
1173 * Search for the seek track and set appropriate indices. Used after shuffle
1174 * to make sure the current index is still pointing to correct track.
1176 static void find_and_set_playlist_index(struct playlist_info* playlist,
1177 unsigned int seek)
1179 int i;
1181 /* Set the index to the current song */
1182 for (i=0; i<playlist->amount; i++)
1184 if (playlist->indices[i] == seek)
1186 playlist->index = playlist->first_index = i;
1188 break;
1194 * used to sort track indices. Sort order is as follows:
1195 * 1. Prepended tracks (in prepend order)
1196 * 2. Playlist/directory tracks (in playlist order)
1197 * 3. Inserted/Appended tracks (in insert order)
1199 static int compare(const void* p1, const void* p2)
1201 unsigned long* e1 = (unsigned long*) p1;
1202 unsigned long* e2 = (unsigned long*) p2;
1203 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1204 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1206 if (flags1 == flags2)
1207 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1208 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1209 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1210 return -1;
1211 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1212 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1213 return 1;
1214 else if (flags1 && flags2)
1215 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1216 else
1217 return *e1 - *e2;
1220 #ifdef HAVE_DIRCACHE
1222 * Thread to update filename pointers to dircache on background
1223 * without affecting playlist load up performance. This thread also flushes
1224 * any pending control commands when the disk spins up.
1226 static void playlist_flush_callback(void *param)
1228 (void)param;
1229 struct playlist_info *playlist;
1230 playlist = &current_playlist;
1231 if (playlist->control_fd >= 0)
1233 if (playlist->num_cached > 0)
1235 mutex_lock(playlist->control_mutex);
1236 flush_cached_control(playlist);
1237 mutex_unlock(playlist->control_mutex);
1239 sync_control(playlist, true);
1243 static bool is_dircache_pointers_intact(void)
1245 return dircache_get_appflag(DIRCACHE_APPFLAG_PLAYLIST) ? true : false;
1248 static void playlist_thread(void)
1250 struct queue_event ev;
1251 bool dirty_pointers = false;
1252 static char tmp[MAX_PATH+1];
1254 struct playlist_info *playlist;
1255 int index;
1256 int seek;
1257 bool control_file;
1259 int sleep_time = 5;
1261 #ifdef HAVE_DISK_STORAGE
1262 if (global_settings.disk_spindown > 1 &&
1263 global_settings.disk_spindown <= 5)
1264 sleep_time = global_settings.disk_spindown - 1;
1265 #endif
1267 while (1)
1269 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1271 switch (ev.id)
1273 case PLAYLIST_LOAD_POINTERS:
1274 dirty_pointers = true;
1275 break ;
1277 /* Start the background scanning after either the disk spindown
1278 timeout or 5s, whichever is less */
1279 case SYS_TIMEOUT:
1281 playlist = &current_playlist;
1282 if (playlist->control_fd >= 0)
1284 if (playlist->num_cached > 0)
1285 register_storage_idle_func(playlist_flush_callback);
1288 if (!dircache_is_enabled() || !playlist->filenames
1289 || playlist->amount <= 0)
1291 break ;
1294 /* Check if previously loaded pointers are intact. */
1295 if (is_dircache_pointers_intact() && !dirty_pointers)
1296 break ;
1298 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1299 cpu_boost(true);
1300 #endif
1301 for (index = 0; index < playlist->amount
1302 && queue_empty(&playlist_queue); index++)
1304 /* Process only pointers that are not already loaded. */
1305 if (is_dircache_pointers_intact() && playlist->filenames[index] >= 0)
1306 continue ;
1308 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1309 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1311 /* Load the filename from playlist file. */
1312 if (get_filename(playlist, index, seek, control_file, tmp,
1313 sizeof(tmp)) < 0)
1315 break ;
1318 /* Set the dircache entry pointer. */
1319 playlist->filenames[index] = dircache_get_entry_id(tmp);
1321 /* And be on background so user doesn't notice any delays. */
1322 yield();
1325 if (dircache_is_enabled())
1326 dircache_set_appflag(DIRCACHE_APPFLAG_PLAYLIST);
1328 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1329 cpu_boost(false);
1330 #endif
1331 if (index == playlist->amount)
1332 dirty_pointers = false;
1334 break ;
1337 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
1338 case SYS_USB_CONNECTED:
1339 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1340 usb_wait_for_disconnect(&playlist_queue);
1341 break ;
1342 #endif
1346 #endif
1349 * gets pathname for track at seek index
1351 static int get_filename(struct playlist_info* playlist, int index, int seek,
1352 bool control_file, char *buf, int buf_length)
1354 int fd;
1355 int max = -1;
1356 char tmp_buf[MAX_PATH+1];
1357 char dir_buf[MAX_PATH+1];
1358 bool utf8 = playlist->utf8;
1360 if (buf_length > MAX_PATH+1)
1361 buf_length = MAX_PATH+1;
1363 #ifdef HAVE_DIRCACHE
1364 if (is_dircache_pointers_intact() && playlist->filenames)
1366 if (playlist->filenames[index] >= 0)
1368 max = dircache_copy_path(playlist->filenames[index],
1369 tmp_buf, sizeof(tmp_buf)-1);
1372 #else
1373 (void)index;
1374 #endif
1376 if (playlist->in_ram && !control_file && max < 0)
1378 max = strlcpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1380 else if (max < 0)
1382 mutex_lock(playlist->control_mutex);
1384 if (control_file)
1386 fd = playlist->control_fd;
1387 utf8 = true;
1389 else
1391 if(-1 == playlist->fd)
1392 playlist->fd = open(playlist->filename, O_RDONLY);
1394 fd = playlist->fd;
1397 if(-1 != fd)
1400 if (lseek(fd, seek, SEEK_SET) != seek)
1401 max = -1;
1402 else
1404 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1406 if ((max > 0) && !utf8)
1408 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1409 * be as large as tmp_buf.
1411 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1416 mutex_unlock(playlist->control_mutex);
1418 if (max < 0)
1420 if (control_file)
1421 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1422 else
1423 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1425 return max;
1429 strlcpy(dir_buf, playlist->filename, playlist->dirlen);
1431 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1434 static int get_next_directory(char *dir){
1435 return get_next_dir(dir,true,false);
1438 static int get_previous_directory(char *dir){
1439 return get_next_dir(dir,false,false);
1443 * search through all the directories (starting with the current) to find
1444 * one that has tracks to play
1446 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1448 struct playlist_info* playlist = &current_playlist;
1449 int result = -1;
1450 char *start_dir = NULL;
1451 bool exit = false;
1452 int i;
1453 struct tree_context* tc = tree_get_context();
1454 int saved_dirfilter = *(tc->dirfilter);
1456 /* process random folder advance */
1457 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1459 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
1460 if (fd >= 0)
1462 char buffer[MAX_PATH];
1463 int folder_count = 0;
1464 srand(current_tick);
1465 *(tc->dirfilter) = SHOW_MUSIC;
1466 tc->sort_dir = global_settings.sort_dir;
1467 read(fd,&folder_count,sizeof(int));
1468 if (!folder_count)
1469 exit = true;
1470 while (!exit)
1472 i = rand()%folder_count;
1473 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1474 read(fd,buffer,MAX_PATH);
1475 if (check_subdir_for_music(buffer, "", false) ==0)
1476 exit = true;
1478 if (folder_count)
1479 strcpy(dir,buffer);
1480 close(fd);
1481 *(tc->dirfilter) = saved_dirfilter;
1482 tc->sort_dir = global_settings.sort_dir;
1483 reload_directory();
1484 return 0;
1488 /* not random folder advance (or random folder advance unavailable) */
1489 if (recursion)
1491 /* start with root */
1492 dir[0] = '\0';
1494 else
1496 /* start with current directory */
1497 strlcpy(dir, playlist->filename, playlist->dirlen);
1500 /* use the tree browser dircache to load files */
1501 *(tc->dirfilter) = SHOW_ALL;
1503 /* set up sorting/direction */
1504 tc->sort_dir = global_settings.sort_dir;
1505 if (!is_forward)
1507 static const char sortpairs[] =
1509 [SORT_ALPHA] = SORT_ALPHA_REVERSED,
1510 [SORT_DATE] = SORT_DATE_REVERSED,
1511 [SORT_TYPE] = SORT_TYPE_REVERSED,
1512 [SORT_ALPHA_REVERSED] = SORT_ALPHA,
1513 [SORT_DATE_REVERSED] = SORT_DATE,
1514 [SORT_TYPE_REVERSED] = SORT_TYPE,
1517 if ((unsigned)tc->sort_dir < sizeof(sortpairs))
1518 tc->sort_dir = sortpairs[tc->sort_dir];
1521 while (!exit)
1523 struct entry *files;
1524 int num_files = 0;
1525 int i;
1527 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1529 exit = true;
1530 result = -1;
1531 break;
1534 files = tc->cache.entries;
1535 num_files = tc->filesindir;
1537 for (i=0; i<num_files; i++)
1539 /* user abort */
1540 if (action_userabort(TIMEOUT_NOBLOCK))
1542 result = -1;
1543 exit = true;
1544 break;
1547 if (files[i].attr & ATTR_DIRECTORY)
1549 if (!start_dir)
1551 result = check_subdir_for_music(dir, files[i].name, true);
1552 if (result != -1)
1554 exit = true;
1555 break;
1558 else if (!strcmp(start_dir, files[i].name))
1559 start_dir = NULL;
1563 if (!exit)
1565 /* move down to parent directory. current directory name is
1566 stored as the starting point for the search in parent */
1567 start_dir = strrchr(dir, '/');
1568 if (start_dir)
1570 *start_dir = '\0';
1571 start_dir++;
1573 else
1574 break;
1578 /* restore dirfilter */
1579 *(tc->dirfilter) = saved_dirfilter;
1580 tc->sort_dir = global_settings.sort_dir;
1582 /* special case if nothing found: try start searching again from root */
1583 if (result == -1 && !recursion){
1584 result = get_next_dir(dir, is_forward, true);
1587 return result;
1591 * Checks if there are any music files in the dir or any of its
1592 * subdirectories. May be called recursively.
1594 static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1596 int result = -1;
1597 int dirlen = strlen(dir);
1598 int num_files = 0;
1599 int i;
1600 struct entry *files;
1601 bool has_music = false;
1602 bool has_subdir = false;
1603 struct tree_context* tc = tree_get_context();
1605 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1607 if (ft_load(tc, dir) < 0)
1609 return -2;
1612 files = tc->cache.entries;
1613 num_files = tc->filesindir;
1615 for (i=0; i<num_files; i++)
1617 if (files[i].attr & ATTR_DIRECTORY)
1618 has_subdir = true;
1619 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1621 has_music = true;
1622 break;
1626 if (has_music)
1627 return 0;
1629 if (has_subdir && recurse)
1631 for (i=0; i<num_files; i++)
1633 if (action_userabort(TIMEOUT_NOBLOCK))
1635 result = -2;
1636 break;
1639 if (files[i].attr & ATTR_DIRECTORY)
1641 result = check_subdir_for_music(dir, files[i].name, true);
1642 if (!result)
1643 break;
1648 if (result < 0)
1650 if (dirlen)
1652 dir[dirlen] = '\0';
1654 else
1656 strcpy(dir, "/");
1659 /* we now need to reload our current directory */
1660 if(ft_load(tc, dir) < 0)
1661 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1663 return result;
1667 * Returns absolute path of track
1669 static int format_track_path(char *dest, char *src, int buf_length, int max,
1670 const char *dir)
1672 int i = 0;
1673 int j;
1674 char *temp_ptr;
1676 /* Look for the end of the string */
1677 while((i < max) &&
1678 (src[i] != '\n') &&
1679 (src[i] != '\r') &&
1680 (src[i] != '\0'))
1681 i++;
1683 /* Now work back killing white space */
1684 while((i > 0) &&
1685 ((src[i-1] == ' ') ||
1686 (src[i-1] == '\t')))
1687 i--;
1689 /* Zero-terminate the file name */
1690 src[i]=0;
1692 /* replace backslashes with forward slashes */
1693 for ( j=0; j<i; j++ )
1694 if ( src[j] == '\\' )
1695 src[j] = '/';
1697 if('/' == src[0])
1699 strlcpy(dest, src, buf_length);
1701 else
1703 /* handle dos style drive letter */
1704 if (':' == src[1])
1705 strlcpy(dest, &src[2], buf_length);
1706 else if (!strncmp(src, "../", 3))
1708 /* handle relative paths */
1709 i=3;
1710 while(!strncmp(&src[i], "../", 3))
1711 i += 3;
1712 for (j=0; j<i/3; j++) {
1713 temp_ptr = strrchr(dir, '/');
1714 if (temp_ptr)
1715 *temp_ptr = '\0';
1716 else
1717 break;
1719 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1721 else if ( '.' == src[0] && '/' == src[1] ) {
1722 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1724 else {
1725 snprintf(dest, buf_length, "%s/%s", dir, src);
1729 return 0;
1733 * Display splash message showing progress of playlist/directory insertion or
1734 * save.
1736 static void display_playlist_count(int count, const unsigned char *fmt,
1737 bool final)
1739 static long talked_tick = 0;
1740 long id = P2ID(fmt);
1741 if(global_settings.talk_menu && id>=0)
1743 if(final || (count && (talked_tick == 0
1744 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1746 talked_tick = current_tick;
1747 talk_number(count, false);
1748 talk_id(id, true);
1751 fmt = P2STR(fmt);
1753 splashf(0, fmt, count, str(LANG_OFF_ABORT));
1757 * Display buffer full message
1759 static void display_buffer_full(void)
1761 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1765 * Flush any cached control commands to disk. Called when playlist is being
1766 * modified. Returns 0 on success and -1 on failure.
1768 static int flush_cached_control(struct playlist_info* playlist)
1770 int result = 0;
1771 int i;
1773 if (!playlist->num_cached)
1774 return 0;
1776 lseek(playlist->control_fd, 0, SEEK_END);
1778 for (i=0; i<playlist->num_cached; i++)
1780 struct playlist_control_cache* cache =
1781 &(playlist->control_cache[i]);
1783 switch (cache->command)
1785 case PLAYLIST_COMMAND_PLAYLIST:
1786 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1787 cache->i1, cache->s1, cache->s2);
1788 break;
1789 case PLAYLIST_COMMAND_ADD:
1790 case PLAYLIST_COMMAND_QUEUE:
1791 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1792 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1793 cache->i1, cache->i2);
1794 if (result > 0)
1796 /* save the position in file where name is written */
1797 int* seek_pos = (int *)cache->data;
1798 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1799 result = fdprintf(playlist->control_fd, "%s\n",
1800 cache->s1);
1802 break;
1803 case PLAYLIST_COMMAND_DELETE:
1804 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1805 break;
1806 case PLAYLIST_COMMAND_SHUFFLE:
1807 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1808 cache->i1, cache->i2);
1809 break;
1810 case PLAYLIST_COMMAND_UNSHUFFLE:
1811 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1812 break;
1813 case PLAYLIST_COMMAND_RESET:
1814 result = fdprintf(playlist->control_fd, "R\n");
1815 break;
1816 default:
1817 break;
1820 if (result <= 0)
1821 break;
1824 if (result > 0)
1826 playlist->num_cached = 0;
1827 playlist->pending_control_sync = true;
1829 result = 0;
1831 else
1833 result = -1;
1834 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1837 return result;
1841 * Update control data with new command. Depending on the command, it may be
1842 * cached or flushed to disk.
1844 static int update_control(struct playlist_info* playlist,
1845 enum playlist_command command, int i1, int i2,
1846 const char* s1, const char* s2, void* data)
1848 int result = 0;
1849 struct playlist_control_cache* cache;
1850 bool flush = false;
1852 mutex_lock(playlist->control_mutex);
1854 cache = &(playlist->control_cache[playlist->num_cached++]);
1856 cache->command = command;
1857 cache->i1 = i1;
1858 cache->i2 = i2;
1859 cache->s1 = s1;
1860 cache->s2 = s2;
1861 cache->data = data;
1863 switch (command)
1865 case PLAYLIST_COMMAND_PLAYLIST:
1866 case PLAYLIST_COMMAND_ADD:
1867 case PLAYLIST_COMMAND_QUEUE:
1868 #ifndef HAVE_DIRCACHE
1869 case PLAYLIST_COMMAND_DELETE:
1870 case PLAYLIST_COMMAND_RESET:
1871 #endif
1872 flush = true;
1873 break;
1874 case PLAYLIST_COMMAND_SHUFFLE:
1875 case PLAYLIST_COMMAND_UNSHUFFLE:
1876 default:
1877 /* only flush when needed */
1878 break;
1881 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1882 result = flush_cached_control(playlist);
1884 mutex_unlock(playlist->control_mutex);
1886 return result;
1890 * sync control file to disk
1892 static void sync_control(struct playlist_info* playlist, bool force)
1894 #ifdef HAVE_DIRCACHE
1895 if (playlist->started && force)
1896 #else
1897 (void) force;
1899 if (playlist->started)
1900 #endif
1902 if (playlist->pending_control_sync)
1904 mutex_lock(playlist->control_mutex);
1905 fsync(playlist->control_fd);
1906 playlist->pending_control_sync = false;
1907 mutex_unlock(playlist->control_mutex);
1913 * Rotate indices such that first_index is index 0
1915 static int rotate_index(const struct playlist_info* playlist, int index)
1917 index -= playlist->first_index;
1918 if (index < 0)
1919 index += playlist->amount;
1921 return index;
1924 static int move_callback(int handle, void* current, void* new)
1926 (void)handle;
1927 struct playlist_info* playlist = &current_playlist;
1928 if (current == playlist->indices)
1929 playlist->indices = new;
1930 else if (current == playlist->buffer)
1931 playlist->buffer = new;
1932 else /* current == playlist->filenames */
1933 playlist->filenames = new;
1935 return BUFLIB_CB_OK;
1938 static struct buflib_callbacks ops = {
1939 .move_callback = move_callback,
1940 .shrink_callback = NULL,
1943 * Initialize playlist entries at startup
1945 void playlist_init(void)
1947 int handle;
1948 struct playlist_info* playlist = &current_playlist;
1950 mutex_init(&current_playlist_mutex);
1951 mutex_init(&created_playlist_mutex);
1953 playlist->current = true;
1954 strlcpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
1955 sizeof(playlist->control_filename));
1956 playlist->fd = -1;
1957 playlist->control_fd = -1;
1958 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1959 handle = core_alloc_ex("playlist idx",
1960 playlist->max_playlist_size * sizeof(int), &ops);
1961 playlist->indices = core_get_data(handle);
1962 playlist->buffer_size =
1963 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1964 handle = core_alloc_ex("playlist buf",
1965 playlist->buffer_size, &ops);
1966 playlist->buffer = core_get_data(handle);
1967 playlist->control_mutex = &current_playlist_mutex;
1969 empty_playlist(playlist, true);
1971 #ifdef HAVE_DIRCACHE
1972 handle = core_alloc_ex("playlist dc",
1973 playlist->max_playlist_size * sizeof(int), &ops);
1974 playlist->filenames = core_get_data(handle);
1975 memset(playlist->filenames, 0xff,
1976 playlist->max_playlist_size * sizeof(int));
1977 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1978 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1979 IF_COP(, CPU));
1980 queue_init(&playlist_queue, true);
1981 #endif
1985 * Clean playlist at shutdown
1987 void playlist_shutdown(void)
1989 struct playlist_info* playlist = &current_playlist;
1991 if (playlist->control_fd >= 0)
1993 mutex_lock(playlist->control_mutex);
1995 if (playlist->num_cached > 0)
1996 flush_cached_control(playlist);
1998 close(playlist->control_fd);
2000 mutex_unlock(playlist->control_mutex);
2005 * Create new playlist
2007 int playlist_create(const char *dir, const char *file)
2009 struct playlist_info* playlist = &current_playlist;
2011 new_playlist(playlist, dir, file);
2013 if (file)
2014 /* load the playlist file */
2015 add_indices_to_playlist(playlist, NULL, 0);
2017 return 0;
2020 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2023 * Restore the playlist state based on control file commands. Called to
2024 * resume playback after shutdown.
2026 int playlist_resume(void)
2028 struct playlist_info* playlist = &current_playlist;
2029 char *buffer;
2030 size_t buflen;
2031 int nread;
2032 int total_read = 0;
2033 int control_file_size = 0;
2034 bool first = true;
2035 bool sorted = true;
2037 /* use mp3 buffer for maximum load speed */
2038 buffer = (char *)audio_get_buffer(false, &buflen);
2040 empty_playlist(playlist, true);
2042 splash(0, ID2P(LANG_WAIT));
2043 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2044 if (playlist->control_fd < 0)
2046 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2047 return -1;
2049 playlist->control_created = true;
2051 control_file_size = filesize(playlist->control_fd);
2052 if (control_file_size <= 0)
2054 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2055 return -1;
2058 /* read a small amount first to get the header */
2059 nread = read(playlist->control_fd, buffer,
2060 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2061 if(nread <= 0)
2063 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2064 return -1;
2067 playlist->started = true;
2069 while (1)
2071 int result = 0;
2072 int count;
2073 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2074 int last_newline = 0;
2075 int str_count = -1;
2076 bool newline = true;
2077 bool exit_loop = false;
2078 char *p = buffer;
2079 char *str1 = NULL;
2080 char *str2 = NULL;
2081 char *str3 = NULL;
2082 unsigned long last_tick = current_tick;
2083 bool useraborted = false;
2085 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
2087 /* So a splash while we are loading. */
2088 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2090 splashf(0, str(LANG_LOADING_PERCENT),
2091 (total_read+count)*100/control_file_size,
2092 str(LANG_OFF_ABORT));
2093 if (action_userabort(TIMEOUT_NOBLOCK))
2095 useraborted = true;
2096 break;
2098 last_tick = current_tick;
2101 /* Are we on a new line? */
2102 if((*p == '\n') || (*p == '\r'))
2104 *p = '\0';
2106 /* save last_newline in case we need to load more data */
2107 last_newline = count;
2109 switch (current_command)
2111 case PLAYLIST_COMMAND_PLAYLIST:
2113 /* str1=version str2=dir str3=file */
2114 int version;
2116 if (!str1)
2118 result = -1;
2119 exit_loop = true;
2120 break;
2123 if (!str2)
2124 str2 = "";
2126 if (!str3)
2127 str3 = "";
2129 version = atoi(str1);
2131 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2132 return -1;
2134 update_playlist_filename(playlist, str2, str3);
2136 if (str3[0] != '\0')
2138 /* NOTE: add_indices_to_playlist() overwrites the
2139 audiobuf so we need to reload control file
2140 data */
2141 add_indices_to_playlist(playlist, NULL, 0);
2143 else if (str2[0] != '\0')
2145 playlist->in_ram = true;
2146 resume_directory(str2);
2149 /* load the rest of the data */
2150 first = false;
2151 exit_loop = true;
2153 break;
2155 case PLAYLIST_COMMAND_ADD:
2156 case PLAYLIST_COMMAND_QUEUE:
2158 /* str1=position str2=last_position str3=file */
2159 int position, last_position;
2160 bool queue;
2162 if (!str1 || !str2 || !str3)
2164 result = -1;
2165 exit_loop = true;
2166 break;
2169 position = atoi(str1);
2170 last_position = atoi(str2);
2172 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2173 false:true;
2175 /* seek position is based on str3's position in
2176 buffer */
2177 if (add_track_to_playlist(playlist, str3, position,
2178 queue, total_read+(str3-buffer)) < 0)
2179 return -1;
2181 playlist->last_insert_pos = last_position;
2183 break;
2185 case PLAYLIST_COMMAND_DELETE:
2187 /* str1=position */
2188 int position;
2190 if (!str1)
2192 result = -1;
2193 exit_loop = true;
2194 break;
2197 position = atoi(str1);
2199 if (remove_track_from_playlist(playlist, position,
2200 false) < 0)
2201 return -1;
2203 break;
2205 case PLAYLIST_COMMAND_SHUFFLE:
2207 /* str1=seed str2=first_index */
2208 int seed;
2210 if (!str1 || !str2)
2212 result = -1;
2213 exit_loop = true;
2214 break;
2217 if (!sorted)
2219 /* Always sort list before shuffling */
2220 sort_playlist(playlist, false, false);
2223 seed = atoi(str1);
2224 playlist->first_index = atoi(str2);
2226 if (randomise_playlist(playlist, seed, false,
2227 false) < 0)
2228 return -1;
2229 sorted = false;
2230 break;
2232 case PLAYLIST_COMMAND_UNSHUFFLE:
2234 /* str1=first_index */
2235 if (!str1)
2237 result = -1;
2238 exit_loop = true;
2239 break;
2242 playlist->first_index = atoi(str1);
2244 if (sort_playlist(playlist, false, false) < 0)
2245 return -1;
2247 sorted = true;
2248 break;
2250 case PLAYLIST_COMMAND_RESET:
2252 playlist->last_insert_pos = -1;
2253 break;
2255 case PLAYLIST_COMMAND_COMMENT:
2256 default:
2257 break;
2260 newline = true;
2262 /* to ignore any extra newlines */
2263 current_command = PLAYLIST_COMMAND_COMMENT;
2265 else if(newline)
2267 newline = false;
2269 /* first non-comment line must always specify playlist */
2270 if (first && *p != 'P' && *p != '#')
2272 result = -1;
2273 exit_loop = true;
2274 break;
2277 switch (*p)
2279 case 'P':
2280 /* playlist can only be specified once */
2281 if (!first)
2283 result = -1;
2284 exit_loop = true;
2285 break;
2288 current_command = PLAYLIST_COMMAND_PLAYLIST;
2289 break;
2290 case 'A':
2291 current_command = PLAYLIST_COMMAND_ADD;
2292 break;
2293 case 'Q':
2294 current_command = PLAYLIST_COMMAND_QUEUE;
2295 break;
2296 case 'D':
2297 current_command = PLAYLIST_COMMAND_DELETE;
2298 break;
2299 case 'S':
2300 current_command = PLAYLIST_COMMAND_SHUFFLE;
2301 break;
2302 case 'U':
2303 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2304 break;
2305 case 'R':
2306 current_command = PLAYLIST_COMMAND_RESET;
2307 break;
2308 case '#':
2309 current_command = PLAYLIST_COMMAND_COMMENT;
2310 break;
2311 default:
2312 result = -1;
2313 exit_loop = true;
2314 break;
2317 str_count = -1;
2318 str1 = NULL;
2319 str2 = NULL;
2320 str3 = NULL;
2322 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2324 /* all control file strings are separated with a colon.
2325 Replace the colon with 0 to get proper strings that can be
2326 used by commands above */
2327 if (*p == ':')
2329 *p = '\0';
2330 str_count++;
2332 if ((count+1) < nread)
2334 switch (str_count)
2336 case 0:
2337 str1 = p+1;
2338 break;
2339 case 1:
2340 str2 = p+1;
2341 break;
2342 case 2:
2343 str3 = p+1;
2344 break;
2345 default:
2346 /* allow last string to contain colons */
2347 *p = ':';
2348 break;
2355 if (result < 0)
2357 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2358 return result;
2361 if (useraborted)
2363 splash(HZ*2, ID2P(LANG_CANCEL));
2364 return -1;
2366 if (!newline || (exit_loop && count<nread))
2368 if ((total_read + count) >= control_file_size)
2370 /* no newline at end of control file */
2371 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2372 return -1;
2375 /* We didn't end on a newline or we exited loop prematurely.
2376 Either way, re-read the remainder. */
2377 count = last_newline;
2378 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2381 total_read += count;
2383 if (first)
2384 /* still looking for header */
2385 nread = read(playlist->control_fd, buffer,
2386 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2387 else
2388 nread = read(playlist->control_fd, buffer, buflen);
2390 /* Terminate on EOF */
2391 if(nread <= 0)
2393 break;
2397 #ifdef HAVE_DIRCACHE
2398 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2399 #endif
2401 return 0;
2405 * Add track to in_ram playlist. Used when playing directories.
2407 int playlist_add(const char *filename)
2409 struct playlist_info* playlist = &current_playlist;
2410 int len = strlen(filename);
2412 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2413 (playlist->amount >= playlist->max_playlist_size))
2415 display_buffer_full();
2416 return -1;
2419 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2420 #ifdef HAVE_DIRCACHE
2421 playlist->filenames[playlist->amount] = -1;
2422 #endif
2423 playlist->amount++;
2425 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2426 playlist->buffer_end_pos += len;
2427 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2429 return 0;
2432 /* shuffle newly created playlist using random seed. */
2433 int playlist_shuffle(int random_seed, int start_index)
2435 struct playlist_info* playlist = &current_playlist;
2437 bool start_current = false;
2439 if (start_index >= 0 && global_settings.play_selected)
2441 /* store the seek position before the shuffle */
2442 playlist->index = playlist->first_index = start_index;
2443 start_current = true;
2446 randomise_playlist(playlist, random_seed, start_current, true);
2448 return playlist->index;
2451 /* start playing current playlist at specified index/offset */
2452 void playlist_start(int start_index, int offset)
2454 struct playlist_info* playlist = &current_playlist;
2456 /* Cancel FM radio selection as previous music. For cases where we start
2457 playback without going to the WPS, such as playlist insert.. or
2458 playlist catalog. */
2459 previous_music_is_wps();
2461 playlist->index = start_index;
2463 playlist->started = true;
2464 sync_control(playlist, false);
2465 audio_play(offset);
2468 /* Returns false if 'steps' is out of bounds, else true */
2469 bool playlist_check(int steps)
2471 struct playlist_info* playlist = &current_playlist;
2473 /* always allow folder navigation */
2474 if (global_settings.next_folder && playlist->in_ram)
2475 return true;
2477 int index = get_next_index(playlist, steps, -1);
2479 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2480 index = get_next_index(playlist, steps, REPEAT_ALL);
2482 return (index >= 0);
2485 /* get trackname of track that is "steps" away from current playing track.
2486 NULL is used to identify end of playlist */
2487 const char* playlist_peek(int steps, char* buf, size_t buf_size)
2489 struct playlist_info* playlist = &current_playlist;
2490 int seek;
2491 char *temp_ptr;
2492 int index;
2493 bool control_file;
2495 index = get_next_index(playlist, steps, -1);
2496 if (index < 0)
2497 return NULL;
2499 #if CONFIG_CODEC == SWCODEC
2500 /* Just testing - don't care about the file name */
2501 if (!buf || !buf_size)
2502 return "";
2503 #endif
2505 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2506 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2508 if (get_filename(playlist, index, seek, control_file, buf,
2509 buf_size) < 0)
2510 return NULL;
2512 temp_ptr = buf;
2514 if (!playlist->in_ram || control_file)
2516 /* remove bogus dirs from beginning of path
2517 (workaround for buggy playlist creation tools) */
2518 while (temp_ptr)
2520 if (file_exists(temp_ptr))
2521 break;
2523 temp_ptr = strchr(temp_ptr+1, '/');
2526 if (!temp_ptr)
2528 /* Even though this is an invalid file, we still need to pass a
2529 file name to the caller because NULL is used to indicate end
2530 of playlist */
2531 return buf;
2535 return temp_ptr;
2539 * Update indices as track has changed
2541 int playlist_next(int steps)
2543 struct playlist_info* playlist = &current_playlist;
2544 int index;
2546 if ( (steps > 0)
2547 #ifdef AB_REPEAT_ENABLE
2548 && (global_settings.repeat_mode != REPEAT_AB)
2549 #endif
2550 && (global_settings.repeat_mode != REPEAT_ONE) )
2552 int i, j;
2554 /* We need to delete all the queued songs */
2555 for (i=0, j=steps; i<j; i++)
2557 index = get_next_index(playlist, i, -1);
2559 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2561 remove_track_from_playlist(playlist, index, true);
2562 steps--; /* one less track */
2567 index = get_next_index(playlist, steps, -1);
2569 if (index < 0)
2571 /* end of playlist... or is it */
2572 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2573 playlist->amount > 1)
2575 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2576 playlist->first_index = 0;
2577 sort_playlist(playlist, false, false);
2578 randomise_playlist(playlist, current_tick, false, true);
2580 #if CONFIG_CODEC == SWCODEC
2581 playlist->started = true;
2582 #else
2583 playlist_start(0, 0);
2584 #endif
2585 playlist->index = 0;
2586 index = 0;
2588 else if (playlist->in_ram && global_settings.next_folder)
2590 index = create_and_play_dir(steps, true);
2592 if (index >= 0)
2594 playlist->index = index;
2598 return index;
2601 playlist->index = index;
2603 if (playlist->last_insert_pos >= 0 && steps > 0)
2605 /* check to see if we've gone beyond the last inserted track */
2606 int cur = rotate_index(playlist, index);
2607 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2609 if (cur > last_pos)
2611 /* reset last inserted track */
2612 playlist->last_insert_pos = -1;
2614 if (playlist->control_fd >= 0)
2616 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2617 -1, -1, NULL, NULL, NULL);
2619 if (result < 0)
2620 return result;
2622 sync_control(playlist, false);
2627 return index;
2630 /* try playing next or previous folder */
2631 bool playlist_next_dir(int direction)
2633 /* not to mess up real playlists */
2634 if(!current_playlist.in_ram)
2635 return false;
2637 return create_and_play_dir(direction, false) >= 0;
2640 /* Get resume info for current playing song. If return value is -1 then
2641 settings shouldn't be saved. */
2642 int playlist_get_resume_info(int *resume_index)
2644 struct playlist_info* playlist = &current_playlist;
2646 *resume_index = playlist->index;
2648 return 0;
2651 /* Update resume info for current playing song. Returns -1 on error. */
2652 int playlist_update_resume_info(const struct mp3entry* id3)
2654 struct playlist_info* playlist = &current_playlist;
2656 if (id3)
2658 if (global_status.resume_index != playlist->index ||
2659 global_status.resume_offset != id3->offset)
2661 global_status.resume_index = playlist->index;
2662 global_status.resume_offset = id3->offset;
2663 status_save();
2666 else
2668 global_status.resume_index = -1;
2669 global_status.resume_offset = -1;
2670 status_save();
2673 return 0;
2676 /* Returns index of current playing track for display purposes. This value
2677 should not be used for resume purposes as it doesn't represent the actual
2678 index into the playlist */
2679 int playlist_get_display_index(void)
2681 struct playlist_info* playlist = &current_playlist;
2683 /* first_index should always be index 0 for display purposes */
2684 int index = rotate_index(playlist, playlist->index);
2686 return (index+1);
2689 /* returns number of tracks in current playlist */
2690 int playlist_amount(void)
2692 return playlist_amount_ex(NULL);
2694 /* set playlist->last_shuffle_start to playlist->amount for
2695 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2696 void playlist_set_last_shuffled_start(void)
2698 struct playlist_info* playlist = &current_playlist;
2699 playlist->last_shuffled_start = playlist->amount;
2702 * Create a new playlist If playlist is not NULL then we're loading a
2703 * playlist off disk for viewing/editing. The index_buffer is used to store
2704 * playlist indices (required for and only used if !current playlist). The
2705 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2707 int playlist_create_ex(struct playlist_info* playlist,
2708 const char* dir, const char* file,
2709 void* index_buffer, int index_buffer_size,
2710 void* temp_buffer, int temp_buffer_size)
2712 if (!playlist)
2713 playlist = &current_playlist;
2714 else
2716 /* Initialize playlist structure */
2717 int r = rand() % 10;
2718 playlist->current = false;
2720 /* Use random name for control file */
2721 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2722 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2723 playlist->fd = -1;
2724 playlist->control_fd = -1;
2726 if (index_buffer)
2728 int num_indices = index_buffer_size / sizeof(int);
2730 #ifdef HAVE_DIRCACHE
2731 num_indices /= 2;
2732 #endif
2733 if (num_indices > global_settings.max_files_in_playlist)
2734 num_indices = global_settings.max_files_in_playlist;
2736 playlist->max_playlist_size = num_indices;
2737 playlist->indices = index_buffer;
2738 #ifdef HAVE_DIRCACHE
2739 playlist->filenames = (int*)&playlist->indices[num_indices];
2740 #endif
2742 else
2744 playlist->max_playlist_size = current_playlist.max_playlist_size;
2745 playlist->indices = current_playlist.indices;
2746 #ifdef HAVE_DIRCACHE
2747 playlist->filenames = current_playlist.filenames;
2748 #endif
2751 playlist->buffer_size = 0;
2752 playlist->buffer = NULL;
2753 playlist->control_mutex = &created_playlist_mutex;
2756 new_playlist(playlist, dir, file);
2758 if (file)
2759 /* load the playlist file */
2760 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2762 return 0;
2766 * Set the specified playlist as the current.
2767 * NOTE: You will get undefined behaviour if something is already playing so
2768 * remember to stop before calling this. Also, this call will
2769 * effectively close your playlist, making it unusable.
2771 int playlist_set_current(struct playlist_info* playlist)
2773 if (!playlist || (check_control(playlist) < 0))
2774 return -1;
2776 empty_playlist(&current_playlist, false);
2778 strlcpy(current_playlist.filename, playlist->filename,
2779 sizeof(current_playlist.filename));
2781 current_playlist.utf8 = playlist->utf8;
2782 current_playlist.fd = playlist->fd;
2784 close(playlist->control_fd);
2785 close(current_playlist.control_fd);
2786 remove(current_playlist.control_filename);
2787 if (rename(playlist->control_filename,
2788 current_playlist.control_filename) < 0)
2789 return -1;
2790 current_playlist.control_fd = open(current_playlist.control_filename,
2791 O_RDWR);
2792 if (current_playlist.control_fd < 0)
2793 return -1;
2794 current_playlist.control_created = true;
2796 current_playlist.dirlen = playlist->dirlen;
2798 if (playlist->indices && playlist->indices != current_playlist.indices)
2800 memcpy(current_playlist.indices, playlist->indices,
2801 playlist->max_playlist_size*sizeof(int));
2802 #ifdef HAVE_DIRCACHE
2803 memcpy(current_playlist.filenames, playlist->filenames,
2804 playlist->max_playlist_size*sizeof(int));
2805 #endif
2808 current_playlist.first_index = playlist->first_index;
2809 current_playlist.amount = playlist->amount;
2810 current_playlist.last_insert_pos = playlist->last_insert_pos;
2811 current_playlist.seed = playlist->seed;
2812 current_playlist.shuffle_modified = playlist->shuffle_modified;
2813 current_playlist.deleted = playlist->deleted;
2814 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2816 memcpy(current_playlist.control_cache, playlist->control_cache,
2817 sizeof(current_playlist.control_cache));
2818 current_playlist.num_cached = playlist->num_cached;
2819 current_playlist.pending_control_sync = playlist->pending_control_sync;
2821 return 0;
2823 struct playlist_info *playlist_get_current(void)
2825 return &current_playlist;
2828 * Close files and delete control file for non-current playlist.
2830 void playlist_close(struct playlist_info* playlist)
2832 if (!playlist)
2833 return;
2835 if (playlist->fd >= 0)
2836 close(playlist->fd);
2838 if (playlist->control_fd >= 0)
2839 close(playlist->control_fd);
2841 if (playlist->control_created)
2842 remove(playlist->control_filename);
2845 void playlist_sync(struct playlist_info* playlist)
2847 if (!playlist)
2848 playlist = &current_playlist;
2850 sync_control(playlist, false);
2851 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2852 audio_flush_and_reload_tracks();
2854 #ifdef HAVE_DIRCACHE
2855 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2856 #endif
2860 * Insert track into playlist at specified position (or one of the special
2861 * positions). Returns position where track was inserted or -1 if error.
2863 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2864 int position, bool queue, bool sync)
2866 int result;
2868 if (!playlist)
2869 playlist = &current_playlist;
2871 if (check_control(playlist) < 0)
2873 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2874 return -1;
2877 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2879 /* Check if we want manually sync later. For example when adding
2880 * bunch of files from tagcache, syncing after every file wouldn't be
2881 * a good thing to do. */
2882 if (sync && result >= 0)
2883 playlist_sync(playlist);
2885 return result;
2889 * Insert all tracks from specified directory into playlist.
2891 int playlist_insert_directory(struct playlist_info* playlist,
2892 const char *dirname, int position, bool queue,
2893 bool recurse)
2895 int result;
2896 unsigned char *count_str;
2897 struct directory_search_context context;
2899 if (!playlist)
2900 playlist = &current_playlist;
2902 if (check_control(playlist) < 0)
2904 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2905 return -1;
2908 if (position == PLAYLIST_REPLACE)
2910 if (playlist_remove_all_tracks(playlist) == 0)
2911 position = PLAYLIST_INSERT_LAST;
2912 else
2913 return -1;
2916 if (queue)
2917 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2918 else
2919 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2921 display_playlist_count(0, count_str, false);
2923 context.playlist = playlist;
2924 context.position = position;
2925 context.queue = queue;
2926 context.count = 0;
2928 cpu_boost(true);
2930 result = playlist_directory_tracksearch(dirname, recurse,
2931 directory_search_callback, &context);
2933 sync_control(playlist, false);
2935 cpu_boost(false);
2937 display_playlist_count(context.count, count_str, true);
2939 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2940 audio_flush_and_reload_tracks();
2942 #ifdef HAVE_DIRCACHE
2943 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2944 #endif
2946 return result;
2950 * Insert all tracks from specified playlist into dynamic playlist.
2952 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
2953 int position, bool queue)
2955 int fd;
2956 int max;
2957 char *temp_ptr;
2958 const char *dir;
2959 unsigned char *count_str;
2960 char temp_buf[MAX_PATH+1];
2961 char trackname[MAX_PATH+1];
2962 int count = 0;
2963 int result = 0;
2964 bool utf8 = is_m3u8(filename);
2966 if (!playlist)
2967 playlist = &current_playlist;
2969 if (check_control(playlist) < 0)
2971 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2972 return -1;
2975 fd = open_utf8(filename, O_RDONLY);
2976 if (fd < 0)
2978 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2979 return -1;
2982 /* we need the directory name for formatting purposes */
2983 dir = filename;
2985 temp_ptr = strrchr(filename+1,'/');
2986 if (temp_ptr)
2987 *temp_ptr = 0;
2988 else
2989 dir = "/";
2991 if (queue)
2992 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2993 else
2994 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2996 display_playlist_count(count, count_str, false);
2998 if (position == PLAYLIST_REPLACE)
3000 if (playlist_remove_all_tracks(playlist) == 0)
3001 position = PLAYLIST_INSERT_LAST;
3002 else return -1;
3005 cpu_boost(true);
3007 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3009 /* user abort */
3010 if (action_userabort(TIMEOUT_NOBLOCK))
3011 break;
3013 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3015 int insert_pos;
3017 if (!utf8)
3019 /* Use trackname as a temporay buffer. Note that trackname must
3020 * be as large as temp_buf.
3022 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3025 /* we need to format so that relative paths are correctly
3026 handled */
3027 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3028 dir) < 0)
3030 result = -1;
3031 break;
3034 insert_pos = add_track_to_playlist(playlist, trackname, position,
3035 queue, -1);
3037 if (insert_pos < 0)
3039 result = -1;
3040 break;
3043 /* Make sure tracks are inserted in correct order if user
3044 requests INSERT_FIRST */
3045 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3046 position = insert_pos + 1;
3048 count++;
3050 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3052 display_playlist_count(count, count_str, false);
3054 if (count == PLAYLIST_DISPLAY_COUNT &&
3055 (audio_status() & AUDIO_STATUS_PLAY) &&
3056 playlist->started)
3057 audio_flush_and_reload_tracks();
3061 /* let the other threads work */
3062 yield();
3065 close(fd);
3067 if (temp_ptr)
3068 *temp_ptr = '/';
3070 sync_control(playlist, false);
3072 cpu_boost(false);
3074 display_playlist_count(count, count_str, true);
3076 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3077 audio_flush_and_reload_tracks();
3079 #ifdef HAVE_DIRCACHE
3080 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3081 #endif
3083 return result;
3087 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3088 * we want to delete the current playing track.
3090 int playlist_delete(struct playlist_info* playlist, int index)
3092 int result = 0;
3094 if (!playlist)
3095 playlist = &current_playlist;
3097 if (check_control(playlist) < 0)
3099 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3100 return -1;
3103 if (index == PLAYLIST_DELETE_CURRENT)
3104 index = playlist->index;
3106 result = remove_track_from_playlist(playlist, index, true);
3108 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3109 playlist->started)
3110 audio_flush_and_reload_tracks();
3112 return result;
3116 * Move track at index to new_index. Tracks between the two are shifted
3117 * appropriately. Returns 0 on success and -1 on failure.
3119 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3121 int result;
3122 int seek;
3123 bool control_file;
3124 bool queue;
3125 bool current = false;
3126 int r;
3127 char filename[MAX_PATH];
3129 if (!playlist)
3130 playlist = &current_playlist;
3132 if (check_control(playlist) < 0)
3134 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3135 return -1;
3138 if (index == new_index)
3139 return -1;
3141 if (index == playlist->index)
3142 /* Moving the current track */
3143 current = true;
3145 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3146 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3147 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3149 if (get_filename(playlist, index, seek, control_file, filename,
3150 sizeof(filename)) < 0)
3151 return -1;
3153 /* We want to insert the track at the position that was specified by
3154 new_index. This may be different then new_index because of the
3155 shifting that will occur after the delete.
3156 We calculate this before we do the remove as it depends on the
3157 size of the playlist before the track removal */
3158 r = rotate_index(playlist, new_index);
3160 /* Delete track from original position */
3161 result = remove_track_from_playlist(playlist, index, true);
3163 if (result != -1)
3165 if (r == 0)
3166 /* First index */
3167 new_index = PLAYLIST_PREPEND;
3168 else if (r == playlist->amount)
3169 /* Append */
3170 new_index = PLAYLIST_INSERT_LAST;
3171 else
3172 /* Calculate index of desired position */
3173 new_index = (r+playlist->first_index)%playlist->amount;
3175 result = add_track_to_playlist(playlist, filename, new_index, queue,
3176 -1);
3178 if (result != -1)
3180 if (current)
3182 /* Moved the current track */
3183 switch (new_index)
3185 case PLAYLIST_PREPEND:
3186 playlist->index = playlist->first_index;
3187 break;
3188 case PLAYLIST_INSERT_LAST:
3189 playlist->index = playlist->first_index - 1;
3190 if (playlist->index < 0)
3191 playlist->index += playlist->amount;
3192 break;
3193 default:
3194 playlist->index = new_index;
3195 break;
3199 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3200 audio_flush_and_reload_tracks();
3204 #ifdef HAVE_DIRCACHE
3205 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3206 #endif
3208 return result;
3211 /* shuffle currently playing playlist */
3212 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3213 bool start_current)
3215 int result;
3217 if (!playlist)
3218 playlist = &current_playlist;
3220 check_control(playlist);
3222 result = randomise_playlist(playlist, seed, start_current, true);
3224 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3225 playlist->started)
3226 audio_flush_and_reload_tracks();
3228 return result;
3231 /* sort currently playing playlist */
3232 int playlist_sort(struct playlist_info* playlist, bool start_current)
3234 int result;
3236 if (!playlist)
3237 playlist = &current_playlist;
3239 check_control(playlist);
3241 result = sort_playlist(playlist, start_current, true);
3243 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3244 playlist->started)
3245 audio_flush_and_reload_tracks();
3247 return result;
3250 /* returns true if playlist has been modified */
3251 bool playlist_modified(const struct playlist_info* playlist)
3253 if (!playlist)
3254 playlist = &current_playlist;
3256 if (playlist->shuffle_modified ||
3257 playlist->deleted ||
3258 playlist->num_inserted_tracks > 0)
3259 return true;
3261 return false;
3264 /* returns index of first track in playlist */
3265 int playlist_get_first_index(const struct playlist_info* playlist)
3267 if (!playlist)
3268 playlist = &current_playlist;
3270 return playlist->first_index;
3273 /* returns shuffle seed of playlist */
3274 int playlist_get_seed(const struct playlist_info* playlist)
3276 if (!playlist)
3277 playlist = &current_playlist;
3279 return playlist->seed;
3282 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3283 int playlist_amount_ex(const struct playlist_info* playlist)
3285 if (!playlist)
3286 playlist = &current_playlist;
3288 return playlist->amount;
3291 /* returns full path of playlist (minus extension) */
3292 char *playlist_name(const struct playlist_info* playlist, char *buf,
3293 int buf_size)
3295 char *sep;
3297 if (!playlist)
3298 playlist = &current_playlist;
3300 strlcpy(buf, playlist->filename+playlist->dirlen, buf_size);
3302 if (!buf[0])
3303 return NULL;
3305 /* Remove extension */
3306 sep = strrchr(buf, '.');
3307 if (sep)
3308 *sep = 0;
3310 return buf;
3313 /* returns the playlist filename */
3314 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3315 int buf_size)
3317 if (!playlist)
3318 playlist = &current_playlist;
3320 strlcpy(buf, playlist->filename, buf_size);
3322 if (!buf[0])
3323 return NULL;
3325 return buf;
3328 /* Fills info structure with information about track at specified index.
3329 Returns 0 on success and -1 on failure */
3330 int playlist_get_track_info(struct playlist_info* playlist, int index,
3331 struct playlist_track_info* info)
3333 int seek;
3334 bool control_file;
3336 if (!playlist)
3337 playlist = &current_playlist;
3339 if (index < 0 || index >= playlist->amount)
3340 return -1;
3342 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3343 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3345 if (get_filename(playlist, index, seek, control_file, info->filename,
3346 sizeof(info->filename)) < 0)
3347 return -1;
3349 info->attr = 0;
3351 if (control_file)
3353 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3354 info->attr |= PLAYLIST_ATTR_QUEUED;
3355 else
3356 info->attr |= PLAYLIST_ATTR_INSERTED;
3360 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3361 info->attr |= PLAYLIST_ATTR_SKIPPED;
3363 info->index = index;
3364 info->display_index = rotate_index(playlist, index) + 1;
3366 return 0;
3369 /* save the current dynamic playlist to specified file */
3370 int playlist_save(struct playlist_info* playlist, char *filename)
3372 int fd;
3373 int i, index;
3374 int count = 0;
3375 char path[MAX_PATH+1];
3376 char tmp_buf[MAX_PATH+1];
3377 int result = 0;
3378 bool overwrite_current = false;
3379 char* old_buffer = NULL;
3380 size_t old_buffer_size = 0;
3382 if (!playlist)
3383 playlist = &current_playlist;
3385 if (playlist->amount <= 0)
3386 return -1;
3388 /* use current working directory as base for pathname */
3389 if (format_track_path(path, filename, sizeof(tmp_buf),
3390 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3391 return -1;
3393 if (!strncmp(playlist->filename, path, strlen(path)))
3395 /* Attempting to overwrite current playlist file.*/
3397 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3399 /* not enough buffer space to store updated indices */
3400 /* Try to get a buffer */
3401 old_buffer = playlist->buffer;
3402 old_buffer_size = playlist->buffer_size;
3403 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size);
3404 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3406 playlist->buffer = old_buffer;
3407 playlist->buffer_size = old_buffer_size;
3408 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3409 return -1;
3413 /* use temporary pathname */
3414 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3415 overwrite_current = true;
3418 if (is_m3u8(path))
3420 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
3422 else
3424 /* some applications require a BOM to read the file properly */
3425 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
3427 if (fd < 0)
3429 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3430 if (old_buffer != NULL)
3432 playlist->buffer = old_buffer;
3433 playlist->buffer_size = old_buffer_size;
3435 return -1;
3438 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3440 cpu_boost(true);
3442 index = playlist->first_index;
3443 for (i=0; i<playlist->amount; i++)
3445 bool control_file;
3446 bool queue;
3447 int seek;
3449 /* user abort */
3450 if (action_userabort(TIMEOUT_NOBLOCK))
3452 result = -1;
3453 break;
3456 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3457 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3458 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3460 /* Don't save queued files */
3461 if (!queue)
3463 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3464 MAX_PATH+1) < 0)
3466 result = -1;
3467 break;
3470 if (overwrite_current)
3471 playlist->seek_buf[count] = lseek(fd, 0, SEEK_CUR);
3473 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3475 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3476 result = -1;
3477 break;
3480 count++;
3482 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3483 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3484 false);
3486 yield();
3489 index = (index+1)%playlist->amount;
3492 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3494 close(fd);
3496 if (overwrite_current && result >= 0)
3498 result = -1;
3500 mutex_lock(playlist->control_mutex);
3502 /* Replace the current playlist with the new one and update indices */
3503 close(playlist->fd);
3504 if (remove(playlist->filename) >= 0)
3506 if (rename(path, playlist->filename) >= 0)
3508 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
3509 if (playlist->fd >= 0)
3511 index = playlist->first_index;
3512 for (i=0, count=0; i<playlist->amount; i++)
3514 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3516 playlist->indices[index] = playlist->seek_buf[count];
3517 count++;
3519 index = (index+1)%playlist->amount;
3522 /* we need to recreate control because inserted tracks are
3523 now part of the playlist and shuffle has been
3524 invalidated */
3525 result = recreate_control(playlist);
3530 mutex_unlock(playlist->control_mutex);
3534 cpu_boost(false);
3535 if (old_buffer != NULL)
3537 playlist->buffer = old_buffer;
3538 playlist->buffer_size = old_buffer_size;
3541 return result;
3545 * Search specified directory for tracks and notify via callback. May be
3546 * called recursively.
3548 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3549 int (*callback)(char*, void*),
3550 void* context)
3552 char buf[MAX_PATH+1];
3553 int result = 0;
3554 int num_files = 0;
3555 int i;
3556 struct entry *files;
3557 struct tree_context* tc = tree_get_context();
3558 int old_dirfilter = *(tc->dirfilter);
3560 if (!callback)
3561 return -1;
3563 /* use the tree browser dircache to load files */
3564 *(tc->dirfilter) = SHOW_ALL;
3566 if (ft_load(tc, dirname) < 0)
3568 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3569 *(tc->dirfilter) = old_dirfilter;
3570 return -1;
3573 files = tc->cache.entries;
3574 num_files = tc->filesindir;
3576 /* we've overwritten the dircache so tree browser will need to be
3577 reloaded */
3578 reload_directory();
3580 for (i=0; i<num_files; i++)
3582 /* user abort */
3583 if (action_userabort(TIMEOUT_NOBLOCK))
3585 result = -1;
3586 break;
3589 if (files[i].attr & ATTR_DIRECTORY)
3591 if (recurse)
3593 /* recursively add directories */
3594 snprintf(buf, sizeof(buf), "%s/%s",
3595 dirname[1]? dirname: "", files[i].name);
3596 result = playlist_directory_tracksearch(buf, recurse,
3597 callback, context);
3598 if (result < 0)
3599 break;
3601 /* we now need to reload our current directory */
3602 if(ft_load(tc, dirname) < 0)
3604 result = -1;
3605 break;
3608 files = tc->cache.entries;
3609 num_files = tc->filesindir;
3610 if (!num_files)
3612 result = -1;
3613 break;
3616 else
3617 continue;
3619 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3621 snprintf(buf, sizeof(buf), "%s/%s",
3622 dirname[1]? dirname: "", files[i].name);
3624 if (callback(buf, context) != 0)
3626 result = -1;
3627 break;
3630 /* let the other threads work */
3631 yield();
3635 /* restore dirfilter */
3636 *(tc->dirfilter) = old_dirfilter;
3638 return result;