The threading model should be set from configure, not config.h.
[maemo-rb.git] / apps / playlist.c
blob5b5f489cde9aa800be9742ca7a6ef92eef7d4d17
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);
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)
422 close(temp_fd);
423 return -1;
426 playlist->filename[playlist->dirlen-1] = '\0';
428 /* cannot call update_control() because of mutex */
429 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
430 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
432 playlist->filename[playlist->dirlen-1] = c;
434 if (result < 0)
436 close(temp_fd);
437 return result;
441 playlist->seed = 0;
442 playlist->shuffle_modified = false;
443 playlist->deleted = false;
444 playlist->num_inserted_tracks = 0;
446 for (i=0; i<playlist->amount; i++)
448 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
450 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
451 char inserted_file[MAX_PATH+1];
453 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
454 SEEK_SET);
455 read_line(temp_fd, inserted_file, sizeof(inserted_file));
457 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
458 queue?'Q':'A', i, playlist->last_insert_pos);
459 if (result > 0)
461 /* save the position in file where name is written */
462 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
464 result = fdprintf(playlist->control_fd, "%s\n",
465 inserted_file);
467 playlist->indices[i] =
468 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
471 if (result < 0)
472 break;
474 playlist->num_inserted_tracks++;
478 close(temp_fd);
479 remove(temp_file);
480 fsync(playlist->control_fd);
482 if (result < 0)
483 return result;
485 return 0;
489 * store directory and name of playlist file
491 static void update_playlist_filename(struct playlist_info* playlist,
492 const char *dir, const char *file)
494 char *sep="";
495 int dirlen = strlen(dir);
497 playlist->utf8 = is_m3u8(file);
499 /* If the dir does not end in trailing slash, we use a separator.
500 Otherwise we don't. */
501 if('/' != dir[dirlen-1])
503 sep="/";
504 dirlen++;
507 playlist->dirlen = dirlen;
509 snprintf(playlist->filename, sizeof(playlist->filename),
510 "%s%s%s", dir, sep, file);
514 * calculate track offsets within a playlist file
516 static int add_indices_to_playlist(struct playlist_info* playlist,
517 char* buffer, size_t buflen)
519 unsigned int nread;
520 unsigned int i = 0;
521 unsigned int count = 0;
522 bool store_index;
523 unsigned char *p;
524 int result = 0;
526 if(-1 == playlist->fd)
527 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
528 if(playlist->fd < 0)
529 return -1; /* failure */
530 if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
531 playlist->utf8 = true; /* Override any earlier indication. */
533 splash(0, ID2P(LANG_WAIT));
535 if (!buffer)
537 /* use mp3 buffer for maximum load speed */
538 audio_stop();
539 buffer = audio_get_buffer(false, &buflen);
542 store_index = true;
544 while(1)
546 nread = read(playlist->fd, buffer, buflen);
547 /* Terminate on EOF */
548 if(nread <= 0)
549 break;
551 p = (unsigned char *)buffer;
553 for(count=0; count < nread; count++,p++) {
555 /* Are we on a new line? */
556 if((*p == '\n') || (*p == '\r'))
558 store_index = true;
560 else if(store_index)
562 store_index = false;
564 if(*p != '#')
566 if ( playlist->amount >= playlist->max_playlist_size ) {
567 display_buffer_full();
568 result = -1;
569 goto exit;
572 /* Store a new entry */
573 playlist->indices[ playlist->amount ] = i+count;
574 #ifdef HAVE_DIRCACHE
575 if (playlist->filenames)
576 playlist->filenames[ playlist->amount ] = -1;
577 #endif
578 playlist->amount++;
583 i+= count;
586 exit:
587 #ifdef HAVE_DIRCACHE
588 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
589 #endif
591 return result;
595 * Utility function to create a new playlist, fill it with the next or
596 * previous directory, shuffle it if needed, and start playback.
597 * If play_last is true and direction zero or negative, start playing
598 * the last file in the directory, otherwise start playing the first.
600 static int create_and_play_dir(int direction, bool play_last)
602 char dir[MAX_PATH + 1];
603 int res;
604 int index = -1;
606 if(direction > 0)
607 res = get_next_directory(dir);
608 else
609 res = get_previous_directory(dir);
611 if (res < 0)
612 /* return the error encountered */
613 return res;
615 if (playlist_create(dir, NULL) != -1)
617 ft_build_playlist(tree_get_context(), 0);
619 if (global_settings.playlist_shuffle)
620 playlist_shuffle(current_tick, -1);
622 if (play_last && direction <= 0)
623 index = current_playlist.amount - 1;
624 else
625 index = 0;
627 #if (CONFIG_CODEC == SWCODEC)
628 current_playlist.started = true;
629 #else
630 playlist_start(index, 0);
631 #endif
634 /* we've overwritten the dircache when getting the next/previous dir,
635 so the tree browser context will need to be reloaded */
636 reload_directory();
638 return index;
642 * Removes all tracks, from the playlist, leaving the presently playing
643 * track queued.
645 int playlist_remove_all_tracks(struct playlist_info *playlist)
647 int result;
649 if (playlist == NULL)
650 playlist = &current_playlist;
652 while (playlist->index > 0)
653 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
654 return result;
656 while (playlist->amount > 1)
657 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
658 return result;
660 if (playlist->amount == 1) {
661 playlist->indices[0] |= PLAYLIST_QUEUED;
664 return 0;
669 * Add track to playlist at specified position. There are seven special
670 * positions that can be specified:
671 * PLAYLIST_PREPEND - Add track at beginning of playlist
672 * PLAYLIST_INSERT - Add track after current song. NOTE: If
673 * there are already inserted tracks then track
674 * is added to the end of the insertion list
675 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
676 * matter what other tracks have been inserted
677 * PLAYLIST_INSERT_LAST - Add track to end of playlist
678 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
679 * current playing track and end of playlist
680 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
681 * the playlist.
682 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
683 * and inster this track at the end.
685 static int add_track_to_playlist(struct playlist_info* playlist,
686 const char *filename, int position,
687 bool queue, int seek_pos)
689 int insert_position, orig_position;
690 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
691 int i;
693 insert_position = orig_position = position;
695 if (playlist->amount >= playlist->max_playlist_size)
697 display_buffer_full();
698 return -1;
701 switch (position)
703 case PLAYLIST_PREPEND:
704 position = insert_position = playlist->first_index;
705 break;
706 case PLAYLIST_INSERT:
707 /* if there are already inserted tracks then add track to end of
708 insertion list else add after current playing track */
709 if (playlist->last_insert_pos >= 0 &&
710 playlist->last_insert_pos < playlist->amount &&
711 (playlist->indices[playlist->last_insert_pos]&
712 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
713 position = insert_position = playlist->last_insert_pos+1;
714 else if (playlist->amount > 0)
715 position = insert_position = playlist->index + 1;
716 else
717 position = insert_position = 0;
719 playlist->last_insert_pos = position;
720 break;
721 case PLAYLIST_INSERT_FIRST:
722 if (playlist->amount > 0)
723 position = insert_position = playlist->index + 1;
724 else
725 position = insert_position = 0;
727 playlist->last_insert_pos = position;
728 break;
729 case PLAYLIST_INSERT_LAST:
730 if (playlist->first_index > 0)
731 position = insert_position = playlist->first_index;
732 else
733 position = insert_position = playlist->amount;
735 playlist->last_insert_pos = position;
736 break;
737 case PLAYLIST_INSERT_SHUFFLED:
739 if (playlist->started)
741 int offset;
742 int n = playlist->amount -
743 rotate_index(playlist, playlist->index);
745 if (n > 0)
746 offset = rand() % n;
747 else
748 offset = 0;
750 position = playlist->index + offset + 1;
751 if (position >= playlist->amount)
752 position -= playlist->amount;
754 insert_position = position;
756 else
757 position = insert_position = (rand() % (playlist->amount+1));
758 break;
760 case PLAYLIST_INSERT_LAST_SHUFFLED:
762 position = insert_position = playlist->last_shuffled_start +
763 rand() % (playlist->amount - playlist->last_shuffled_start + 1);
764 break;
766 case PLAYLIST_REPLACE:
767 if (playlist_remove_all_tracks(playlist) < 0)
768 return -1;
770 playlist->last_insert_pos = position = insert_position = playlist->index + 1;
771 break;
774 if (queue)
775 flags |= PLAYLIST_QUEUED;
777 /* shift indices so that track can be added */
778 for (i=playlist->amount; i>insert_position; i--)
780 playlist->indices[i] = playlist->indices[i-1];
781 #ifdef HAVE_DIRCACHE
782 if (playlist->filenames)
783 playlist->filenames[i] = playlist->filenames[i-1];
784 #endif
787 /* update stored indices if needed */
789 if (orig_position < 0)
791 if (playlist->amount > 0 && insert_position <= playlist->index &&
792 playlist->started)
793 playlist->index++;
795 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
796 orig_position != PLAYLIST_PREPEND && playlist->started)
797 playlist->first_index++;
800 if (insert_position < playlist->last_insert_pos ||
801 (insert_position == playlist->last_insert_pos && position < 0))
802 playlist->last_insert_pos++;
804 if (seek_pos < 0 && playlist->control_fd >= 0)
806 int result = update_control(playlist,
807 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
808 playlist->last_insert_pos, filename, NULL, &seek_pos);
810 if (result < 0)
811 return result;
814 playlist->indices[insert_position] = flags | seek_pos;
816 #ifdef HAVE_DIRCACHE
817 if (playlist->filenames)
818 playlist->filenames[insert_position] = -1;
819 #endif
821 playlist->amount++;
822 playlist->num_inserted_tracks++;
824 return insert_position;
828 * Callback for playlist_directory_tracksearch to insert track into
829 * playlist.
831 static int directory_search_callback(char* filename, void* context)
833 struct directory_search_context* c =
834 (struct directory_search_context*) context;
835 int insert_pos;
837 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
838 c->queue, -1);
840 if (insert_pos < 0)
841 return -1;
843 (c->count)++;
845 /* Make sure tracks are inserted in correct order if user requests
846 INSERT_FIRST */
847 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
848 c->position = insert_pos + 1;
850 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
852 unsigned char* count_str;
854 if (c->queue)
855 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
856 else
857 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
859 display_playlist_count(c->count, count_str, false);
861 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
862 (audio_status() & AUDIO_STATUS_PLAY) &&
863 c->playlist->started)
864 audio_flush_and_reload_tracks();
867 return 0;
871 * remove track at specified position
873 static int remove_track_from_playlist(struct playlist_info* playlist,
874 int position, bool write)
876 int i;
877 bool inserted;
879 if (playlist->amount <= 0)
880 return -1;
882 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
884 /* shift indices now that track has been removed */
885 for (i=position; i<playlist->amount; i++)
887 playlist->indices[i] = playlist->indices[i+1];
888 #ifdef HAVE_DIRCACHE
889 if (playlist->filenames)
890 playlist->filenames[i] = playlist->filenames[i+1];
891 #endif
894 playlist->amount--;
896 if (inserted)
897 playlist->num_inserted_tracks--;
898 else
899 playlist->deleted = true;
901 /* update stored indices if needed */
902 if (position < playlist->index)
903 playlist->index--;
905 if (position < playlist->first_index)
907 playlist->first_index--;
910 if (position <= playlist->last_insert_pos)
911 playlist->last_insert_pos--;
913 if (write && playlist->control_fd >= 0)
915 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
916 position, -1, NULL, NULL, NULL);
918 if (result < 0)
919 return result;
921 sync_control(playlist, false);
924 return 0;
928 * randomly rearrange the array of indices for the playlist. If start_current
929 * is true then update the index to the new index of the current playing track
931 static int randomise_playlist(struct playlist_info* playlist,
932 unsigned int seed, bool start_current,
933 bool write)
935 int count;
936 int candidate;
937 long store;
938 unsigned int current = playlist->indices[playlist->index];
940 /* seed 0 is used to identify sorted playlist for resume purposes */
941 if (seed == 0)
942 seed = 1;
944 /* seed with the given seed */
945 srand(seed);
947 /* randomise entire indices list */
948 for(count = playlist->amount - 1; count >= 0; count--)
950 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
951 candidate = rand() % (count + 1);
953 /* now swap the values at the 'count' and 'candidate' positions */
954 store = playlist->indices[candidate];
955 playlist->indices[candidate] = playlist->indices[count];
956 playlist->indices[count] = store;
957 #ifdef HAVE_DIRCACHE
958 if (playlist->filenames)
960 store = playlist->filenames[candidate];
961 playlist->filenames[candidate] = playlist->filenames[count];
962 playlist->filenames[count] = store;
964 #endif
967 if (start_current)
968 find_and_set_playlist_index(playlist, current);
970 /* indices have been moved so last insert position is no longer valid */
971 playlist->last_insert_pos = -1;
973 playlist->seed = seed;
974 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
975 playlist->shuffle_modified = true;
977 if (write)
979 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
980 playlist->first_index, NULL, NULL, NULL);
983 return 0;
987 * Sort the array of indices for the playlist. If start_current is true then
988 * set the index to the new index of the current song.
989 * Also while going to unshuffled mode set the first_index to 0.
991 static int sort_playlist(struct playlist_info* playlist, bool start_current,
992 bool write)
994 unsigned int current = playlist->indices[playlist->index];
996 if (playlist->amount > 0)
997 qsort((void*)playlist->indices, playlist->amount,
998 sizeof(playlist->indices[0]), compare);
1000 #ifdef HAVE_DIRCACHE
1001 /** We need to re-check the song names from disk because qsort can't
1002 * sort two arrays at once :/
1003 * FIXME: Please implement a better way to do this. */
1004 memset((void*)playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int));
1005 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1006 #endif
1008 if (start_current)
1009 find_and_set_playlist_index(playlist, current);
1011 /* indices have been moved so last insert position is no longer valid */
1012 playlist->last_insert_pos = -1;
1014 if (!playlist->num_inserted_tracks && !playlist->deleted)
1015 playlist->shuffle_modified = false;
1016 if (write && playlist->control_fd >= 0)
1018 playlist->first_index = 0;
1019 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1020 playlist->first_index, -1, NULL, NULL, NULL);
1023 return 0;
1026 /* Calculate how many steps we have to really step when skipping entries
1027 * marked as bad.
1029 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1031 int i, count, direction;
1032 int index;
1033 int stepped_count = 0;
1035 if (steps < 0)
1037 direction = -1;
1038 count = -steps;
1040 else
1042 direction = 1;
1043 count = steps;
1046 index = playlist->index;
1047 i = 0;
1048 do {
1049 /* Boundary check */
1050 if (index < 0)
1051 index += playlist->amount;
1052 if (index >= playlist->amount)
1053 index -= playlist->amount;
1055 /* Check if we found a bad entry. */
1056 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1058 steps += direction;
1059 /* Are all entries bad? */
1060 if (stepped_count++ > playlist->amount)
1061 break ;
1063 else
1064 i++;
1066 index += direction;
1067 } while (i <= count);
1069 return steps;
1072 #if CONFIG_CODEC == SWCODEC
1073 /* Marks the index of the track to be skipped that is "steps" away from
1074 * current playing track.
1076 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1078 int index;
1080 if (playlist == NULL)
1081 playlist = &current_playlist;
1083 /* need to account for already skipped tracks */
1084 steps = calculate_step_count(playlist, steps);
1086 index = playlist->index + steps;
1087 if (index < 0)
1088 index += playlist->amount;
1089 else if (index >= playlist->amount)
1090 index -= playlist->amount;
1092 playlist->indices[index] |= PLAYLIST_SKIPPED;
1094 #endif /* CONFIG_CODEC == SWCODEC */
1097 * returns the index of the track that is "steps" away from current playing
1098 * track.
1100 static int get_next_index(const struct playlist_info* playlist, int steps,
1101 int repeat_mode)
1103 int current_index = playlist->index;
1104 int next_index = -1;
1106 if (playlist->amount <= 0)
1107 return -1;
1109 if (repeat_mode == -1)
1110 repeat_mode = global_settings.repeat_mode;
1112 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1113 repeat_mode = REPEAT_ALL;
1115 steps = calculate_step_count(playlist, steps);
1116 switch (repeat_mode)
1118 case REPEAT_SHUFFLE:
1119 /* Treat repeat shuffle just like repeat off. At end of playlist,
1120 play will be resumed in playlist_next() */
1121 case REPEAT_OFF:
1123 current_index = rotate_index(playlist, current_index);
1124 next_index = current_index+steps;
1125 if ((next_index < 0) || (next_index >= playlist->amount))
1126 next_index = -1;
1127 else
1128 next_index = (next_index+playlist->first_index) %
1129 playlist->amount;
1131 break;
1134 case REPEAT_ONE:
1135 #ifdef AB_REPEAT_ENABLE
1136 case REPEAT_AB:
1137 #endif
1138 next_index = current_index;
1139 break;
1141 case REPEAT_ALL:
1142 default:
1144 next_index = (current_index+steps) % playlist->amount;
1145 while (next_index < 0)
1146 next_index += playlist->amount;
1148 if (steps >= playlist->amount)
1150 int i, index;
1152 index = next_index;
1153 next_index = -1;
1155 /* second time around so skip the queued files */
1156 for (i=0; i<playlist->amount; i++)
1158 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1159 index = (index+1) % playlist->amount;
1160 else
1162 next_index = index;
1163 break;
1167 break;
1171 /* No luck if the whole playlist was bad. */
1172 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1173 return -1;
1175 return next_index;
1179 * Search for the seek track and set appropriate indices. Used after shuffle
1180 * to make sure the current index is still pointing to correct track.
1182 static void find_and_set_playlist_index(struct playlist_info* playlist,
1183 unsigned int seek)
1185 int i;
1187 /* Set the index to the current song */
1188 for (i=0; i<playlist->amount; i++)
1190 if (playlist->indices[i] == seek)
1192 playlist->index = playlist->first_index = i;
1194 break;
1200 * used to sort track indices. Sort order is as follows:
1201 * 1. Prepended tracks (in prepend order)
1202 * 2. Playlist/directory tracks (in playlist order)
1203 * 3. Inserted/Appended tracks (in insert order)
1205 static int compare(const void* p1, const void* p2)
1207 unsigned long* e1 = (unsigned long*) p1;
1208 unsigned long* e2 = (unsigned long*) p2;
1209 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1210 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1212 if (flags1 == flags2)
1213 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1214 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1215 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1216 return -1;
1217 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1218 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1219 return 1;
1220 else if (flags1 && flags2)
1221 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1222 else
1223 return *e1 - *e2;
1226 #ifdef HAVE_DIRCACHE
1228 * Thread to update filename pointers to dircache on background
1229 * without affecting playlist load up performance. This thread also flushes
1230 * any pending control commands when the disk spins up.
1232 static void playlist_flush_callback(void *param)
1234 (void)param;
1235 struct playlist_info *playlist;
1236 playlist = &current_playlist;
1237 if (playlist->control_fd >= 0)
1239 if (playlist->num_cached > 0)
1241 mutex_lock(playlist->control_mutex);
1242 flush_cached_control(playlist);
1243 mutex_unlock(playlist->control_mutex);
1245 sync_control(playlist, true);
1249 static bool is_dircache_pointers_intact(void)
1251 return dircache_get_appflag(DIRCACHE_APPFLAG_PLAYLIST) ? true : false;
1254 static void playlist_thread(void)
1256 struct queue_event ev;
1257 bool dirty_pointers = false;
1258 static char tmp[MAX_PATH+1];
1260 struct playlist_info *playlist;
1261 int index;
1262 int seek;
1263 bool control_file;
1265 int sleep_time = 5;
1267 #ifdef HAVE_DISK_STORAGE
1268 if (global_settings.disk_spindown > 1 &&
1269 global_settings.disk_spindown <= 5)
1270 sleep_time = global_settings.disk_spindown - 1;
1271 #endif
1273 while (1)
1275 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1277 switch (ev.id)
1279 case PLAYLIST_LOAD_POINTERS:
1280 dirty_pointers = true;
1281 break ;
1283 /* Start the background scanning after either the disk spindown
1284 timeout or 5s, whichever is less */
1285 case SYS_TIMEOUT:
1287 playlist = &current_playlist;
1288 if (playlist->control_fd >= 0)
1290 if (playlist->num_cached > 0)
1291 register_storage_idle_func(playlist_flush_callback);
1294 if (!dircache_is_enabled() || !playlist->filenames
1295 || playlist->amount <= 0)
1297 break ;
1300 /* Check if previously loaded pointers are intact. */
1301 if (is_dircache_pointers_intact() && !dirty_pointers)
1302 break ;
1304 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1305 cpu_boost(true);
1306 #endif
1307 for (index = 0; index < playlist->amount
1308 && queue_empty(&playlist_queue); index++)
1310 /* Process only pointers that are not already loaded. */
1311 if (is_dircache_pointers_intact() && playlist->filenames[index] >= 0)
1312 continue ;
1314 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1315 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1317 /* Load the filename from playlist file. */
1318 if (get_filename(playlist, index, seek, control_file, tmp,
1319 sizeof(tmp)) < 0)
1321 break ;
1324 /* Set the dircache entry pointer. */
1325 playlist->filenames[index] = dircache_get_entry_id(tmp);
1327 /* And be on background so user doesn't notice any delays. */
1328 yield();
1331 if (dircache_is_enabled())
1332 dircache_set_appflag(DIRCACHE_APPFLAG_PLAYLIST);
1334 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1335 cpu_boost(false);
1336 #endif
1337 if (index == playlist->amount)
1338 dirty_pointers = false;
1340 break ;
1343 case SYS_USB_CONNECTED:
1344 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1345 usb_wait_for_disconnect(&playlist_queue);
1346 break ;
1350 #endif
1353 * gets pathname for track at seek index
1355 static int get_filename(struct playlist_info* playlist, int index, int seek,
1356 bool control_file, char *buf, int buf_length)
1358 int fd;
1359 int max = -1;
1360 char tmp_buf[MAX_PATH+1];
1361 char dir_buf[MAX_PATH+1];
1362 bool utf8 = playlist->utf8;
1364 if (buf_length > MAX_PATH+1)
1365 buf_length = MAX_PATH+1;
1367 #ifdef HAVE_DIRCACHE
1368 if (is_dircache_pointers_intact() && playlist->filenames)
1370 if (playlist->filenames[index] >= 0)
1372 max = dircache_copy_path(playlist->filenames[index],
1373 tmp_buf, sizeof(tmp_buf)-1);
1376 #else
1377 (void)index;
1378 #endif
1380 if (playlist->in_ram && !control_file && max < 0)
1382 max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
1384 else if (max < 0)
1386 mutex_lock(playlist->control_mutex);
1388 if (control_file)
1390 fd = playlist->control_fd;
1391 utf8 = true;
1393 else
1395 if(-1 == playlist->fd)
1396 playlist->fd = open(playlist->filename, O_RDONLY);
1398 fd = playlist->fd;
1401 if(-1 != fd)
1404 if (lseek(fd, seek, SEEK_SET) != seek)
1405 max = -1;
1406 else
1408 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1410 if ((max > 0) && !utf8)
1412 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1413 * be as large as tmp_buf.
1415 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1420 mutex_unlock(playlist->control_mutex);
1422 if (max < 0)
1424 if (control_file)
1425 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1426 else
1427 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1429 return max;
1433 strlcpy(dir_buf, playlist->filename, playlist->dirlen);
1435 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1438 static int get_next_directory(char *dir){
1439 return get_next_dir(dir, true);
1442 static int get_previous_directory(char *dir){
1443 return get_next_dir(dir, false);
1447 * search through all the directories (starting with the current) to find
1448 * one that has tracks to play
1450 static int get_next_dir(char *dir, bool is_forward)
1452 struct playlist_info* playlist = &current_playlist;
1453 int result = -1;
1454 char *start_dir = NULL;
1455 bool exit = false;
1456 struct tree_context* tc = tree_get_context();
1457 int saved_dirfilter = *(tc->dirfilter);
1458 unsigned int base_len;
1460 if (global_settings.constrain_next_folder)
1462 /* constrain results to directories below user's start directory */
1463 strcpy(dir, global_settings.start_directory);
1464 base_len = strlen(dir);
1466 /* strip any trailing slash from base directory */
1467 if (base_len > 0 && dir[base_len - 1] == '/')
1469 base_len--;
1470 dir[base_len] = '\0';
1473 else
1475 /* start from root directory */
1476 dir[0] = '\0';
1477 base_len = 0;
1480 /* process random folder advance */
1481 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1483 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
1484 if (fd >= 0)
1486 int folder_count = 0;
1487 read(fd,&folder_count,sizeof(int));
1488 if (folder_count)
1490 char buffer[MAX_PATH];
1491 /* give up looking for a directory after we've had four
1492 times as many tries as there are directories. */
1493 unsigned long allowed_tries = folder_count * 4;
1494 int i;
1495 srand(current_tick);
1496 *(tc->dirfilter) = SHOW_MUSIC;
1497 tc->sort_dir = global_settings.sort_dir;
1498 while (!exit && allowed_tries--)
1500 i = rand() % folder_count;
1501 lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
1502 read(fd, buffer, MAX_PATH);
1503 /* is the current dir within our base dir and has music? */
1504 if ((base_len == 0 || !strncmp(buffer, dir, base_len))
1505 && check_subdir_for_music(buffer, "", false) == 0)
1506 exit = true;
1508 close(fd);
1509 *(tc->dirfilter) = saved_dirfilter;
1510 tc->sort_dir = global_settings.sort_dir;
1511 reload_directory();
1512 if (exit)
1514 strcpy(dir,buffer);
1515 return 0;
1518 else
1519 close(fd);
1523 /* if the current file is within our base dir, use its dir instead */
1524 if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
1525 strlcpy(dir, playlist->filename, playlist->dirlen);
1527 /* use the tree browser dircache to load files */
1528 *(tc->dirfilter) = SHOW_ALL;
1530 /* set up sorting/direction */
1531 tc->sort_dir = global_settings.sort_dir;
1532 if (!is_forward)
1534 static const char sortpairs[] =
1536 [SORT_ALPHA] = SORT_ALPHA_REVERSED,
1537 [SORT_DATE] = SORT_DATE_REVERSED,
1538 [SORT_TYPE] = SORT_TYPE_REVERSED,
1539 [SORT_ALPHA_REVERSED] = SORT_ALPHA,
1540 [SORT_DATE_REVERSED] = SORT_DATE,
1541 [SORT_TYPE_REVERSED] = SORT_TYPE,
1544 if ((unsigned)tc->sort_dir < sizeof(sortpairs))
1545 tc->sort_dir = sortpairs[tc->sort_dir];
1548 while (!exit)
1550 struct entry *files;
1551 int num_files = 0;
1552 int i;
1554 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1556 exit = true;
1557 result = -1;
1558 break;
1561 files = tree_get_entries(tc);
1562 num_files = tc->filesindir;
1564 tree_lock_cache(tc);
1565 for (i=0; i<num_files; i++)
1567 /* user abort */
1568 if (action_userabort(TIMEOUT_NOBLOCK))
1570 result = -1;
1571 exit = true;
1572 break;
1575 if (files[i].attr & ATTR_DIRECTORY)
1577 if (!start_dir)
1579 result = check_subdir_for_music(dir, files[i].name, true);
1580 if (result != -1)
1582 exit = true;
1583 break;
1586 else if (!strcmp(start_dir, files[i].name))
1587 start_dir = NULL;
1590 tree_unlock_cache(tc);
1592 if (!exit)
1594 /* we've already descended to the base dir with nothing found,
1595 check whether that contains music */
1596 if (strlen(dir) <= base_len)
1598 result = check_subdir_for_music(dir, "", true);
1599 if (result == -1)
1600 /* there's no music files in the base directory,
1601 treat as a fatal error */
1602 result = -2;
1603 break;
1605 else
1607 /* move down to parent directory. current directory name is
1608 stored as the starting point for the search in parent */
1609 start_dir = strrchr(dir, '/');
1610 if (start_dir)
1612 *start_dir = '\0';
1613 start_dir++;
1615 else
1616 break;
1621 /* restore dirfilter */
1622 *(tc->dirfilter) = saved_dirfilter;
1623 tc->sort_dir = global_settings.sort_dir;
1625 return result;
1629 * Checks if there are any music files in the dir or any of its
1630 * subdirectories. May be called recursively.
1632 static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1634 int result = -1;
1635 int dirlen = strlen(dir);
1636 int num_files = 0;
1637 int i;
1638 struct entry *files;
1639 bool has_music = false;
1640 bool has_subdir = false;
1641 struct tree_context* tc = tree_get_context();
1643 snprintf(
1644 dir + dirlen, MAX_PATH - dirlen,
1645 /* only add a trailing slash if we need one */
1646 dirlen && dir[dirlen - 1] == '/' ? "%s" : "/%s",
1647 subdir
1650 if (ft_load(tc, dir) < 0)
1652 return -2;
1655 files = tree_get_entries(tc);
1656 num_files = tc->filesindir;
1658 for (i=0; i<num_files; i++)
1660 if (files[i].attr & ATTR_DIRECTORY)
1661 has_subdir = true;
1662 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1664 has_music = true;
1665 break;
1669 if (has_music)
1670 return 0;
1672 tree_lock_cache(tc);
1673 if (has_subdir && recurse)
1675 for (i=0; i<num_files; i++)
1677 if (action_userabort(TIMEOUT_NOBLOCK))
1679 result = -2;
1680 break;
1683 if (files[i].attr & ATTR_DIRECTORY)
1685 result = check_subdir_for_music(dir, files[i].name, true);
1686 if (!result)
1687 break;
1691 tree_unlock_cache(tc);
1693 if (result < 0)
1695 if (dirlen)
1697 dir[dirlen] = '\0';
1699 else
1701 strcpy(dir, "/");
1704 /* we now need to reload our current directory */
1705 if(ft_load(tc, dir) < 0)
1706 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1708 return result;
1712 * Returns absolute path of track
1714 static int format_track_path(char *dest, char *src, int buf_length, int max,
1715 const char *dir)
1717 int i = 0;
1718 int j;
1719 char *temp_ptr;
1721 /* Look for the end of the string */
1722 while((i < max) &&
1723 (src[i] != '\n') &&
1724 (src[i] != '\r') &&
1725 (src[i] != '\0'))
1726 i++;
1728 /* Now work back killing white space */
1729 while((i > 0) &&
1730 ((src[i-1] == ' ') ||
1731 (src[i-1] == '\t')))
1732 i--;
1734 /* Zero-terminate the file name */
1735 src[i]=0;
1737 /* replace backslashes with forward slashes */
1738 for ( j=0; j<i; j++ )
1739 if ( src[j] == '\\' )
1740 src[j] = '/';
1742 if('/' == src[0])
1744 strlcpy(dest, src, buf_length);
1746 else
1748 /* handle dos style drive letter */
1749 if (':' == src[1])
1750 strlcpy(dest, &src[2], buf_length);
1751 else if (!strncmp(src, "../", 3))
1753 /* handle relative paths */
1754 i=3;
1755 while(!strncmp(&src[i], "../", 3))
1756 i += 3;
1757 for (j=0; j<i/3; j++) {
1758 temp_ptr = strrchr(dir, '/');
1759 if (temp_ptr)
1760 *temp_ptr = '\0';
1761 else
1762 break;
1764 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1766 else if ( '.' == src[0] && '/' == src[1] ) {
1767 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1769 else {
1770 snprintf(dest, buf_length, "%s/%s", dir, src);
1774 return 0;
1778 * Display splash message showing progress of playlist/directory insertion or
1779 * save.
1781 static void display_playlist_count(int count, const unsigned char *fmt,
1782 bool final)
1784 static long talked_tick = 0;
1785 long id = P2ID(fmt);
1786 if(global_settings.talk_menu && id>=0)
1788 if(final || (count && (talked_tick == 0
1789 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1791 talked_tick = current_tick;
1792 talk_number(count, false);
1793 talk_id(id, true);
1796 fmt = P2STR(fmt);
1798 splashf(0, fmt, count, str(LANG_OFF_ABORT));
1802 * Display buffer full message
1804 static void display_buffer_full(void)
1806 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1810 * Flush any cached control commands to disk. Called when playlist is being
1811 * modified. Returns 0 on success and -1 on failure.
1813 static int flush_cached_control(struct playlist_info* playlist)
1815 int result = 0;
1816 int i;
1818 if (!playlist->num_cached)
1819 return 0;
1821 lseek(playlist->control_fd, 0, SEEK_END);
1823 for (i=0; i<playlist->num_cached; i++)
1825 struct playlist_control_cache* cache =
1826 &(playlist->control_cache[i]);
1828 switch (cache->command)
1830 case PLAYLIST_COMMAND_PLAYLIST:
1831 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1832 cache->i1, cache->s1, cache->s2);
1833 break;
1834 case PLAYLIST_COMMAND_ADD:
1835 case PLAYLIST_COMMAND_QUEUE:
1836 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1837 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1838 cache->i1, cache->i2);
1839 if (result > 0)
1841 /* save the position in file where name is written */
1842 int* seek_pos = (int *)cache->data;
1843 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1844 result = fdprintf(playlist->control_fd, "%s\n",
1845 cache->s1);
1847 break;
1848 case PLAYLIST_COMMAND_DELETE:
1849 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1850 break;
1851 case PLAYLIST_COMMAND_SHUFFLE:
1852 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1853 cache->i1, cache->i2);
1854 break;
1855 case PLAYLIST_COMMAND_UNSHUFFLE:
1856 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1857 break;
1858 case PLAYLIST_COMMAND_RESET:
1859 result = fdprintf(playlist->control_fd, "R\n");
1860 break;
1861 default:
1862 break;
1865 if (result <= 0)
1866 break;
1869 if (result > 0)
1871 playlist->num_cached = 0;
1872 playlist->pending_control_sync = true;
1874 result = 0;
1876 else
1878 result = -1;
1879 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1882 return result;
1886 * Update control data with new command. Depending on the command, it may be
1887 * cached or flushed to disk.
1889 static int update_control(struct playlist_info* playlist,
1890 enum playlist_command command, int i1, int i2,
1891 const char* s1, const char* s2, void* data)
1893 int result = 0;
1894 struct playlist_control_cache* cache;
1895 bool flush = false;
1897 mutex_lock(playlist->control_mutex);
1899 cache = &(playlist->control_cache[playlist->num_cached++]);
1901 cache->command = command;
1902 cache->i1 = i1;
1903 cache->i2 = i2;
1904 cache->s1 = s1;
1905 cache->s2 = s2;
1906 cache->data = data;
1908 switch (command)
1910 case PLAYLIST_COMMAND_PLAYLIST:
1911 case PLAYLIST_COMMAND_ADD:
1912 case PLAYLIST_COMMAND_QUEUE:
1913 #ifndef HAVE_DIRCACHE
1914 case PLAYLIST_COMMAND_DELETE:
1915 case PLAYLIST_COMMAND_RESET:
1916 #endif
1917 flush = true;
1918 break;
1919 case PLAYLIST_COMMAND_SHUFFLE:
1920 case PLAYLIST_COMMAND_UNSHUFFLE:
1921 default:
1922 /* only flush when needed */
1923 break;
1926 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1927 result = flush_cached_control(playlist);
1929 mutex_unlock(playlist->control_mutex);
1931 return result;
1935 * sync control file to disk
1937 static void sync_control(struct playlist_info* playlist, bool force)
1939 #ifdef HAVE_DIRCACHE
1940 if (playlist->started && force)
1941 #else
1942 (void) force;
1944 if (playlist->started)
1945 #endif
1947 if (playlist->pending_control_sync)
1949 mutex_lock(playlist->control_mutex);
1950 fsync(playlist->control_fd);
1951 playlist->pending_control_sync = false;
1952 mutex_unlock(playlist->control_mutex);
1958 * Rotate indices such that first_index is index 0
1960 static int rotate_index(const struct playlist_info* playlist, int index)
1962 index -= playlist->first_index;
1963 if (index < 0)
1964 index += playlist->amount;
1966 return index;
1970 * Need no movement protection since all 3 allocations are not passed to
1971 * other functions which can yield().
1973 static int move_callback(int handle, void* current, void* new)
1975 (void)handle;
1976 struct playlist_info* playlist = &current_playlist;
1977 if (current == playlist->indices)
1978 playlist->indices = new;
1979 else if (current == playlist->filenames)
1980 playlist->filenames = new;
1981 /* buffer can possibly point to a new buffer temporarily (playlist_save()).
1982 * just don't overwrite the pointer to that temp buffer */
1983 else if (current == playlist->buffer)
1984 playlist->buffer = new;
1986 return BUFLIB_CB_OK;
1990 static struct buflib_callbacks ops = {
1991 .move_callback = move_callback,
1992 .shrink_callback = NULL,
1995 * Initialize playlist entries at startup
1997 void playlist_init(void)
1999 int handle;
2000 struct playlist_info* playlist = &current_playlist;
2002 mutex_init(&current_playlist_mutex);
2003 mutex_init(&created_playlist_mutex);
2005 playlist->current = true;
2006 strlcpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
2007 sizeof(playlist->control_filename));
2008 playlist->fd = -1;
2009 playlist->control_fd = -1;
2010 playlist->max_playlist_size = global_settings.max_files_in_playlist;
2011 handle = core_alloc_ex("playlist idx",
2012 playlist->max_playlist_size * sizeof(int), &ops);
2013 playlist->indices = core_get_data(handle);
2014 playlist->buffer_size =
2015 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
2016 handle = core_alloc_ex("playlist buf",
2017 playlist->buffer_size, &ops);
2018 playlist->buffer = core_get_data(handle);
2019 playlist->buffer_handle = handle;
2020 playlist->control_mutex = &current_playlist_mutex;
2022 empty_playlist(playlist, true);
2024 #ifdef HAVE_DIRCACHE
2025 handle = core_alloc_ex("playlist dc",
2026 playlist->max_playlist_size * sizeof(int), &ops);
2027 playlist->filenames = core_get_data(handle);
2028 memset((void*)playlist->filenames, 0xff,
2029 playlist->max_playlist_size * sizeof(int));
2030 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
2031 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
2032 IF_COP(, CPU));
2033 queue_init(&playlist_queue, true);
2034 #endif
2038 * Clean playlist at shutdown
2040 void playlist_shutdown(void)
2042 struct playlist_info* playlist = &current_playlist;
2044 if (playlist->control_fd >= 0)
2046 mutex_lock(playlist->control_mutex);
2048 if (playlist->num_cached > 0)
2049 flush_cached_control(playlist);
2051 close(playlist->control_fd);
2053 mutex_unlock(playlist->control_mutex);
2058 * Create new playlist
2060 int playlist_create(const char *dir, const char *file)
2062 struct playlist_info* playlist = &current_playlist;
2064 new_playlist(playlist, dir, file);
2066 if (file)
2067 /* load the playlist file */
2068 add_indices_to_playlist(playlist, NULL, 0);
2070 return 0;
2073 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2076 * Restore the playlist state based on control file commands. Called to
2077 * resume playback after shutdown.
2079 int playlist_resume(void)
2081 struct playlist_info* playlist = &current_playlist;
2082 char *buffer;
2083 size_t buflen;
2084 int nread;
2085 int total_read = 0;
2086 int control_file_size = 0;
2087 bool first = true;
2088 bool sorted = true;
2090 /* use mp3 buffer for maximum load speed */
2091 buffer = (char *)audio_get_buffer(false, &buflen);
2093 empty_playlist(playlist, true);
2095 splash(0, ID2P(LANG_WAIT));
2096 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2097 if (playlist->control_fd < 0)
2099 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2100 return -1;
2102 playlist->control_created = true;
2104 control_file_size = filesize(playlist->control_fd);
2105 if (control_file_size <= 0)
2107 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2108 return -1;
2111 /* read a small amount first to get the header */
2112 nread = read(playlist->control_fd, buffer,
2113 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2114 if(nread <= 0)
2116 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2117 return -1;
2120 playlist->started = true;
2122 while (1)
2124 int result = 0;
2125 int count;
2126 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2127 int last_newline = 0;
2128 int str_count = -1;
2129 bool newline = true;
2130 bool exit_loop = false;
2131 char *p = buffer;
2132 char *str1 = NULL;
2133 char *str2 = NULL;
2134 char *str3 = NULL;
2135 unsigned long last_tick = current_tick;
2136 bool useraborted = false;
2138 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
2140 /* So a splash while we are loading. */
2141 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2143 splashf(0, str(LANG_LOADING_PERCENT),
2144 (total_read+count)*100/control_file_size,
2145 str(LANG_OFF_ABORT));
2146 if (action_userabort(TIMEOUT_NOBLOCK))
2148 useraborted = true;
2149 break;
2151 last_tick = current_tick;
2154 /* Are we on a new line? */
2155 if((*p == '\n') || (*p == '\r'))
2157 *p = '\0';
2159 /* save last_newline in case we need to load more data */
2160 last_newline = count;
2162 switch (current_command)
2164 case PLAYLIST_COMMAND_PLAYLIST:
2166 /* str1=version str2=dir str3=file */
2167 int version;
2169 if (!str1)
2171 result = -1;
2172 exit_loop = true;
2173 break;
2176 if (!str2)
2177 str2 = "";
2179 if (!str3)
2180 str3 = "";
2182 version = atoi(str1);
2184 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2185 return -1;
2187 update_playlist_filename(playlist, str2, str3);
2189 if (str3[0] != '\0')
2191 /* NOTE: add_indices_to_playlist() overwrites the
2192 audiobuf so we need to reload control file
2193 data */
2194 add_indices_to_playlist(playlist, NULL, 0);
2196 else if (str2[0] != '\0')
2198 playlist->in_ram = true;
2199 resume_directory(str2);
2202 /* load the rest of the data */
2203 first = false;
2204 exit_loop = true;
2206 break;
2208 case PLAYLIST_COMMAND_ADD:
2209 case PLAYLIST_COMMAND_QUEUE:
2211 /* str1=position str2=last_position str3=file */
2212 int position, last_position;
2213 bool queue;
2215 if (!str1 || !str2 || !str3)
2217 result = -1;
2218 exit_loop = true;
2219 break;
2222 position = atoi(str1);
2223 last_position = atoi(str2);
2225 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2226 false:true;
2228 /* seek position is based on str3's position in
2229 buffer */
2230 if (add_track_to_playlist(playlist, str3, position,
2231 queue, total_read+(str3-buffer)) < 0)
2232 return -1;
2234 playlist->last_insert_pos = last_position;
2236 break;
2238 case PLAYLIST_COMMAND_DELETE:
2240 /* str1=position */
2241 int position;
2243 if (!str1)
2245 result = -1;
2246 exit_loop = true;
2247 break;
2250 position = atoi(str1);
2252 if (remove_track_from_playlist(playlist, position,
2253 false) < 0)
2254 return -1;
2256 break;
2258 case PLAYLIST_COMMAND_SHUFFLE:
2260 /* str1=seed str2=first_index */
2261 int seed;
2263 if (!str1 || !str2)
2265 result = -1;
2266 exit_loop = true;
2267 break;
2270 if (!sorted)
2272 /* Always sort list before shuffling */
2273 sort_playlist(playlist, false, false);
2276 seed = atoi(str1);
2277 playlist->first_index = atoi(str2);
2279 if (randomise_playlist(playlist, seed, false,
2280 false) < 0)
2281 return -1;
2282 sorted = false;
2283 break;
2285 case PLAYLIST_COMMAND_UNSHUFFLE:
2287 /* str1=first_index */
2288 if (!str1)
2290 result = -1;
2291 exit_loop = true;
2292 break;
2295 playlist->first_index = atoi(str1);
2297 if (sort_playlist(playlist, false, false) < 0)
2298 return -1;
2300 sorted = true;
2301 break;
2303 case PLAYLIST_COMMAND_RESET:
2305 playlist->last_insert_pos = -1;
2306 break;
2308 case PLAYLIST_COMMAND_COMMENT:
2309 default:
2310 break;
2313 newline = true;
2315 /* to ignore any extra newlines */
2316 current_command = PLAYLIST_COMMAND_COMMENT;
2318 else if(newline)
2320 newline = false;
2322 /* first non-comment line must always specify playlist */
2323 if (first && *p != 'P' && *p != '#')
2325 result = -1;
2326 exit_loop = true;
2327 break;
2330 switch (*p)
2332 case 'P':
2333 /* playlist can only be specified once */
2334 if (!first)
2336 result = -1;
2337 exit_loop = true;
2338 break;
2341 current_command = PLAYLIST_COMMAND_PLAYLIST;
2342 break;
2343 case 'A':
2344 current_command = PLAYLIST_COMMAND_ADD;
2345 break;
2346 case 'Q':
2347 current_command = PLAYLIST_COMMAND_QUEUE;
2348 break;
2349 case 'D':
2350 current_command = PLAYLIST_COMMAND_DELETE;
2351 break;
2352 case 'S':
2353 current_command = PLAYLIST_COMMAND_SHUFFLE;
2354 break;
2355 case 'U':
2356 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2357 break;
2358 case 'R':
2359 current_command = PLAYLIST_COMMAND_RESET;
2360 break;
2361 case '#':
2362 current_command = PLAYLIST_COMMAND_COMMENT;
2363 break;
2364 default:
2365 result = -1;
2366 exit_loop = true;
2367 break;
2370 str_count = -1;
2371 str1 = NULL;
2372 str2 = NULL;
2373 str3 = NULL;
2375 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2377 /* all control file strings are separated with a colon.
2378 Replace the colon with 0 to get proper strings that can be
2379 used by commands above */
2380 if (*p == ':')
2382 *p = '\0';
2383 str_count++;
2385 if ((count+1) < nread)
2387 switch (str_count)
2389 case 0:
2390 str1 = p+1;
2391 break;
2392 case 1:
2393 str2 = p+1;
2394 break;
2395 case 2:
2396 str3 = p+1;
2397 break;
2398 default:
2399 /* allow last string to contain colons */
2400 *p = ':';
2401 break;
2408 if (result < 0)
2410 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2411 return result;
2414 if (useraborted)
2416 splash(HZ*2, ID2P(LANG_CANCEL));
2417 return -1;
2419 if (!newline || (exit_loop && count<nread))
2421 if ((total_read + count) >= control_file_size)
2423 /* no newline at end of control file */
2424 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2425 return -1;
2428 /* We didn't end on a newline or we exited loop prematurely.
2429 Either way, re-read the remainder. */
2430 count = last_newline;
2431 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2434 total_read += count;
2436 if (first)
2437 /* still looking for header */
2438 nread = read(playlist->control_fd, buffer,
2439 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2440 else
2441 nread = read(playlist->control_fd, buffer, buflen);
2443 /* Terminate on EOF */
2444 if(nread <= 0)
2446 break;
2450 #ifdef HAVE_DIRCACHE
2451 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2452 #endif
2454 return 0;
2458 * Add track to in_ram playlist. Used when playing directories.
2460 int playlist_add(const char *filename)
2462 struct playlist_info* playlist = &current_playlist;
2463 int len = strlen(filename);
2465 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2466 (playlist->amount >= playlist->max_playlist_size))
2468 display_buffer_full();
2469 return -1;
2472 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2473 #ifdef HAVE_DIRCACHE
2474 playlist->filenames[playlist->amount] = -1;
2475 #endif
2476 playlist->amount++;
2478 strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
2479 playlist->buffer_end_pos += len;
2480 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2482 return 0;
2485 /* shuffle newly created playlist using random seed. */
2486 int playlist_shuffle(int random_seed, int start_index)
2488 struct playlist_info* playlist = &current_playlist;
2490 bool start_current = false;
2492 if (start_index >= 0 && global_settings.play_selected)
2494 /* store the seek position before the shuffle */
2495 playlist->index = playlist->first_index = start_index;
2496 start_current = true;
2499 randomise_playlist(playlist, random_seed, start_current, true);
2501 return playlist->index;
2504 /* start playing current playlist at specified index/offset */
2505 void playlist_start(int start_index, int offset)
2507 struct playlist_info* playlist = &current_playlist;
2509 /* Cancel FM radio selection as previous music. For cases where we start
2510 playback without going to the WPS, such as playlist insert.. or
2511 playlist catalog. */
2512 previous_music_is_wps();
2514 playlist->index = start_index;
2516 playlist->started = true;
2517 sync_control(playlist, false);
2518 audio_play(offset);
2521 /* Returns false if 'steps' is out of bounds, else true */
2522 bool playlist_check(int steps)
2524 struct playlist_info* playlist = &current_playlist;
2526 /* always allow folder navigation */
2527 if (global_settings.next_folder && playlist->in_ram)
2528 return true;
2530 int index = get_next_index(playlist, steps, -1);
2532 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2533 index = get_next_index(playlist, steps, REPEAT_ALL);
2535 return (index >= 0);
2538 /* get trackname of track that is "steps" away from current playing track.
2539 NULL is used to identify end of playlist */
2540 const char* playlist_peek(int steps, char* buf, size_t buf_size)
2542 struct playlist_info* playlist = &current_playlist;
2543 int seek;
2544 char *temp_ptr;
2545 int index;
2546 bool control_file;
2548 index = get_next_index(playlist, steps, -1);
2549 if (index < 0)
2550 return NULL;
2552 #if CONFIG_CODEC == SWCODEC
2553 /* Just testing - don't care about the file name */
2554 if (!buf || !buf_size)
2555 return "";
2556 #endif
2558 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2559 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2561 if (get_filename(playlist, index, seek, control_file, buf,
2562 buf_size) < 0)
2563 return NULL;
2565 temp_ptr = buf;
2567 if (!playlist->in_ram || control_file)
2569 /* remove bogus dirs from beginning of path
2570 (workaround for buggy playlist creation tools) */
2571 while (temp_ptr)
2573 if (file_exists(temp_ptr))
2574 break;
2576 temp_ptr = strchr(temp_ptr+1, '/');
2579 if (!temp_ptr)
2581 /* Even though this is an invalid file, we still need to pass a
2582 file name to the caller because NULL is used to indicate end
2583 of playlist */
2584 return buf;
2588 return temp_ptr;
2592 * Update indices as track has changed
2594 int playlist_next(int steps)
2596 struct playlist_info* playlist = &current_playlist;
2597 int index;
2599 if ( (steps > 0)
2600 #ifdef AB_REPEAT_ENABLE
2601 && (global_settings.repeat_mode != REPEAT_AB)
2602 #endif
2603 && (global_settings.repeat_mode != REPEAT_ONE) )
2605 int i, j;
2607 /* We need to delete all the queued songs */
2608 for (i=0, j=steps; i<j; i++)
2610 index = get_next_index(playlist, i, -1);
2612 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2614 remove_track_from_playlist(playlist, index, true);
2615 steps--; /* one less track */
2620 index = get_next_index(playlist, steps, -1);
2622 if (index < 0)
2624 /* end of playlist... or is it */
2625 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2626 playlist->amount > 1)
2628 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2629 playlist->first_index = 0;
2630 sort_playlist(playlist, false, false);
2631 randomise_playlist(playlist, current_tick, false, true);
2633 #if CONFIG_CODEC == SWCODEC
2634 playlist->started = true;
2635 #else
2636 playlist_start(0, 0);
2637 #endif
2638 playlist->index = 0;
2639 index = 0;
2641 else if (playlist->in_ram && global_settings.next_folder)
2643 index = create_and_play_dir(steps, true);
2645 if (index >= 0)
2647 playlist->index = index;
2651 return index;
2654 playlist->index = index;
2656 if (playlist->last_insert_pos >= 0 && steps > 0)
2658 /* check to see if we've gone beyond the last inserted track */
2659 int cur = rotate_index(playlist, index);
2660 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2662 if (cur > last_pos)
2664 /* reset last inserted track */
2665 playlist->last_insert_pos = -1;
2667 if (playlist->control_fd >= 0)
2669 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2670 -1, -1, NULL, NULL, NULL);
2672 if (result < 0)
2673 return result;
2675 sync_control(playlist, false);
2680 return index;
2683 #if CONFIG_CODEC == SWCODEC
2684 /* try playing next or previous folder */
2685 bool playlist_next_dir(int direction)
2687 /* not to mess up real playlists */
2688 if(!current_playlist.in_ram)
2689 return false;
2691 return create_and_play_dir(direction, false) >= 0;
2693 #endif /* CONFIG_CODEC == SWCODEC */
2695 /* Get resume info for current playing song. If return value is -1 then
2696 settings shouldn't be saved. */
2697 int playlist_get_resume_info(int *resume_index)
2699 struct playlist_info* playlist = &current_playlist;
2701 *resume_index = playlist->index;
2703 return 0;
2706 /* Update resume info for current playing song. Returns -1 on error. */
2707 int playlist_update_resume_info(const struct mp3entry* id3)
2709 struct playlist_info* playlist = &current_playlist;
2711 if (id3)
2713 if (global_status.resume_index != playlist->index ||
2714 global_status.resume_offset != id3->offset)
2716 global_status.resume_index = playlist->index;
2717 global_status.resume_offset = id3->offset;
2718 status_save();
2721 else
2723 global_status.resume_index = -1;
2724 global_status.resume_offset = -1;
2725 status_save();
2728 return 0;
2731 /* Returns index of current playing track for display purposes. This value
2732 should not be used for resume purposes as it doesn't represent the actual
2733 index into the playlist */
2734 int playlist_get_display_index(void)
2736 struct playlist_info* playlist = &current_playlist;
2738 /* first_index should always be index 0 for display purposes */
2739 int index = rotate_index(playlist, playlist->index);
2741 return (index+1);
2744 /* returns number of tracks in current playlist */
2745 int playlist_amount(void)
2747 return playlist_amount_ex(NULL);
2749 /* set playlist->last_shuffle_start to playlist->amount for
2750 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2751 void playlist_set_last_shuffled_start(void)
2753 struct playlist_info* playlist = &current_playlist;
2754 playlist->last_shuffled_start = playlist->amount;
2757 * Create a new playlist If playlist is not NULL then we're loading a
2758 * playlist off disk for viewing/editing. The index_buffer is used to store
2759 * playlist indices (required for and only used if !current playlist). The
2760 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2762 int playlist_create_ex(struct playlist_info* playlist,
2763 const char* dir, const char* file,
2764 void* index_buffer, int index_buffer_size,
2765 void* temp_buffer, int temp_buffer_size)
2767 if (!playlist)
2768 playlist = &current_playlist;
2769 else
2771 /* Initialize playlist structure */
2772 int r = rand() % 10;
2773 playlist->current = false;
2775 /* Use random name for control file */
2776 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2777 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2778 playlist->fd = -1;
2779 playlist->control_fd = -1;
2781 if (index_buffer)
2783 int num_indices = index_buffer_size / sizeof(int);
2785 #ifdef HAVE_DIRCACHE
2786 num_indices /= 2;
2787 #endif
2788 if (num_indices > global_settings.max_files_in_playlist)
2789 num_indices = global_settings.max_files_in_playlist;
2791 playlist->max_playlist_size = num_indices;
2792 playlist->indices = index_buffer;
2793 #ifdef HAVE_DIRCACHE
2794 playlist->filenames = (int*)&playlist->indices[num_indices];
2795 #endif
2797 else
2799 playlist->max_playlist_size = current_playlist.max_playlist_size;
2800 playlist->indices = current_playlist.indices;
2801 #ifdef HAVE_DIRCACHE
2802 playlist->filenames = current_playlist.filenames;
2803 #endif
2806 playlist->buffer_size = 0;
2807 playlist->buffer_handle = -1;
2808 playlist->buffer = NULL;
2809 playlist->control_mutex = &created_playlist_mutex;
2812 new_playlist(playlist, dir, file);
2814 if (file)
2815 /* load the playlist file */
2816 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2818 return 0;
2822 * Set the specified playlist as the current.
2823 * NOTE: You will get undefined behaviour if something is already playing so
2824 * remember to stop before calling this. Also, this call will
2825 * effectively close your playlist, making it unusable.
2827 int playlist_set_current(struct playlist_info* playlist)
2829 if (!playlist || (check_control(playlist) < 0))
2830 return -1;
2832 empty_playlist(&current_playlist, false);
2834 strlcpy(current_playlist.filename, playlist->filename,
2835 sizeof(current_playlist.filename));
2837 current_playlist.utf8 = playlist->utf8;
2838 current_playlist.fd = playlist->fd;
2840 close(playlist->control_fd);
2841 close(current_playlist.control_fd);
2842 remove(current_playlist.control_filename);
2843 if (rename(playlist->control_filename,
2844 current_playlist.control_filename) < 0)
2845 return -1;
2846 current_playlist.control_fd = open(current_playlist.control_filename,
2847 O_RDWR);
2848 if (current_playlist.control_fd < 0)
2849 return -1;
2850 current_playlist.control_created = true;
2852 current_playlist.dirlen = playlist->dirlen;
2854 if (playlist->indices && playlist->indices != current_playlist.indices)
2856 memcpy((void*)current_playlist.indices, (void*)playlist->indices,
2857 playlist->max_playlist_size*sizeof(int));
2858 #ifdef HAVE_DIRCACHE
2859 memcpy((void*)current_playlist.filenames, (void*)playlist->filenames,
2860 playlist->max_playlist_size*sizeof(int));
2861 #endif
2864 current_playlist.first_index = playlist->first_index;
2865 current_playlist.amount = playlist->amount;
2866 current_playlist.last_insert_pos = playlist->last_insert_pos;
2867 current_playlist.seed = playlist->seed;
2868 current_playlist.shuffle_modified = playlist->shuffle_modified;
2869 current_playlist.deleted = playlist->deleted;
2870 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2872 memcpy(current_playlist.control_cache, playlist->control_cache,
2873 sizeof(current_playlist.control_cache));
2874 current_playlist.num_cached = playlist->num_cached;
2875 current_playlist.pending_control_sync = playlist->pending_control_sync;
2877 return 0;
2879 struct playlist_info *playlist_get_current(void)
2881 return &current_playlist;
2884 * Close files and delete control file for non-current playlist.
2886 void playlist_close(struct playlist_info* playlist)
2888 if (!playlist)
2889 return;
2891 if (playlist->fd >= 0)
2892 close(playlist->fd);
2894 if (playlist->control_fd >= 0)
2895 close(playlist->control_fd);
2897 if (playlist->control_created)
2898 remove(playlist->control_filename);
2901 void playlist_sync(struct playlist_info* playlist)
2903 if (!playlist)
2904 playlist = &current_playlist;
2906 sync_control(playlist, false);
2907 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2908 audio_flush_and_reload_tracks();
2910 #ifdef HAVE_DIRCACHE
2911 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2912 #endif
2916 * Insert track into playlist at specified position (or one of the special
2917 * positions). Returns position where track was inserted or -1 if error.
2919 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2920 int position, bool queue, bool sync)
2922 int result;
2924 if (!playlist)
2925 playlist = &current_playlist;
2927 if (check_control(playlist) < 0)
2929 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2930 return -1;
2933 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2935 /* Check if we want manually sync later. For example when adding
2936 * bunch of files from tagcache, syncing after every file wouldn't be
2937 * a good thing to do. */
2938 if (sync && result >= 0)
2939 playlist_sync(playlist);
2941 return result;
2945 * Insert all tracks from specified directory into playlist.
2947 int playlist_insert_directory(struct playlist_info* playlist,
2948 const char *dirname, int position, bool queue,
2949 bool recurse)
2951 int result;
2952 unsigned char *count_str;
2953 struct directory_search_context context;
2955 if (!playlist)
2956 playlist = &current_playlist;
2958 if (check_control(playlist) < 0)
2960 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2961 return -1;
2964 if (position == PLAYLIST_REPLACE)
2966 if (playlist_remove_all_tracks(playlist) == 0)
2967 position = PLAYLIST_INSERT_LAST;
2968 else
2969 return -1;
2972 if (queue)
2973 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2974 else
2975 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2977 display_playlist_count(0, count_str, false);
2979 context.playlist = playlist;
2980 context.position = position;
2981 context.queue = queue;
2982 context.count = 0;
2984 cpu_boost(true);
2986 result = playlist_directory_tracksearch(dirname, recurse,
2987 directory_search_callback, &context);
2989 sync_control(playlist, false);
2991 cpu_boost(false);
2993 display_playlist_count(context.count, count_str, true);
2995 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2996 audio_flush_and_reload_tracks();
2998 #ifdef HAVE_DIRCACHE
2999 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3000 #endif
3002 return result;
3006 * Insert all tracks from specified playlist into dynamic playlist.
3008 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
3009 int position, bool queue)
3011 int fd;
3012 int max;
3013 char *temp_ptr;
3014 const char *dir;
3015 unsigned char *count_str;
3016 char temp_buf[MAX_PATH+1];
3017 char trackname[MAX_PATH+1];
3018 int count = 0;
3019 int result = 0;
3020 bool utf8 = is_m3u8(filename);
3022 if (!playlist)
3023 playlist = &current_playlist;
3025 if (check_control(playlist) < 0)
3027 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3028 return -1;
3031 fd = open_utf8(filename, O_RDONLY);
3032 if (fd < 0)
3034 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3035 return -1;
3038 /* we need the directory name for formatting purposes */
3039 dir = filename;
3041 temp_ptr = strrchr(filename+1,'/');
3042 if (temp_ptr)
3043 *temp_ptr = 0;
3044 else
3045 dir = "/";
3047 if (queue)
3048 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
3049 else
3050 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
3052 display_playlist_count(count, count_str, false);
3054 if (position == PLAYLIST_REPLACE)
3056 if (playlist_remove_all_tracks(playlist) == 0)
3057 position = PLAYLIST_INSERT_LAST;
3058 else return -1;
3061 cpu_boost(true);
3063 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3065 /* user abort */
3066 if (action_userabort(TIMEOUT_NOBLOCK))
3067 break;
3069 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3071 int insert_pos;
3073 if (!utf8)
3075 /* Use trackname as a temporay buffer. Note that trackname must
3076 * be as large as temp_buf.
3078 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3081 /* we need to format so that relative paths are correctly
3082 handled */
3083 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3084 dir) < 0)
3086 result = -1;
3087 break;
3090 insert_pos = add_track_to_playlist(playlist, trackname, position,
3091 queue, -1);
3093 if (insert_pos < 0)
3095 result = -1;
3096 break;
3099 /* Make sure tracks are inserted in correct order if user
3100 requests INSERT_FIRST */
3101 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3102 position = insert_pos + 1;
3104 count++;
3106 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3108 display_playlist_count(count, count_str, false);
3110 if (count == PLAYLIST_DISPLAY_COUNT &&
3111 (audio_status() & AUDIO_STATUS_PLAY) &&
3112 playlist->started)
3113 audio_flush_and_reload_tracks();
3117 /* let the other threads work */
3118 yield();
3121 close(fd);
3123 if (temp_ptr)
3124 *temp_ptr = '/';
3126 sync_control(playlist, false);
3128 cpu_boost(false);
3130 display_playlist_count(count, count_str, true);
3132 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3133 audio_flush_and_reload_tracks();
3135 #ifdef HAVE_DIRCACHE
3136 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3137 #endif
3139 return result;
3143 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3144 * we want to delete the current playing track.
3146 int playlist_delete(struct playlist_info* playlist, int index)
3148 int result = 0;
3150 if (!playlist)
3151 playlist = &current_playlist;
3153 if (check_control(playlist) < 0)
3155 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3156 return -1;
3159 if (index == PLAYLIST_DELETE_CURRENT)
3160 index = playlist->index;
3162 result = remove_track_from_playlist(playlist, index, true);
3164 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3165 playlist->started)
3166 audio_flush_and_reload_tracks();
3168 return result;
3172 * Move track at index to new_index. Tracks between the two are shifted
3173 * appropriately. Returns 0 on success and -1 on failure.
3175 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3177 int result;
3178 int seek;
3179 bool control_file;
3180 bool queue;
3181 bool current = false;
3182 int r;
3183 char filename[MAX_PATH];
3185 if (!playlist)
3186 playlist = &current_playlist;
3188 if (check_control(playlist) < 0)
3190 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3191 return -1;
3194 if (index == new_index)
3195 return -1;
3197 if (index == playlist->index)
3198 /* Moving the current track */
3199 current = true;
3201 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3202 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3203 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3205 if (get_filename(playlist, index, seek, control_file, filename,
3206 sizeof(filename)) < 0)
3207 return -1;
3209 /* We want to insert the track at the position that was specified by
3210 new_index. This may be different then new_index because of the
3211 shifting that will occur after the delete.
3212 We calculate this before we do the remove as it depends on the
3213 size of the playlist before the track removal */
3214 r = rotate_index(playlist, new_index);
3216 /* Delete track from original position */
3217 result = remove_track_from_playlist(playlist, index, true);
3219 if (result != -1)
3221 if (r == 0)
3222 /* First index */
3223 new_index = PLAYLIST_PREPEND;
3224 else if (r == playlist->amount)
3225 /* Append */
3226 new_index = PLAYLIST_INSERT_LAST;
3227 else
3228 /* Calculate index of desired position */
3229 new_index = (r+playlist->first_index)%playlist->amount;
3231 result = add_track_to_playlist(playlist, filename, new_index, queue,
3232 -1);
3234 if (result != -1)
3236 if (current)
3238 /* Moved the current track */
3239 switch (new_index)
3241 case PLAYLIST_PREPEND:
3242 playlist->index = playlist->first_index;
3243 break;
3244 case PLAYLIST_INSERT_LAST:
3245 playlist->index = playlist->first_index - 1;
3246 if (playlist->index < 0)
3247 playlist->index += playlist->amount;
3248 break;
3249 default:
3250 playlist->index = new_index;
3251 break;
3255 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3256 audio_flush_and_reload_tracks();
3260 #ifdef HAVE_DIRCACHE
3261 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3262 #endif
3264 return result;
3267 /* shuffle currently playing playlist */
3268 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3269 bool start_current)
3271 int result;
3273 if (!playlist)
3274 playlist = &current_playlist;
3276 check_control(playlist);
3278 result = randomise_playlist(playlist, seed, start_current, true);
3280 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3281 playlist->started)
3282 audio_flush_and_reload_tracks();
3284 return result;
3287 /* sort currently playing playlist */
3288 int playlist_sort(struct playlist_info* playlist, bool start_current)
3290 int result;
3292 if (!playlist)
3293 playlist = &current_playlist;
3295 check_control(playlist);
3297 result = sort_playlist(playlist, start_current, true);
3299 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3300 playlist->started)
3301 audio_flush_and_reload_tracks();
3303 return result;
3306 /* returns true if playlist has been modified */
3307 bool playlist_modified(const struct playlist_info* playlist)
3309 if (!playlist)
3310 playlist = &current_playlist;
3312 if (playlist->shuffle_modified ||
3313 playlist->deleted ||
3314 playlist->num_inserted_tracks > 0)
3315 return true;
3317 return false;
3320 /* returns index of first track in playlist */
3321 int playlist_get_first_index(const struct playlist_info* playlist)
3323 if (!playlist)
3324 playlist = &current_playlist;
3326 return playlist->first_index;
3329 /* returns shuffle seed of playlist */
3330 int playlist_get_seed(const struct playlist_info* playlist)
3332 if (!playlist)
3333 playlist = &current_playlist;
3335 return playlist->seed;
3338 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3339 int playlist_amount_ex(const struct playlist_info* playlist)
3341 if (!playlist)
3342 playlist = &current_playlist;
3344 return playlist->amount;
3347 /* returns full path of playlist (minus extension) */
3348 char *playlist_name(const struct playlist_info* playlist, char *buf,
3349 int buf_size)
3351 char *sep;
3353 if (!playlist)
3354 playlist = &current_playlist;
3356 strlcpy(buf, playlist->filename+playlist->dirlen, buf_size);
3358 if (!buf[0])
3359 return NULL;
3361 /* Remove extension */
3362 sep = strrchr(buf, '.');
3363 if (sep)
3364 *sep = 0;
3366 return buf;
3369 /* returns the playlist filename */
3370 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3371 int buf_size)
3373 if (!playlist)
3374 playlist = &current_playlist;
3376 strlcpy(buf, playlist->filename, buf_size);
3378 if (!buf[0])
3379 return NULL;
3381 return buf;
3384 /* Fills info structure with information about track at specified index.
3385 Returns 0 on success and -1 on failure */
3386 int playlist_get_track_info(struct playlist_info* playlist, int index,
3387 struct playlist_track_info* info)
3389 int seek;
3390 bool control_file;
3392 if (!playlist)
3393 playlist = &current_playlist;
3395 if (index < 0 || index >= playlist->amount)
3396 return -1;
3398 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3399 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3401 if (get_filename(playlist, index, seek, control_file, info->filename,
3402 sizeof(info->filename)) < 0)
3403 return -1;
3405 info->attr = 0;
3407 if (control_file)
3409 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3410 info->attr |= PLAYLIST_ATTR_QUEUED;
3411 else
3412 info->attr |= PLAYLIST_ATTR_INSERTED;
3416 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3417 info->attr |= PLAYLIST_ATTR_SKIPPED;
3419 info->index = index;
3420 info->display_index = rotate_index(playlist, index) + 1;
3422 return 0;
3425 /* save the current dynamic playlist to specified file */
3426 int playlist_save(struct playlist_info* playlist, char *filename)
3428 int fd;
3429 int i, index;
3430 int count = 0;
3431 char path[MAX_PATH+1];
3432 char tmp_buf[MAX_PATH+1];
3433 int result = 0;
3434 bool overwrite_current = false;
3436 if (!playlist)
3437 playlist = &current_playlist;
3439 if (playlist->amount <= 0)
3440 return -1;
3442 /* use current working directory as base for pathname */
3443 if (format_track_path(path, filename, sizeof(tmp_buf),
3444 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3445 return -1;
3447 /* can ignore volatile here, because core_get_data() is called later */
3448 char* old_buffer = (char*)playlist->buffer;
3449 size_t old_buffer_size = playlist->buffer_size;
3451 if (!strncmp(playlist->filename, path, strlen(path)))
3453 /* Attempting to overwrite current playlist file.*/
3455 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3457 /* not enough buffer space to store updated indices */
3458 /* Try to get a buffer */
3459 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size);
3460 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3462 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3463 result = -1;
3464 goto reset_old_buffer;
3468 /* use temporary pathname */
3469 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3470 overwrite_current = true;
3473 if (is_m3u8(path))
3475 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
3477 else
3479 /* some applications require a BOM to read the file properly */
3480 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
3482 if (fd < 0)
3484 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3485 result = -1;
3486 goto reset_old_buffer;
3489 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3491 cpu_boost(true);
3493 index = playlist->first_index;
3494 for (i=0; i<playlist->amount; i++)
3496 bool control_file;
3497 bool queue;
3498 int seek;
3500 /* user abort */
3501 if (action_userabort(TIMEOUT_NOBLOCK))
3503 result = -1;
3504 break;
3507 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3508 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3509 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3511 /* Don't save queued files */
3512 if (!queue)
3514 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3515 MAX_PATH+1) < 0)
3517 result = -1;
3518 break;
3521 if (overwrite_current)
3522 playlist->seek_buf[count] = lseek(fd, 0, SEEK_CUR);
3524 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3526 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3527 result = -1;
3528 break;
3531 count++;
3533 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3534 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3535 false);
3537 yield();
3540 index = (index+1)%playlist->amount;
3543 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3545 close(fd);
3547 if (overwrite_current && result >= 0)
3549 result = -1;
3551 mutex_lock(playlist->control_mutex);
3553 /* Replace the current playlist with the new one and update indices */
3554 close(playlist->fd);
3555 if (remove(playlist->filename) >= 0)
3557 if (rename(path, playlist->filename) >= 0)
3559 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
3560 if (playlist->fd >= 0)
3562 index = playlist->first_index;
3563 for (i=0, count=0; i<playlist->amount; i++)
3565 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3567 playlist->indices[index] = playlist->seek_buf[count];
3568 count++;
3570 index = (index+1)%playlist->amount;
3573 /* we need to recreate control because inserted tracks are
3574 now part of the playlist and shuffle has been
3575 invalidated */
3576 result = recreate_control(playlist);
3581 mutex_unlock(playlist->control_mutex);
3585 cpu_boost(false);
3587 reset_old_buffer:
3588 if (playlist->buffer_handle > 0)
3589 old_buffer = core_get_data(playlist->buffer_handle);
3590 playlist->buffer = old_buffer;
3591 playlist->buffer_size = old_buffer_size;
3593 return result;
3597 * Search specified directory for tracks and notify via callback. May be
3598 * called recursively.
3600 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3601 int (*callback)(char*, void*),
3602 void* context)
3604 char buf[MAX_PATH+1];
3605 int result = 0;
3606 int num_files = 0;
3607 int i;;
3608 struct tree_context* tc = tree_get_context();
3609 struct tree_cache* cache = &tc->cache;
3610 int old_dirfilter = *(tc->dirfilter);
3612 if (!callback)
3613 return -1;
3615 /* use the tree browser dircache to load files */
3616 *(tc->dirfilter) = SHOW_ALL;
3618 if (ft_load(tc, dirname) < 0)
3620 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3621 *(tc->dirfilter) = old_dirfilter;
3622 return -1;
3625 num_files = tc->filesindir;
3627 /* we've overwritten the dircache so tree browser will need to be
3628 reloaded */
3629 reload_directory();
3631 for (i=0; i<num_files; i++)
3633 /* user abort */
3634 if (action_userabort(TIMEOUT_NOBLOCK))
3636 result = -1;
3637 break;
3640 struct entry *files = core_get_data(cache->entries_handle);
3641 if (files[i].attr & ATTR_DIRECTORY)
3643 if (recurse)
3645 /* recursively add directories */
3646 snprintf(buf, sizeof(buf), "%s/%s",
3647 dirname[1]? dirname: "", files[i].name);
3648 result = playlist_directory_tracksearch(buf, recurse,
3649 callback, context);
3650 if (result < 0)
3651 break;
3653 /* we now need to reload our current directory */
3654 if(ft_load(tc, dirname) < 0)
3656 result = -1;
3657 break;
3660 num_files = tc->filesindir;
3661 if (!num_files)
3663 result = -1;
3664 break;
3667 else
3668 continue;
3670 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3672 snprintf(buf, sizeof(buf), "%s/%s",
3673 dirname[1]? dirname: "", files[i].name);
3675 if (callback(buf, context) != 0)
3677 result = -1;
3678 break;
3681 /* let the other threads work */
3682 yield();
3686 /* restore dirfilter */
3687 *(tc->dirfilter) = old_dirfilter;
3689 return result;