Replace all direct accesses to audiobuf with buffer API functions.
[kugel-rb.git] / apps / playlist.c
blob8334260242aef2f457a3146e6cd1be5678a46bc7
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 "buffer.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;
1925 * Initialize playlist entries at startup
1927 void playlist_init(void)
1929 struct playlist_info* playlist = &current_playlist;
1931 mutex_init(&current_playlist_mutex);
1932 mutex_init(&created_playlist_mutex);
1934 playlist->current = true;
1935 strlcpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
1936 sizeof(playlist->control_filename));
1937 playlist->fd = -1;
1938 playlist->control_fd = -1;
1939 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1940 playlist->indices = buffer_alloc(
1941 playlist->max_playlist_size * sizeof(int));
1942 playlist->buffer_size =
1943 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1944 playlist->buffer = buffer_alloc(playlist->buffer_size);
1945 playlist->control_mutex = &current_playlist_mutex;
1947 empty_playlist(playlist, true);
1949 #ifdef HAVE_DIRCACHE
1950 playlist->filenames = buffer_alloc(
1951 playlist->max_playlist_size * sizeof(int));
1952 memset(playlist->filenames, 0xff,
1953 playlist->max_playlist_size * sizeof(int));
1954 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1955 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1956 IF_COP(, CPU));
1957 queue_init(&playlist_queue, true);
1958 #endif
1962 * Clean playlist at shutdown
1964 void playlist_shutdown(void)
1966 struct playlist_info* playlist = &current_playlist;
1968 if (playlist->control_fd >= 0)
1970 mutex_lock(playlist->control_mutex);
1972 if (playlist->num_cached > 0)
1973 flush_cached_control(playlist);
1975 close(playlist->control_fd);
1977 mutex_unlock(playlist->control_mutex);
1982 * Create new playlist
1984 int playlist_create(const char *dir, const char *file)
1986 struct playlist_info* playlist = &current_playlist;
1988 new_playlist(playlist, dir, file);
1990 if (file)
1991 /* load the playlist file */
1992 add_indices_to_playlist(playlist, NULL, 0);
1994 return 0;
1997 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2000 * Restore the playlist state based on control file commands. Called to
2001 * resume playback after shutdown.
2003 int playlist_resume(void)
2005 struct playlist_info* playlist = &current_playlist;
2006 char *buffer;
2007 size_t buflen;
2008 int nread;
2009 int total_read = 0;
2010 int control_file_size = 0;
2011 bool first = true;
2012 bool sorted = true;
2014 /* use mp3 buffer for maximum load speed */
2015 buffer = (char *)audio_get_buffer(false, &buflen);
2017 empty_playlist(playlist, true);
2019 splash(0, ID2P(LANG_WAIT));
2020 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2021 if (playlist->control_fd < 0)
2023 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2024 return -1;
2026 playlist->control_created = true;
2028 control_file_size = filesize(playlist->control_fd);
2029 if (control_file_size <= 0)
2031 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2032 return -1;
2035 /* read a small amount first to get the header */
2036 nread = read(playlist->control_fd, buffer,
2037 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2038 if(nread <= 0)
2040 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2041 return -1;
2044 playlist->started = true;
2046 while (1)
2048 int result = 0;
2049 int count;
2050 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2051 int last_newline = 0;
2052 int str_count = -1;
2053 bool newline = true;
2054 bool exit_loop = false;
2055 char *p = buffer;
2056 char *str1 = NULL;
2057 char *str2 = NULL;
2058 char *str3 = NULL;
2059 unsigned long last_tick = current_tick;
2060 bool useraborted = false;
2062 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
2064 /* So a splash while we are loading. */
2065 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2067 splashf(0, str(LANG_LOADING_PERCENT),
2068 (total_read+count)*100/control_file_size,
2069 str(LANG_OFF_ABORT));
2070 if (action_userabort(TIMEOUT_NOBLOCK))
2072 useraborted = true;
2073 break;
2075 last_tick = current_tick;
2078 /* Are we on a new line? */
2079 if((*p == '\n') || (*p == '\r'))
2081 *p = '\0';
2083 /* save last_newline in case we need to load more data */
2084 last_newline = count;
2086 switch (current_command)
2088 case PLAYLIST_COMMAND_PLAYLIST:
2090 /* str1=version str2=dir str3=file */
2091 int version;
2093 if (!str1)
2095 result = -1;
2096 exit_loop = true;
2097 break;
2100 if (!str2)
2101 str2 = "";
2103 if (!str3)
2104 str3 = "";
2106 version = atoi(str1);
2108 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2109 return -1;
2111 update_playlist_filename(playlist, str2, str3);
2113 if (str3[0] != '\0')
2115 /* NOTE: add_indices_to_playlist() overwrites the
2116 audiobuf so we need to reload control file
2117 data */
2118 add_indices_to_playlist(playlist, NULL, 0);
2120 else if (str2[0] != '\0')
2122 playlist->in_ram = true;
2123 resume_directory(str2);
2126 /* load the rest of the data */
2127 first = false;
2128 exit_loop = true;
2130 break;
2132 case PLAYLIST_COMMAND_ADD:
2133 case PLAYLIST_COMMAND_QUEUE:
2135 /* str1=position str2=last_position str3=file */
2136 int position, last_position;
2137 bool queue;
2139 if (!str1 || !str2 || !str3)
2141 result = -1;
2142 exit_loop = true;
2143 break;
2146 position = atoi(str1);
2147 last_position = atoi(str2);
2149 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2150 false:true;
2152 /* seek position is based on str3's position in
2153 buffer */
2154 if (add_track_to_playlist(playlist, str3, position,
2155 queue, total_read+(str3-buffer)) < 0)
2156 return -1;
2158 playlist->last_insert_pos = last_position;
2160 break;
2162 case PLAYLIST_COMMAND_DELETE:
2164 /* str1=position */
2165 int position;
2167 if (!str1)
2169 result = -1;
2170 exit_loop = true;
2171 break;
2174 position = atoi(str1);
2176 if (remove_track_from_playlist(playlist, position,
2177 false) < 0)
2178 return -1;
2180 break;
2182 case PLAYLIST_COMMAND_SHUFFLE:
2184 /* str1=seed str2=first_index */
2185 int seed;
2187 if (!str1 || !str2)
2189 result = -1;
2190 exit_loop = true;
2191 break;
2194 if (!sorted)
2196 /* Always sort list before shuffling */
2197 sort_playlist(playlist, false, false);
2200 seed = atoi(str1);
2201 playlist->first_index = atoi(str2);
2203 if (randomise_playlist(playlist, seed, false,
2204 false) < 0)
2205 return -1;
2206 sorted = false;
2207 break;
2209 case PLAYLIST_COMMAND_UNSHUFFLE:
2211 /* str1=first_index */
2212 if (!str1)
2214 result = -1;
2215 exit_loop = true;
2216 break;
2219 playlist->first_index = atoi(str1);
2221 if (sort_playlist(playlist, false, false) < 0)
2222 return -1;
2224 sorted = true;
2225 break;
2227 case PLAYLIST_COMMAND_RESET:
2229 playlist->last_insert_pos = -1;
2230 break;
2232 case PLAYLIST_COMMAND_COMMENT:
2233 default:
2234 break;
2237 newline = true;
2239 /* to ignore any extra newlines */
2240 current_command = PLAYLIST_COMMAND_COMMENT;
2242 else if(newline)
2244 newline = false;
2246 /* first non-comment line must always specify playlist */
2247 if (first && *p != 'P' && *p != '#')
2249 result = -1;
2250 exit_loop = true;
2251 break;
2254 switch (*p)
2256 case 'P':
2257 /* playlist can only be specified once */
2258 if (!first)
2260 result = -1;
2261 exit_loop = true;
2262 break;
2265 current_command = PLAYLIST_COMMAND_PLAYLIST;
2266 break;
2267 case 'A':
2268 current_command = PLAYLIST_COMMAND_ADD;
2269 break;
2270 case 'Q':
2271 current_command = PLAYLIST_COMMAND_QUEUE;
2272 break;
2273 case 'D':
2274 current_command = PLAYLIST_COMMAND_DELETE;
2275 break;
2276 case 'S':
2277 current_command = PLAYLIST_COMMAND_SHUFFLE;
2278 break;
2279 case 'U':
2280 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2281 break;
2282 case 'R':
2283 current_command = PLAYLIST_COMMAND_RESET;
2284 break;
2285 case '#':
2286 current_command = PLAYLIST_COMMAND_COMMENT;
2287 break;
2288 default:
2289 result = -1;
2290 exit_loop = true;
2291 break;
2294 str_count = -1;
2295 str1 = NULL;
2296 str2 = NULL;
2297 str3 = NULL;
2299 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2301 /* all control file strings are separated with a colon.
2302 Replace the colon with 0 to get proper strings that can be
2303 used by commands above */
2304 if (*p == ':')
2306 *p = '\0';
2307 str_count++;
2309 if ((count+1) < nread)
2311 switch (str_count)
2313 case 0:
2314 str1 = p+1;
2315 break;
2316 case 1:
2317 str2 = p+1;
2318 break;
2319 case 2:
2320 str3 = p+1;
2321 break;
2322 default:
2323 /* allow last string to contain colons */
2324 *p = ':';
2325 break;
2332 if (result < 0)
2334 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2335 return result;
2338 if (useraborted)
2340 splash(HZ*2, ID2P(LANG_CANCEL));
2341 return -1;
2343 if (!newline || (exit_loop && count<nread))
2345 if ((total_read + count) >= control_file_size)
2347 /* no newline at end of control file */
2348 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2349 return -1;
2352 /* We didn't end on a newline or we exited loop prematurely.
2353 Either way, re-read the remainder. */
2354 count = last_newline;
2355 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2358 total_read += count;
2360 if (first)
2361 /* still looking for header */
2362 nread = read(playlist->control_fd, buffer,
2363 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2364 else
2365 nread = read(playlist->control_fd, buffer, buflen);
2367 /* Terminate on EOF */
2368 if(nread <= 0)
2370 break;
2374 #ifdef HAVE_DIRCACHE
2375 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2376 #endif
2378 return 0;
2382 * Add track to in_ram playlist. Used when playing directories.
2384 int playlist_add(const char *filename)
2386 struct playlist_info* playlist = &current_playlist;
2387 int len = strlen(filename);
2389 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2390 (playlist->amount >= playlist->max_playlist_size))
2392 display_buffer_full();
2393 return -1;
2396 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2397 #ifdef HAVE_DIRCACHE
2398 playlist->filenames[playlist->amount] = -1;
2399 #endif
2400 playlist->amount++;
2402 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2403 playlist->buffer_end_pos += len;
2404 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2406 return 0;
2409 /* shuffle newly created playlist using random seed. */
2410 int playlist_shuffle(int random_seed, int start_index)
2412 struct playlist_info* playlist = &current_playlist;
2414 bool start_current = false;
2416 if (start_index >= 0 && global_settings.play_selected)
2418 /* store the seek position before the shuffle */
2419 playlist->index = playlist->first_index = start_index;
2420 start_current = true;
2423 randomise_playlist(playlist, random_seed, start_current, true);
2425 return playlist->index;
2428 /* start playing current playlist at specified index/offset */
2429 void playlist_start(int start_index, int offset)
2431 struct playlist_info* playlist = &current_playlist;
2433 /* Cancel FM radio selection as previous music. For cases where we start
2434 playback without going to the WPS, such as playlist insert.. or
2435 playlist catalog. */
2436 previous_music_is_wps();
2438 playlist->index = start_index;
2440 playlist->started = true;
2441 sync_control(playlist, false);
2442 audio_play(offset);
2445 /* Returns false if 'steps' is out of bounds, else true */
2446 bool playlist_check(int steps)
2448 struct playlist_info* playlist = &current_playlist;
2450 /* always allow folder navigation */
2451 if (global_settings.next_folder && playlist->in_ram)
2452 return true;
2454 int index = get_next_index(playlist, steps, -1);
2456 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2457 index = get_next_index(playlist, steps, REPEAT_ALL);
2459 return (index >= 0);
2462 /* get trackname of track that is "steps" away from current playing track.
2463 NULL is used to identify end of playlist */
2464 const char* playlist_peek(int steps, char* buf, size_t buf_size)
2466 struct playlist_info* playlist = &current_playlist;
2467 int seek;
2468 char *temp_ptr;
2469 int index;
2470 bool control_file;
2472 index = get_next_index(playlist, steps, -1);
2473 if (index < 0)
2474 return NULL;
2476 #if CONFIG_CODEC == SWCODEC
2477 /* Just testing - don't care about the file name */
2478 if (!buf || !buf_size)
2479 return "";
2480 #endif
2482 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2483 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2485 if (get_filename(playlist, index, seek, control_file, buf,
2486 buf_size) < 0)
2487 return NULL;
2489 temp_ptr = buf;
2491 if (!playlist->in_ram || control_file)
2493 /* remove bogus dirs from beginning of path
2494 (workaround for buggy playlist creation tools) */
2495 while (temp_ptr)
2497 if (file_exists(temp_ptr))
2498 break;
2500 temp_ptr = strchr(temp_ptr+1, '/');
2503 if (!temp_ptr)
2505 /* Even though this is an invalid file, we still need to pass a
2506 file name to the caller because NULL is used to indicate end
2507 of playlist */
2508 return buf;
2512 return temp_ptr;
2516 * Update indices as track has changed
2518 int playlist_next(int steps)
2520 struct playlist_info* playlist = &current_playlist;
2521 int index;
2523 if ( (steps > 0)
2524 #ifdef AB_REPEAT_ENABLE
2525 && (global_settings.repeat_mode != REPEAT_AB)
2526 #endif
2527 && (global_settings.repeat_mode != REPEAT_ONE) )
2529 int i, j;
2531 /* We need to delete all the queued songs */
2532 for (i=0, j=steps; i<j; i++)
2534 index = get_next_index(playlist, i, -1);
2536 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2538 remove_track_from_playlist(playlist, index, true);
2539 steps--; /* one less track */
2544 index = get_next_index(playlist, steps, -1);
2546 if (index < 0)
2548 /* end of playlist... or is it */
2549 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2550 playlist->amount > 1)
2552 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2553 playlist->first_index = 0;
2554 sort_playlist(playlist, false, false);
2555 randomise_playlist(playlist, current_tick, false, true);
2557 #if CONFIG_CODEC == SWCODEC
2558 playlist->started = true;
2559 #else
2560 playlist_start(0, 0);
2561 #endif
2562 playlist->index = 0;
2563 index = 0;
2565 else if (playlist->in_ram && global_settings.next_folder)
2567 index = create_and_play_dir(steps, true);
2569 if (index >= 0)
2571 playlist->index = index;
2575 return index;
2578 playlist->index = index;
2580 if (playlist->last_insert_pos >= 0 && steps > 0)
2582 /* check to see if we've gone beyond the last inserted track */
2583 int cur = rotate_index(playlist, index);
2584 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2586 if (cur > last_pos)
2588 /* reset last inserted track */
2589 playlist->last_insert_pos = -1;
2591 if (playlist->control_fd >= 0)
2593 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2594 -1, -1, NULL, NULL, NULL);
2596 if (result < 0)
2597 return result;
2599 sync_control(playlist, false);
2604 return index;
2607 /* try playing next or previous folder */
2608 bool playlist_next_dir(int direction)
2610 /* not to mess up real playlists */
2611 if(!current_playlist.in_ram)
2612 return false;
2614 return create_and_play_dir(direction, false) >= 0;
2617 /* Get resume info for current playing song. If return value is -1 then
2618 settings shouldn't be saved. */
2619 int playlist_get_resume_info(int *resume_index)
2621 struct playlist_info* playlist = &current_playlist;
2623 *resume_index = playlist->index;
2625 return 0;
2628 /* Update resume info for current playing song. Returns -1 on error. */
2629 int playlist_update_resume_info(const struct mp3entry* id3)
2631 struct playlist_info* playlist = &current_playlist;
2633 if (id3)
2635 if (global_status.resume_index != playlist->index ||
2636 global_status.resume_offset != id3->offset)
2638 global_status.resume_index = playlist->index;
2639 global_status.resume_offset = id3->offset;
2640 status_save();
2643 else
2645 global_status.resume_index = -1;
2646 global_status.resume_offset = -1;
2647 status_save();
2650 return 0;
2653 /* Returns index of current playing track for display purposes. This value
2654 should not be used for resume purposes as it doesn't represent the actual
2655 index into the playlist */
2656 int playlist_get_display_index(void)
2658 struct playlist_info* playlist = &current_playlist;
2660 /* first_index should always be index 0 for display purposes */
2661 int index = rotate_index(playlist, playlist->index);
2663 return (index+1);
2666 /* returns number of tracks in current playlist */
2667 int playlist_amount(void)
2669 return playlist_amount_ex(NULL);
2671 /* set playlist->last_shuffle_start to playlist->amount for
2672 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2673 void playlist_set_last_shuffled_start(void)
2675 struct playlist_info* playlist = &current_playlist;
2676 playlist->last_shuffled_start = playlist->amount;
2679 * Create a new playlist If playlist is not NULL then we're loading a
2680 * playlist off disk for viewing/editing. The index_buffer is used to store
2681 * playlist indices (required for and only used if !current playlist). The
2682 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2684 int playlist_create_ex(struct playlist_info* playlist,
2685 const char* dir, const char* file,
2686 void* index_buffer, int index_buffer_size,
2687 void* temp_buffer, int temp_buffer_size)
2689 if (!playlist)
2690 playlist = &current_playlist;
2691 else
2693 /* Initialize playlist structure */
2694 int r = rand() % 10;
2695 playlist->current = false;
2697 /* Use random name for control file */
2698 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2699 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2700 playlist->fd = -1;
2701 playlist->control_fd = -1;
2703 if (index_buffer)
2705 int num_indices = index_buffer_size / sizeof(int);
2707 #ifdef HAVE_DIRCACHE
2708 num_indices /= 2;
2709 #endif
2710 if (num_indices > global_settings.max_files_in_playlist)
2711 num_indices = global_settings.max_files_in_playlist;
2713 playlist->max_playlist_size = num_indices;
2714 playlist->indices = index_buffer;
2715 #ifdef HAVE_DIRCACHE
2716 playlist->filenames = (int*)&playlist->indices[num_indices];
2717 #endif
2719 else
2721 playlist->max_playlist_size = current_playlist.max_playlist_size;
2722 playlist->indices = current_playlist.indices;
2723 #ifdef HAVE_DIRCACHE
2724 playlist->filenames = current_playlist.filenames;
2725 #endif
2728 playlist->buffer_size = 0;
2729 playlist->buffer = NULL;
2730 playlist->control_mutex = &created_playlist_mutex;
2733 new_playlist(playlist, dir, file);
2735 if (file)
2736 /* load the playlist file */
2737 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2739 return 0;
2743 * Set the specified playlist as the current.
2744 * NOTE: You will get undefined behaviour if something is already playing so
2745 * remember to stop before calling this. Also, this call will
2746 * effectively close your playlist, making it unusable.
2748 int playlist_set_current(struct playlist_info* playlist)
2750 if (!playlist || (check_control(playlist) < 0))
2751 return -1;
2753 empty_playlist(&current_playlist, false);
2755 strlcpy(current_playlist.filename, playlist->filename,
2756 sizeof(current_playlist.filename));
2758 current_playlist.utf8 = playlist->utf8;
2759 current_playlist.fd = playlist->fd;
2761 close(playlist->control_fd);
2762 close(current_playlist.control_fd);
2763 remove(current_playlist.control_filename);
2764 if (rename(playlist->control_filename,
2765 current_playlist.control_filename) < 0)
2766 return -1;
2767 current_playlist.control_fd = open(current_playlist.control_filename,
2768 O_RDWR);
2769 if (current_playlist.control_fd < 0)
2770 return -1;
2771 current_playlist.control_created = true;
2773 current_playlist.dirlen = playlist->dirlen;
2775 if (playlist->indices && playlist->indices != current_playlist.indices)
2777 memcpy(current_playlist.indices, playlist->indices,
2778 playlist->max_playlist_size*sizeof(int));
2779 #ifdef HAVE_DIRCACHE
2780 memcpy(current_playlist.filenames, playlist->filenames,
2781 playlist->max_playlist_size*sizeof(int));
2782 #endif
2785 current_playlist.first_index = playlist->first_index;
2786 current_playlist.amount = playlist->amount;
2787 current_playlist.last_insert_pos = playlist->last_insert_pos;
2788 current_playlist.seed = playlist->seed;
2789 current_playlist.shuffle_modified = playlist->shuffle_modified;
2790 current_playlist.deleted = playlist->deleted;
2791 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2793 memcpy(current_playlist.control_cache, playlist->control_cache,
2794 sizeof(current_playlist.control_cache));
2795 current_playlist.num_cached = playlist->num_cached;
2796 current_playlist.pending_control_sync = playlist->pending_control_sync;
2798 return 0;
2800 struct playlist_info *playlist_get_current(void)
2802 return &current_playlist;
2805 * Close files and delete control file for non-current playlist.
2807 void playlist_close(struct playlist_info* playlist)
2809 if (!playlist)
2810 return;
2812 if (playlist->fd >= 0)
2813 close(playlist->fd);
2815 if (playlist->control_fd >= 0)
2816 close(playlist->control_fd);
2818 if (playlist->control_created)
2819 remove(playlist->control_filename);
2822 void playlist_sync(struct playlist_info* playlist)
2824 if (!playlist)
2825 playlist = &current_playlist;
2827 sync_control(playlist, false);
2828 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2829 audio_flush_and_reload_tracks();
2831 #ifdef HAVE_DIRCACHE
2832 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2833 #endif
2837 * Insert track into playlist at specified position (or one of the special
2838 * positions). Returns position where track was inserted or -1 if error.
2840 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2841 int position, bool queue, bool sync)
2843 int result;
2845 if (!playlist)
2846 playlist = &current_playlist;
2848 if (check_control(playlist) < 0)
2850 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2851 return -1;
2854 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2856 /* Check if we want manually sync later. For example when adding
2857 * bunch of files from tagcache, syncing after every file wouldn't be
2858 * a good thing to do. */
2859 if (sync && result >= 0)
2860 playlist_sync(playlist);
2862 return result;
2866 * Insert all tracks from specified directory into playlist.
2868 int playlist_insert_directory(struct playlist_info* playlist,
2869 const char *dirname, int position, bool queue,
2870 bool recurse)
2872 int result;
2873 unsigned char *count_str;
2874 struct directory_search_context context;
2876 if (!playlist)
2877 playlist = &current_playlist;
2879 if (check_control(playlist) < 0)
2881 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2882 return -1;
2885 if (position == PLAYLIST_REPLACE)
2887 if (playlist_remove_all_tracks(playlist) == 0)
2888 position = PLAYLIST_INSERT_LAST;
2889 else
2890 return -1;
2893 if (queue)
2894 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2895 else
2896 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2898 display_playlist_count(0, count_str, false);
2900 context.playlist = playlist;
2901 context.position = position;
2902 context.queue = queue;
2903 context.count = 0;
2905 cpu_boost(true);
2907 result = playlist_directory_tracksearch(dirname, recurse,
2908 directory_search_callback, &context);
2910 sync_control(playlist, false);
2912 cpu_boost(false);
2914 display_playlist_count(context.count, count_str, true);
2916 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2917 audio_flush_and_reload_tracks();
2919 #ifdef HAVE_DIRCACHE
2920 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2921 #endif
2923 return result;
2927 * Insert all tracks from specified playlist into dynamic playlist.
2929 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
2930 int position, bool queue)
2932 int fd;
2933 int max;
2934 char *temp_ptr;
2935 const char *dir;
2936 unsigned char *count_str;
2937 char temp_buf[MAX_PATH+1];
2938 char trackname[MAX_PATH+1];
2939 int count = 0;
2940 int result = 0;
2941 bool utf8 = is_m3u8(filename);
2943 if (!playlist)
2944 playlist = &current_playlist;
2946 if (check_control(playlist) < 0)
2948 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2949 return -1;
2952 fd = open_utf8(filename, O_RDONLY);
2953 if (fd < 0)
2955 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2956 return -1;
2959 /* we need the directory name for formatting purposes */
2960 dir = filename;
2962 temp_ptr = strrchr(filename+1,'/');
2963 if (temp_ptr)
2964 *temp_ptr = 0;
2965 else
2966 dir = "/";
2968 if (queue)
2969 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2970 else
2971 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2973 display_playlist_count(count, count_str, false);
2975 if (position == PLAYLIST_REPLACE)
2977 if (playlist_remove_all_tracks(playlist) == 0)
2978 position = PLAYLIST_INSERT_LAST;
2979 else return -1;
2982 cpu_boost(true);
2984 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2986 /* user abort */
2987 if (action_userabort(TIMEOUT_NOBLOCK))
2988 break;
2990 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2992 int insert_pos;
2994 if (!utf8)
2996 /* Use trackname as a temporay buffer. Note that trackname must
2997 * be as large as temp_buf.
2999 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3002 /* we need to format so that relative paths are correctly
3003 handled */
3004 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3005 dir) < 0)
3007 result = -1;
3008 break;
3011 insert_pos = add_track_to_playlist(playlist, trackname, position,
3012 queue, -1);
3014 if (insert_pos < 0)
3016 result = -1;
3017 break;
3020 /* Make sure tracks are inserted in correct order if user
3021 requests INSERT_FIRST */
3022 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3023 position = insert_pos + 1;
3025 count++;
3027 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3029 display_playlist_count(count, count_str, false);
3031 if (count == PLAYLIST_DISPLAY_COUNT &&
3032 (audio_status() & AUDIO_STATUS_PLAY) &&
3033 playlist->started)
3034 audio_flush_and_reload_tracks();
3038 /* let the other threads work */
3039 yield();
3042 close(fd);
3044 if (temp_ptr)
3045 *temp_ptr = '/';
3047 sync_control(playlist, false);
3049 cpu_boost(false);
3051 display_playlist_count(count, count_str, true);
3053 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3054 audio_flush_and_reload_tracks();
3056 #ifdef HAVE_DIRCACHE
3057 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3058 #endif
3060 return result;
3064 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3065 * we want to delete the current playing track.
3067 int playlist_delete(struct playlist_info* playlist, int index)
3069 int result = 0;
3071 if (!playlist)
3072 playlist = &current_playlist;
3074 if (check_control(playlist) < 0)
3076 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3077 return -1;
3080 if (index == PLAYLIST_DELETE_CURRENT)
3081 index = playlist->index;
3083 result = remove_track_from_playlist(playlist, index, true);
3085 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3086 playlist->started)
3087 audio_flush_and_reload_tracks();
3089 return result;
3093 * Move track at index to new_index. Tracks between the two are shifted
3094 * appropriately. Returns 0 on success and -1 on failure.
3096 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3098 int result;
3099 int seek;
3100 bool control_file;
3101 bool queue;
3102 bool current = false;
3103 int r;
3104 char filename[MAX_PATH];
3106 if (!playlist)
3107 playlist = &current_playlist;
3109 if (check_control(playlist) < 0)
3111 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3112 return -1;
3115 if (index == new_index)
3116 return -1;
3118 if (index == playlist->index)
3119 /* Moving the current track */
3120 current = true;
3122 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3123 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3124 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3126 if (get_filename(playlist, index, seek, control_file, filename,
3127 sizeof(filename)) < 0)
3128 return -1;
3130 /* We want to insert the track at the position that was specified by
3131 new_index. This may be different then new_index because of the
3132 shifting that will occur after the delete.
3133 We calculate this before we do the remove as it depends on the
3134 size of the playlist before the track removal */
3135 r = rotate_index(playlist, new_index);
3137 /* Delete track from original position */
3138 result = remove_track_from_playlist(playlist, index, true);
3140 if (result != -1)
3142 if (r == 0)
3143 /* First index */
3144 new_index = PLAYLIST_PREPEND;
3145 else if (r == playlist->amount)
3146 /* Append */
3147 new_index = PLAYLIST_INSERT_LAST;
3148 else
3149 /* Calculate index of desired position */
3150 new_index = (r+playlist->first_index)%playlist->amount;
3152 result = add_track_to_playlist(playlist, filename, new_index, queue,
3153 -1);
3155 if (result != -1)
3157 if (current)
3159 /* Moved the current track */
3160 switch (new_index)
3162 case PLAYLIST_PREPEND:
3163 playlist->index = playlist->first_index;
3164 break;
3165 case PLAYLIST_INSERT_LAST:
3166 playlist->index = playlist->first_index - 1;
3167 if (playlist->index < 0)
3168 playlist->index += playlist->amount;
3169 break;
3170 default:
3171 playlist->index = new_index;
3172 break;
3176 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3177 audio_flush_and_reload_tracks();
3181 #ifdef HAVE_DIRCACHE
3182 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3183 #endif
3185 return result;
3188 /* shuffle currently playing playlist */
3189 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3190 bool start_current)
3192 int result;
3194 if (!playlist)
3195 playlist = &current_playlist;
3197 check_control(playlist);
3199 result = randomise_playlist(playlist, seed, start_current, true);
3201 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3202 playlist->started)
3203 audio_flush_and_reload_tracks();
3205 return result;
3208 /* sort currently playing playlist */
3209 int playlist_sort(struct playlist_info* playlist, bool start_current)
3211 int result;
3213 if (!playlist)
3214 playlist = &current_playlist;
3216 check_control(playlist);
3218 result = sort_playlist(playlist, start_current, true);
3220 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3221 playlist->started)
3222 audio_flush_and_reload_tracks();
3224 return result;
3227 /* returns true if playlist has been modified */
3228 bool playlist_modified(const struct playlist_info* playlist)
3230 if (!playlist)
3231 playlist = &current_playlist;
3233 if (playlist->shuffle_modified ||
3234 playlist->deleted ||
3235 playlist->num_inserted_tracks > 0)
3236 return true;
3238 return false;
3241 /* returns index of first track in playlist */
3242 int playlist_get_first_index(const struct playlist_info* playlist)
3244 if (!playlist)
3245 playlist = &current_playlist;
3247 return playlist->first_index;
3250 /* returns shuffle seed of playlist */
3251 int playlist_get_seed(const struct playlist_info* playlist)
3253 if (!playlist)
3254 playlist = &current_playlist;
3256 return playlist->seed;
3259 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3260 int playlist_amount_ex(const struct playlist_info* playlist)
3262 if (!playlist)
3263 playlist = &current_playlist;
3265 return playlist->amount;
3268 /* returns full path of playlist (minus extension) */
3269 char *playlist_name(const struct playlist_info* playlist, char *buf,
3270 int buf_size)
3272 char *sep;
3274 if (!playlist)
3275 playlist = &current_playlist;
3277 strlcpy(buf, playlist->filename+playlist->dirlen, buf_size);
3279 if (!buf[0])
3280 return NULL;
3282 /* Remove extension */
3283 sep = strrchr(buf, '.');
3284 if (sep)
3285 *sep = 0;
3287 return buf;
3290 /* returns the playlist filename */
3291 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3292 int buf_size)
3294 if (!playlist)
3295 playlist = &current_playlist;
3297 strlcpy(buf, playlist->filename, buf_size);
3299 if (!buf[0])
3300 return NULL;
3302 return buf;
3305 /* Fills info structure with information about track at specified index.
3306 Returns 0 on success and -1 on failure */
3307 int playlist_get_track_info(struct playlist_info* playlist, int index,
3308 struct playlist_track_info* info)
3310 int seek;
3311 bool control_file;
3313 if (!playlist)
3314 playlist = &current_playlist;
3316 if (index < 0 || index >= playlist->amount)
3317 return -1;
3319 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3320 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3322 if (get_filename(playlist, index, seek, control_file, info->filename,
3323 sizeof(info->filename)) < 0)
3324 return -1;
3326 info->attr = 0;
3328 if (control_file)
3330 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3331 info->attr |= PLAYLIST_ATTR_QUEUED;
3332 else
3333 info->attr |= PLAYLIST_ATTR_INSERTED;
3337 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3338 info->attr |= PLAYLIST_ATTR_SKIPPED;
3340 info->index = index;
3341 info->display_index = rotate_index(playlist, index) + 1;
3343 return 0;
3346 /* save the current dynamic playlist to specified file */
3347 int playlist_save(struct playlist_info* playlist, char *filename)
3349 int fd;
3350 int i, index;
3351 int count = 0;
3352 char path[MAX_PATH+1];
3353 char tmp_buf[MAX_PATH+1];
3354 int result = 0;
3355 bool overwrite_current = false;
3356 int* index_buf = NULL;
3357 char* old_buffer = NULL;
3358 size_t old_buffer_size = 0;
3360 if (!playlist)
3361 playlist = &current_playlist;
3363 if (playlist->amount <= 0)
3364 return -1;
3366 /* use current working directory as base for pathname */
3367 if (format_track_path(path, filename, sizeof(tmp_buf),
3368 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3369 return -1;
3371 if (!strncmp(playlist->filename, path, strlen(path)))
3373 /* Attempting to overwrite current playlist file.*/
3375 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3377 /* not enough buffer space to store updated indices */
3378 /* Try to get a buffer */
3379 old_buffer = playlist->buffer;
3380 old_buffer_size = playlist->buffer_size;
3381 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size);
3382 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3384 playlist->buffer = old_buffer;
3385 playlist->buffer_size = old_buffer_size;
3386 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3387 return -1;
3391 /* in_ram buffer is unused for m3u files so we'll use for storing
3392 updated indices */
3393 index_buf = (int*)playlist->buffer;
3395 /* use temporary pathname */
3396 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3397 overwrite_current = true;
3400 if (is_m3u8(path))
3402 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
3404 else
3406 /* some applications require a BOM to read the file properly */
3407 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
3409 if (fd < 0)
3411 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3412 if (old_buffer != NULL)
3414 playlist->buffer = old_buffer;
3415 playlist->buffer_size = old_buffer_size;
3417 return -1;
3420 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3422 cpu_boost(true);
3424 index = playlist->first_index;
3425 for (i=0; i<playlist->amount; i++)
3427 bool control_file;
3428 bool queue;
3429 int seek;
3431 /* user abort */
3432 if (action_userabort(TIMEOUT_NOBLOCK))
3434 result = -1;
3435 break;
3438 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3439 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3440 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3442 /* Don't save queued files */
3443 if (!queue)
3445 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3446 MAX_PATH+1) < 0)
3448 result = -1;
3449 break;
3452 if (overwrite_current)
3453 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3455 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3457 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3458 result = -1;
3459 break;
3462 count++;
3464 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3465 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3466 false);
3468 yield();
3471 index = (index+1)%playlist->amount;
3474 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3476 close(fd);
3478 if (overwrite_current && result >= 0)
3480 result = -1;
3482 mutex_lock(playlist->control_mutex);
3484 /* Replace the current playlist with the new one and update indices */
3485 close(playlist->fd);
3486 if (remove(playlist->filename) >= 0)
3488 if (rename(path, playlist->filename) >= 0)
3490 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
3491 if (playlist->fd >= 0)
3493 index = playlist->first_index;
3494 for (i=0, count=0; i<playlist->amount; i++)
3496 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3498 playlist->indices[index] = index_buf[count];
3499 count++;
3501 index = (index+1)%playlist->amount;
3504 /* we need to recreate control because inserted tracks are
3505 now part of the playlist and shuffle has been
3506 invalidated */
3507 result = recreate_control(playlist);
3512 mutex_unlock(playlist->control_mutex);
3516 cpu_boost(false);
3517 if (old_buffer != NULL)
3519 playlist->buffer = old_buffer;
3520 playlist->buffer_size = old_buffer_size;
3523 return result;
3527 * Search specified directory for tracks and notify via callback. May be
3528 * called recursively.
3530 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3531 int (*callback)(char*, void*),
3532 void* context)
3534 char buf[MAX_PATH+1];
3535 int result = 0;
3536 int num_files = 0;
3537 int i;
3538 struct entry *files;
3539 struct tree_context* tc = tree_get_context();
3540 int old_dirfilter = *(tc->dirfilter);
3542 if (!callback)
3543 return -1;
3545 /* use the tree browser dircache to load files */
3546 *(tc->dirfilter) = SHOW_ALL;
3548 if (ft_load(tc, dirname) < 0)
3550 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3551 *(tc->dirfilter) = old_dirfilter;
3552 return -1;
3555 files = tc->cache.entries;
3556 num_files = tc->filesindir;
3558 /* we've overwritten the dircache so tree browser will need to be
3559 reloaded */
3560 reload_directory();
3562 for (i=0; i<num_files; i++)
3564 /* user abort */
3565 if (action_userabort(TIMEOUT_NOBLOCK))
3567 result = -1;
3568 break;
3571 if (files[i].attr & ATTR_DIRECTORY)
3573 if (recurse)
3575 /* recursively add directories */
3576 snprintf(buf, sizeof(buf), "%s/%s",
3577 dirname[1]? dirname: "", files[i].name);
3578 result = playlist_directory_tracksearch(buf, recurse,
3579 callback, context);
3580 if (result < 0)
3581 break;
3583 /* we now need to reload our current directory */
3584 if(ft_load(tc, dirname) < 0)
3586 result = -1;
3587 break;
3590 files = tc->cache.entries;
3591 num_files = tc->filesindir;
3592 if (!num_files)
3594 result = -1;
3595 break;
3598 else
3599 continue;
3601 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3603 snprintf(buf, sizeof(buf), "%s/%s",
3604 dirname[1]? dirname: "", files[i].name);
3606 if (callback(buf, context) != 0)
3608 result = -1;
3609 break;
3612 /* let the other threads work */
3613 yield();
3617 /* restore dirfilter */
3618 *(tc->dirfilter) = old_dirfilter;
3620 return result;