blob: b6f19418eda6a0c8d1062184b079b31edd22f0fd [file] [log] [blame]
Daniel Stenberg4d1fb482002-06-04 21:43:38 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by wavey@wavey.org
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Daniel Stenberg4d1fb482002-06-04 21:43:38 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
Hardeep Sidhu9e426202003-07-01 21:05:43 +000022/*
23 Dynamic playlist design (based on design originally proposed by ricII)
24
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.
32
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.
38
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
60
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
Hardeep Sidhu107ebc52004-01-26 17:05:21 +000065 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.
Hardeep Sidhu9e426202003-07-01 21:05:43 +000068 */
69
Daniel Stenberg4d1fb482002-06-04 21:43:38 +000070#include <stdio.h>
Thomas Martitz50a6ca32010-05-06 21:04:40 +000071#include <stdlib.h>
72#include <ctype.h>
73#include "string-extra.h"
Daniel Stenberg4d1fb482002-06-04 21:43:38 +000074#include "playlist.h"
Jonathan Gordon327f8452008-04-30 09:23:12 +000075#include "ata_idle_notify.h"
Björn Stenbergc78e1b02003-01-09 00:55:00 +000076#include "file.h"
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +000077#include "action.h"
Thomas Martitz706e6b72014-02-18 07:11:11 +010078#include "mv.h"
Daniel Stenberg4d1fb482002-06-04 21:43:38 +000079#include "debug.h"
Linus Nielsen Feltzing8a237a82005-04-04 12:06:29 +000080#include "audio.h"
Björn Stenberg4f004502002-06-13 15:16:41 +000081#include "lcd.h"
82#include "kernel.h"
Björn Stenberg9fbdae52002-06-27 10:10:30 +000083#include "settings.h"
Markus Braunde8fbf02002-08-07 10:35:26 +000084#include "status.h"
Linus Nielsen Feltzing5917e812002-08-15 12:28:52 +000085#include "applimits.h"
Robert Hakf6ee2f62003-03-18 23:11:12 +000086#include "screens.h"
Thomas Martitzd0b72e22011-08-30 14:01:33 +000087#include "core_alloc.h"
Hardeep Sidhu9e426202003-07-01 21:05:43 +000088#include "misc.h"
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040089#include "pathfuncs.h"
Hardeep Sidhu9e426202003-07-01 21:05:43 +000090#include "button.h"
Björn Stenberg8a5de5f2005-01-17 11:39:46 +000091#include "filetree.h"
Linus Nielsen Feltzing0ad617c2005-08-21 23:01:12 +000092#include "abrepeat.h"
Miika Pekkarinen735f4532005-11-17 19:31:29 +000093#include "thread.h"
94#include "usb.h"
Jonathan Gordon36a2e302007-04-18 13:03:01 +000095#include "filetypes.h"
Markus Braunde8fbf02002-08-07 10:35:26 +000096#ifdef HAVE_LCD_BITMAP
Markus Braunc41dcb92002-08-20 19:39:55 +000097#include "icons.h"
Markus Braunc41dcb92002-08-20 19:39:55 +000098#endif
Bertrik Sikken66cf3a32009-03-08 16:10:40 +000099#include "system.h"
Daniel Stenberg4d1fb482002-06-04 21:43:38 +0000100
Björn Stenberg505eca72002-09-18 14:08:05 +0000101#include "lang.h"
Jörg Hohensohn4f36ea82004-03-14 21:33:53 +0000102#include "talk.h"
Kevin Ferrare3aa842c2005-11-06 17:30:53 +0000103#include "splash.h"
Magnus Holmgrena515b102006-11-29 19:22:44 +0000104#include "rbunicode.h"
Stéphane Doyon6d081162007-10-12 04:20:20 +0000105#include "root_menu.h"
Jonathan Gordond1fd4f02011-07-21 06:40:21 +0000106#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
Thomas Martitz22e802e2013-05-30 11:24:16 +0200107#include "panic.h"
Thomas Martitzbebf71a2014-04-08 22:52:37 +0200108#include "logdiskf.h"
Michael Sevakis16a9f842017-01-17 14:45:07 -0500109#ifdef HAVE_DIRCACHE
110#include "dircache.h"
111#endif
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400112
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000113#define PLAYLIST_CONTROL_FILE_VERSION 2
Björn Stenberg728868a2003-03-07 14:38:51 +0000114
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000115/*
116 Each playlist index has a flag associated with it which identifies what
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000117 type of track it is. These flags are stored in the 4 high order bits of
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000118 the index.
119
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000120 NOTE: This limits the playlist file size to a max of 256M.
Linus Nielsen Feltzingd8cc2852002-08-06 13:09:48 +0000121
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000122 Bits 31-30:
123 00 = Playlist track
124 01 = Track was prepended into playlist
125 10 = Track was inserted into playlist
126 11 = Track was appended into playlist
127 Bit 29:
128 0 = Added track
129 1 = Queued track
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000130 Bit 28:
131 0 = Track entry is valid
132 1 = Track does not exist on disk and should be skipped
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000133 */
Miika Pekkarinen56bb45c2005-10-21 07:25:19 +0000134#define PLAYLIST_SEEK_MASK 0x0FFFFFFF
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000135#define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
136#define PLAYLIST_QUEUE_MASK 0x20000000
137
138#define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
139#define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
140#define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
141
142#define PLAYLIST_QUEUED 0x20000000
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000143#define PLAYLIST_SKIPPED 0x10000000
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000144
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000145struct directory_search_context {
146 struct playlist_info* playlist;
147 int position;
148 bool queue;
149 int count;
150};
151
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000152static struct playlist_info current_playlist;
Daniel Stenberg4d1fb482002-06-04 21:43:38 +0000153
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000154static void empty_playlist(struct playlist_info* playlist, bool resume);
Jens Arnold8fb33612004-08-18 01:09:31 +0000155static void new_playlist(struct playlist_info* playlist, const char *dir,
156 const char *file);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000157static void create_control(struct playlist_info* playlist);
158static int check_control(struct playlist_info* playlist);
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000159static int recreate_control(struct playlist_info* playlist);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000160static void update_playlist_filename(struct playlist_info* playlist,
Jens Arnold8fb33612004-08-18 01:09:31 +0000161 const char *dir, const char *file);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000162static int add_indices_to_playlist(struct playlist_info* playlist,
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000163 char* buffer, size_t buflen);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000164static int add_track_to_playlist(struct playlist_info* playlist,
Hardeep Sidhu71d22812005-07-01 11:25:16 +0000165 const char *filename, int position,
Jens Arnold8fb33612004-08-18 01:09:31 +0000166 bool queue, int seek_pos);
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000167static int directory_search_callback(char* filename, void* context);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000168static int remove_track_from_playlist(struct playlist_info* playlist,
169 int position, bool write);
170static int randomise_playlist(struct playlist_info* playlist,
171 unsigned int seed, bool start_current,
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000172 bool write);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000173static int sort_playlist(struct playlist_info* playlist, bool start_current,
174 bool write);
Hardeep Sidhu965c86a2005-06-25 06:28:55 +0000175static int get_next_index(const struct playlist_info* playlist, int steps,
Hardeep Sidhu74d082c2005-06-25 04:46:25 +0000176 int repeat_mode);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000177static void find_and_set_playlist_index(struct playlist_info* playlist,
178 unsigned int seek);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000179static int compare(const void* p1, const void* p2);
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000180static int get_filename(struct playlist_info* playlist, int index, int seek,
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000181 bool control_file, char *buf, int buf_length);
Hardeep Sidhu71d22812005-07-01 11:25:16 +0000182static int get_next_directory(char *dir);
Nick Peskettbe108172012-03-19 09:56:38 +0000183static int get_next_dir(char *dir, bool is_forward);
Anton Oleynikovd102e1f2005-11-02 22:32:04 +0000184static int get_previous_directory(char *dir);
Nils Wallménius48b52ae2008-10-08 16:32:01 +0000185static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400186static ssize_t format_track_path(char *dest, char *src, int buf_length,
187 const char *dir);
Nils Wallménius5b769362007-08-06 13:08:36 +0000188static void display_playlist_count(int count, const unsigned char *fmt,
189 bool final);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000190static void display_buffer_full(void);
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000191static int flush_cached_control(struct playlist_info* playlist);
192static int update_control(struct playlist_info* playlist,
193 enum playlist_command command, int i1, int i2,
194 const char* s1, const char* s2, void* data);
195static void sync_control(struct playlist_info* playlist, bool force);
Jens Arnold8fb33612004-08-18 01:09:31 +0000196static int rotate_index(const struct playlist_info* playlist, int index);
Björn Stenberg7bb746b2003-04-24 17:31:36 +0000197
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000198#ifdef HAVE_DIRCACHE
199#define PLAYLIST_LOAD_POINTERS 1
200
Michael Sevakisb15aa472011-02-14 11:27:45 +0000201static struct event_queue playlist_queue SHAREDBSS_ATTR;
Miika Pekkarinencd605cf2006-11-30 19:42:41 +0000202static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000203static const char playlist_thread_name[] = "playlist cachectrl";
204#endif
205
Michael Sevakisb15aa472011-02-14 11:27:45 +0000206static struct mutex current_playlist_mutex SHAREDBSS_ATTR;
207static struct mutex created_playlist_mutex SHAREDBSS_ATTR;
208
Michael Sevakis16a9f842017-01-17 14:45:07 -0500209#ifdef HAVE_DIRCACHE
210static void copy_filerefs(struct dircache_fileref *dcfto,
211 const struct dircache_fileref *dcffrom,
212 int count)
213{
214 if (!dcfto)
215 return;
216
217 if (dcffrom)
218 memmove(dcfto, dcffrom, count * sizeof (*dcfto));
219 else
220 {
221 /* just initialize the destination */
222 for (int i = 0; i < count; i++, dcfto++)
223 dircache_fileref_init(dcfto);
224 }
225}
226#endif /* HAVE_DIRCACHE */
227
Magnus Holmgren6e068252007-11-13 18:49:20 +0000228/* Check if the filename suggests M3U or M3U8 format. */
229static bool is_m3u8(const char* filename)
230{
231 int len = strlen(filename);
232
233 /* Default to M3U8 unless explicitly told otherwise. */
234 return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
235}
236
Magnus Holmgren6e068252007-11-13 18:49:20 +0000237
238/* Convert a filename in an M3U playlist to UTF-8.
239 *
240 * buf - the filename to convert; can contain more than one line from the
241 * playlist.
242 * buf_len - amount of buf that is used.
243 * buf_max - total size of buf.
244 * temp - temporary conversion buffer, at least buf_max bytes.
245 *
246 * Returns the length of the converted filename.
247 */
248static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
249{
250 int i = 0;
251 char* dest;
252
253 /* Locate EOL. */
Thomas Jaroschf8d9e9c2014-12-20 13:09:22 +0100254 while ((i < buf_len) && (buf[i] != '\n') && (buf[i] != '\r'))
Magnus Holmgren6e068252007-11-13 18:49:20 +0000255 {
256 i++;
257 }
258
259 /* Work back killing white space. */
260 while ((i > 0) && isspace(buf[i - 1]))
261 {
262 i--;
263 }
264
265 buf_len = i;
266 dest = temp;
267
268 /* Convert char by char, so as to not overflow temp (iso_decode should
269 * preferably handle this). No more than 4 bytes should be generated for
270 * each input char.
271 */
272 for (i = 0; i < buf_len && dest < (temp + buf_max - 4); i++)
273 {
274 dest = iso_decode(&buf[i], dest, -1, 1);
275 }
276
277 *dest = 0;
278 strcpy(buf, temp);
279 return dest - temp;
280}
281
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000282/*
283 * remove any files and indices associated with the playlist
284 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000285static void empty_playlist(struct playlist_info* playlist, bool resume)
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000286{
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000287 playlist->filename[0] = '\0';
Magnus Holmgrena515b102006-11-29 19:22:44 +0000288 playlist->utf8 = true;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000289
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000290 if(playlist->fd >= 0)
Daniel Stenberg583821b2003-04-23 18:45:51 +0000291 /* If there is an already open playlist, close it. */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000292 close(playlist->fd);
293 playlist->fd = -1;
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000294
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000295 if(playlist->control_fd >= 0)
296 close(playlist->control_fd);
297 playlist->control_fd = -1;
298 playlist->control_created = false;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000299
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000300 playlist->in_ram = false;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000301
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000302 if (playlist->buffer)
303 playlist->buffer[0] = 0;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000304
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000305 playlist->buffer_end_pos = 0;
306
307 playlist->index = 0;
308 playlist->first_index = 0;
309 playlist->amount = 0;
310 playlist->last_insert_pos = -1;
311 playlist->seed = 0;
312 playlist->shuffle_modified = false;
313 playlist->deleted = false;
314 playlist->num_inserted_tracks = 0;
Hardeep Sidhub79c9a62006-04-19 02:22:23 +0000315 playlist->started = false;
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000316
317 playlist->num_cached = 0;
318 playlist->pending_control_sync = false;
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000319
320 if (!resume && playlist->current)
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000321 {
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000322 /* start with fresh playlist control file when starting new
323 playlist */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000324 create_control(playlist);
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000325 }
326}
327
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000328/*
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000329 * Initialize a new playlist for viewing/editing/playing. dir is the
330 * directory where the playlist is located and file is the filename.
331 */
Jens Arnold8fb33612004-08-18 01:09:31 +0000332static void new_playlist(struct playlist_info* playlist, const char *dir,
333 const char *file)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000334{
Steve Bavinad95df22008-05-12 17:52:50 +0000335 const char *fileused = file;
336 const char *dirused = dir;
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000337 empty_playlist(playlist, false);
338
Steve Bavinad95df22008-05-12 17:52:50 +0000339 if (!fileused)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000340 {
Steve Bavinad95df22008-05-12 17:52:50 +0000341 fileused = "";
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000342
Steve Bavinad95df22008-05-12 17:52:50 +0000343 if (dirused && playlist->current) /* !current cannot be in_ram */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000344 playlist->in_ram = true;
345 else
Steve Bavinad95df22008-05-12 17:52:50 +0000346 dirused = ""; /* empty playlist */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000347 }
348
Steve Bavinad95df22008-05-12 17:52:50 +0000349 update_playlist_filename(playlist, dirused, fileused);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000350
351 if (playlist->control_fd >= 0)
352 {
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000353 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
Steve Bavinad95df22008-05-12 17:52:50 +0000354 PLAYLIST_CONTROL_FILE_VERSION, -1, dirused, fileused, NULL);
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000355 sync_control(playlist, false);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000356 }
357}
358
359/*
360 * create control file for playlist
361 */
362static void create_control(struct playlist_info* playlist)
363{
Björn Stenberg30d8f612005-01-20 16:24:26 +0000364 playlist->control_fd = open(playlist->control_filename,
Thomas Martitz0a1d7c22010-05-06 17:35:13 +0000365 O_CREAT|O_RDWR|O_TRUNC, 0666);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000366 if (playlist->control_fd < 0)
Hardeep Sidhu586ec822004-08-02 05:11:20 +0000367 {
Henrik Backe66b45ee2004-09-10 20:51:12 +0000368 if (check_rockboxdir())
369 {
Nils Wallménius5b769362007-08-06 13:08:36 +0000370 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
Nils Wallménius01729e72008-08-15 08:27:39 +0000371 splashf(HZ*2, (unsigned char *)"%s (%d)",
Daniel Stenbergf981ea92005-12-05 22:44:42 +0000372 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
373 playlist->control_fd);
Henrik Backe66b45ee2004-09-10 20:51:12 +0000374 }
375 playlist->control_created = false;
Hardeep Sidhu586ec822004-08-02 05:11:20 +0000376 }
Henrik Backe66b45ee2004-09-10 20:51:12 +0000377 else
378 {
379 playlist->control_created = true;
380 }
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000381}
382
383/*
384 * validate the control file. This may include creating/initializing it if
385 * necessary;
386 */
387static int check_control(struct playlist_info* playlist)
388{
389 if (!playlist->control_created)
390 {
391 create_control(playlist);
392
393 if (playlist->control_fd >= 0)
394 {
395 char* dir = playlist->filename;
396 char* file = playlist->filename+playlist->dirlen;
397 char c = playlist->filename[playlist->dirlen-1];
398
399 playlist->filename[playlist->dirlen-1] = '\0';
400
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000401 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
402 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
403 sync_control(playlist, false);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000404 playlist->filename[playlist->dirlen-1] = c;
405 }
406 }
407
408 if (playlist->control_fd < 0)
409 return -1;
410
411 return 0;
412}
413
414/*
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000415 * recreate the control file based on current playlist entries
416 */
417static int recreate_control(struct playlist_info* playlist)
418{
William Wilgus2fb6b2b2018-07-25 18:33:10 +0200419 const char file_suffix[] = "_temp\0";
420 char temp_file[MAX_PATH + sizeof(file_suffix)];
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000421 int temp_fd = -1;
422 int i;
423 int result = 0;
424
Michael Sevakis16a9f842017-01-17 14:45:07 -0500425 temp_file[0] = 0;
426
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000427 if(playlist->control_fd >= 0)
428 {
429 char* dir = playlist->filename;
430 char* file = playlist->filename+playlist->dirlen;
431 char c = playlist->filename[playlist->dirlen-1];
432
433 close(playlist->control_fd);
Michael Sevakis16a9f842017-01-17 14:45:07 -0500434 playlist->control_fd = 0;
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000435
William Wilgus2fb6b2b2018-07-25 18:33:10 +0200436 snprintf(temp_file, sizeof(temp_file), "%s%s",
437 playlist->control_filename, file_suffix);
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000438
439 if (rename(playlist->control_filename, temp_file) < 0)
440 return -1;
441
442 temp_fd = open(temp_file, O_RDONLY);
443 if (temp_fd < 0)
444 return -1;
445
446 playlist->control_fd = open(playlist->control_filename,
Thomas Martitz0a1d7c22010-05-06 17:35:13 +0000447 O_CREAT|O_RDWR|O_TRUNC, 0666);
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000448 if (playlist->control_fd < 0)
Thomas Jarosch12ac3812011-08-25 19:39:01 +0000449 {
450 close(temp_fd);
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000451 return -1;
Thomas Jarosch12ac3812011-08-25 19:39:01 +0000452 }
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000453
454 playlist->filename[playlist->dirlen-1] = '\0';
Thomas Jarosch12ac3812011-08-25 19:39:01 +0000455
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000456 /* cannot call update_control() because of mutex */
457 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
458 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
459
460 playlist->filename[playlist->dirlen-1] = c;
461
462 if (result < 0)
463 {
464 close(temp_fd);
465 return result;
466 }
467 }
468
469 playlist->seed = 0;
470 playlist->shuffle_modified = false;
471 playlist->deleted = false;
472 playlist->num_inserted_tracks = 0;
473
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000474 for (i=0; i<playlist->amount; i++)
475 {
476 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
477 {
478 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
479 char inserted_file[MAX_PATH+1];
480
481 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
482 SEEK_SET);
483 read_line(temp_fd, inserted_file, sizeof(inserted_file));
484
485 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
486 queue?'Q':'A', i, playlist->last_insert_pos);
487 if (result > 0)
488 {
489 /* save the position in file where name is written */
490 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
491
492 result = fdprintf(playlist->control_fd, "%s\n",
493 inserted_file);
494
495 playlist->indices[i] =
496 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
497 }
498
499 if (result < 0)
500 break;
501
502 playlist->num_inserted_tracks++;
503 }
504 }
505
506 close(temp_fd);
507 remove(temp_file);
508 fsync(playlist->control_fd);
509
510 if (result < 0)
511 return result;
512
513 return 0;
514}
515
516/*
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000517 * store directory and name of playlist file
518 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000519static void update_playlist_filename(struct playlist_info* playlist,
Jens Arnold8fb33612004-08-18 01:09:31 +0000520 const char *dir, const char *file)
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000521{
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000522 char *sep="";
523 int dirlen = strlen(dir);
Magnus Holmgrena515b102006-11-29 19:22:44 +0000524
Magnus Holmgren6e068252007-11-13 18:49:20 +0000525 playlist->utf8 = is_m3u8(file);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000526
527 /* If the dir does not end in trailing slash, we use a separator.
528 Otherwise we don't. */
529 if('/' != dir[dirlen-1])
530 {
531 sep="/";
532 dirlen++;
533 }
Jens Arnoldac9ad1a2004-09-01 21:34:20 +0000534
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000535 playlist->dirlen = dirlen;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000536
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000537 snprintf(playlist->filename, sizeof(playlist->filename),
Hardeep Sidhu71d22812005-07-01 11:25:16 +0000538 "%s%s%s", dir, sep, file);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000539}
540
541/*
542 * calculate track offsets within a playlist file
543 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000544static int add_indices_to_playlist(struct playlist_info* playlist,
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000545 char* buffer, size_t buflen)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000546{
547 unsigned int nread;
548 unsigned int i = 0;
549 unsigned int count = 0;
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000550 bool store_index;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000551 unsigned char *p;
Hardeep Sidhu6dd728c2006-04-03 14:25:47 +0000552 int result = 0;
Thomas Martitzbebf71a2014-04-08 22:52:37 +0200553 /* get emergency buffer so we don't fail horribly */
554 if (!buflen)
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400555 buffer = alloca((buflen = 64));
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000556
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000557 if(-1 == playlist->fd)
Dominik Riebeling02103a22008-08-02 20:39:03 +0000558 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000559 if(playlist->fd < 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000560 return -1; /* failure */
Dominik Riebelinge2992162008-08-09 11:43:56 +0000561 if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
Dominik Riebeling02103a22008-08-02 20:39:03 +0000562 playlist->utf8 = true; /* Override any earlier indication. */
Dominik Riebelinge2992162008-08-09 11:43:56 +0000563
Nils Wallménius01729e72008-08-15 08:27:39 +0000564 splash(0, ID2P(LANG_WAIT));
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000565
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000566 store_index = true;
567
568 while(1)
569 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000570 nread = read(playlist->fd, buffer, buflen);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000571 /* Terminate on EOF */
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000572 if(nread <= 0)
573 break;
Dominik Riebelinge2992162008-08-09 11:43:56 +0000574
Daniel Stenbergf981ea92005-12-05 22:44:42 +0000575 p = (unsigned char *)buffer;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000576
577 for(count=0; count < nread; count++,p++) {
578
579 /* Are we on a new line? */
580 if((*p == '\n') || (*p == '\r'))
581 {
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000582 store_index = true;
Hardeep Sidhu71d22812005-07-01 11:25:16 +0000583 }
584 else if(store_index)
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000585 {
586 store_index = false;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000587
588 if(*p != '#')
589 {
Hardeep Sidhu6dd728c2006-04-03 14:25:47 +0000590 if ( playlist->amount >= playlist->max_playlist_size ) {
591 display_buffer_full();
592 result = -1;
593 goto exit;
594 }
595
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000596 /* Store a new entry */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000597 playlist->indices[ playlist->amount ] = i+count;
Michael Sevakis16a9f842017-01-17 14:45:07 -0500598 #ifdef HAVE_DIRCACHE
599 copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
600 #endif
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000601 playlist->amount++;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000602 }
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000603 }
604 }
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000605
606 i+= count;
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000607 }
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000608
Hardeep Sidhu6dd728c2006-04-03 14:25:47 +0000609exit:
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000610#ifdef HAVE_DIRCACHE
611 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
612#endif
613
Hardeep Sidhu6dd728c2006-04-03 14:25:47 +0000614 return result;
Björn Stenbergc78e1b02003-01-09 00:55:00 +0000615}
616
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000617/*
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000618 * Utility function to create a new playlist, fill it with the next or
619 * previous directory, shuffle it if needed, and start playback.
620 * If play_last is true and direction zero or negative, start playing
621 * the last file in the directory, otherwise start playing the first.
622 */
623static int create_and_play_dir(int direction, bool play_last)
624{
625 char dir[MAX_PATH + 1];
626 int res;
627 int index = -1;
628
629 if(direction > 0)
630 res = get_next_directory(dir);
631 else
632 res = get_previous_directory(dir);
633
Nick Peskettbe108172012-03-19 09:56:38 +0000634 if (res < 0)
635 /* return the error encountered */
636 return res;
637
638 if (playlist_create(dir, NULL) != -1)
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000639 {
Nick Peskettbe108172012-03-19 09:56:38 +0000640 ft_build_playlist(tree_get_context(), 0);
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000641
Nick Peskettbe108172012-03-19 09:56:38 +0000642 if (global_settings.playlist_shuffle)
643 playlist_shuffle(current_tick, -1);
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000644
Nick Peskettbe108172012-03-19 09:56:38 +0000645 if (play_last && direction <= 0)
646 index = current_playlist.amount - 1;
647 else
648 index = 0;
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000649
Michael Sevakisd5849e02011-04-09 13:07:08 +0000650#if (CONFIG_CODEC == SWCODEC)
Nick Peskettbe108172012-03-19 09:56:38 +0000651 current_playlist.started = true;
Michael Sevakisd5849e02011-04-09 13:07:08 +0000652#else
Michael Sevakis31b71222013-07-14 07:59:39 -0400653 playlist_start(index, 0, 0);
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000654#endif
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000655 }
Nicolas Pennequinca243ce2008-01-09 20:37:36 +0000656
Nick Peskettbe108172012-03-19 09:56:38 +0000657 /* we've overwritten the dircache when getting the next/previous dir,
658 so the tree browser context will need to be reloaded */
659 reload_directory();
660
Magnus Holmgrenccbe2422007-10-25 19:18:20 +0000661 return index;
662}
663
664/*
Jonathan Gordonbe3f29c2006-12-26 13:31:04 +0000665 * Removes all tracks, from the playlist, leaving the presently playing
666 * track queued.
667 */
Bertrik Sikken3e98eb22008-04-20 11:19:50 +0000668int playlist_remove_all_tracks(struct playlist_info *playlist)
Jonathan Gordonbe3f29c2006-12-26 13:31:04 +0000669{
670 int result;
671
672 if (playlist == NULL)
673 playlist = &current_playlist;
674
675 while (playlist->index > 0)
676 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
677 return result;
678
679 while (playlist->amount > 1)
680 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
681 return result;
682
683 if (playlist->amount == 1) {
684 playlist->indices[0] |= PLAYLIST_QUEUED;
685 }
686
687 return 0;
688}
689
690
691/*
Dave Hooper494fd962009-10-28 22:27:38 +0000692 * Add track to playlist at specified position. There are seven special
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000693 * positions that can be specified:
Dave Hooper494fd962009-10-28 22:27:38 +0000694 * PLAYLIST_PREPEND - Add track at beginning of playlist
695 * PLAYLIST_INSERT - Add track after current song. NOTE: If
696 * there are already inserted tracks then track
697 * is added to the end of the insertion list
698 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
699 * matter what other tracks have been inserted
700 * PLAYLIST_INSERT_LAST - Add track to end of playlist
701 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
702 * current playing track and end of playlist
703 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
704 * the playlist.
705 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
706 * and inster this track at the end.
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000707 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000708static int add_track_to_playlist(struct playlist_info* playlist,
Jens Arnold8fb33612004-08-18 01:09:31 +0000709 const char *filename, int position,
710 bool queue, int seek_pos)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000711{
Hardeep Sidhu10ad0a22006-09-18 20:47:27 +0000712 int insert_position, orig_position;
Jean-Philippe Bernardy5203f062005-02-12 12:57:16 +0000713 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000714 int i;
715
Hardeep Sidhu10ad0a22006-09-18 20:47:27 +0000716 insert_position = orig_position = position;
717
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000718 if (playlist->amount >= playlist->max_playlist_size)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000719 {
720 display_buffer_full();
721 return -1;
722 }
723
724 switch (position)
725 {
726 case PLAYLIST_PREPEND:
Hardeep Sidhu685356c2006-06-12 23:06:51 +0000727 position = insert_position = playlist->first_index;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000728 break;
729 case PLAYLIST_INSERT:
730 /* if there are already inserted tracks then add track to end of
731 insertion list else add after current playing track */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000732 if (playlist->last_insert_pos >= 0 &&
733 playlist->last_insert_pos < playlist->amount &&
734 (playlist->indices[playlist->last_insert_pos]&
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000735 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000736 position = insert_position = playlist->last_insert_pos+1;
737 else if (playlist->amount > 0)
738 position = insert_position = playlist->index + 1;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000739 else
740 position = insert_position = 0;
741
Thomas Martitz87d17442009-03-08 17:18:18 +0000742 playlist->last_insert_pos = position;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000743 break;
744 case PLAYLIST_INSERT_FIRST:
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000745 if (playlist->amount > 0)
746 position = insert_position = playlist->index + 1;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000747 else
748 position = insert_position = 0;
749
Thomas Martitz4ae74152009-03-08 19:03:53 +0000750 playlist->last_insert_pos = position;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000751 break;
752 case PLAYLIST_INSERT_LAST:
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000753 if (playlist->first_index > 0)
Hardeep Sidhu685356c2006-06-12 23:06:51 +0000754 position = insert_position = playlist->first_index;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000755 else
Hardeep Sidhu685356c2006-06-12 23:06:51 +0000756 position = insert_position = playlist->amount;
Thomas Martitz4ae74152009-03-08 19:03:53 +0000757
758 playlist->last_insert_pos = position;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000759 break;
Hardeep Sidhu74d082c2005-06-25 04:46:25 +0000760 case PLAYLIST_INSERT_SHUFFLED:
761 {
Hardeep Sidhub79c9a62006-04-19 02:22:23 +0000762 if (playlist->started)
Hardeep Sidhu095ad1a2006-04-18 16:44:51 +0000763 {
764 int offset;
765 int n = playlist->amount -
766 rotate_index(playlist, playlist->index);
767
768 if (n > 0)
769 offset = rand() % n;
770 else
771 offset = 0;
772
773 position = playlist->index + offset + 1;
774 if (position >= playlist->amount)
775 position -= playlist->amount;
776
777 insert_position = position;
778 }
Hardeep Sidhu74d082c2005-06-25 04:46:25 +0000779 else
Hardeep Sidhub79c9a62006-04-19 02:22:23 +0000780 position = insert_position = (rand() % (playlist->amount+1));
Hardeep Sidhu74d082c2005-06-25 04:46:25 +0000781 break;
782 }
Dave Hooper494fd962009-10-28 22:27:38 +0000783 case PLAYLIST_INSERT_LAST_SHUFFLED:
784 {
785 position = insert_position = playlist->last_shuffled_start +
786 rand() % (playlist->amount - playlist->last_shuffled_start + 1);
787 break;
788 }
Jonathan Gordonbe3f29c2006-12-26 13:31:04 +0000789 case PLAYLIST_REPLACE:
Bertrik Sikken3e98eb22008-04-20 11:19:50 +0000790 if (playlist_remove_all_tracks(playlist) < 0)
Jonathan Gordonbe3f29c2006-12-26 13:31:04 +0000791 return -1;
792
Thomas Martitz4ae74152009-03-08 19:03:53 +0000793 playlist->last_insert_pos = position = insert_position = playlist->index + 1;
Jonathan Gordonbe3f29c2006-12-26 13:31:04 +0000794 break;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000795 }
796
797 if (queue)
798 flags |= PLAYLIST_QUEUED;
799
800 /* shift indices so that track can be added */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000801 for (i=playlist->amount; i>insert_position; i--)
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000802 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000803 playlist->indices[i] = playlist->indices[i-1];
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000804#ifdef HAVE_DIRCACHE
Michael Sevakis16a9f842017-01-17 14:45:07 -0500805 if (playlist->dcfrefs)
806 playlist->dcfrefs[i] = playlist->dcfrefs[i-1];
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000807#endif
808 }
809
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000810 /* update stored indices if needed */
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000811
Jonathan Gordond0729ab2010-01-21 07:28:37 +0000812 if (orig_position < 0)
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000813 {
Jonathan Gordond0729ab2010-01-21 07:28:37 +0000814 if (playlist->amount > 0 && insert_position <= playlist->index &&
815 playlist->started)
816 playlist->index++;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000817
Jonathan Gordond0729ab2010-01-21 07:28:37 +0000818 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
819 orig_position != PLAYLIST_PREPEND && playlist->started)
820 playlist->first_index++;
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000821 }
822
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000823 if (insert_position < playlist->last_insert_pos ||
824 (insert_position == playlist->last_insert_pos && position < 0))
825 playlist->last_insert_pos++;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000826
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000827 if (seek_pos < 0 && playlist->control_fd >= 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000828 {
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000829 int result = update_control(playlist,
830 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
831 playlist->last_insert_pos, filename, NULL, &seek_pos);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000832
833 if (result < 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000834 return result;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000835 }
836
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000837 playlist->indices[insert_position] = flags | seek_pos;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000838
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000839#ifdef HAVE_DIRCACHE
Michael Sevakis16a9f842017-01-17 14:45:07 -0500840 copy_filerefs(&playlist->dcfrefs[insert_position], NULL, 1);
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000841#endif
842
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000843 playlist->amount++;
844 playlist->num_inserted_tracks++;
Andree Buschmann4df825b2011-04-07 20:33:00 +0000845
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000846 return insert_position;
847}
848
849/*
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000850 * Callback for playlist_directory_tracksearch to insert track into
851 * playlist.
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000852 */
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000853static int directory_search_callback(char* filename, void* context)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000854{
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000855 struct directory_search_context* c =
856 (struct directory_search_context*) context;
857 int insert_pos;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000858
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000859 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
860 c->queue, -1);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000861
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000862 if (insert_pos < 0)
Björn Stenberg3c10fd52005-02-04 13:20:25 +0000863 return -1;
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000864
865 (c->count)++;
866
867 /* Make sure tracks are inserted in correct order if user requests
868 INSERT_FIRST */
869 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
870 c->position = insert_pos + 1;
871
872 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000873 {
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000874 unsigned char* count_str;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000875
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000876 if (c->queue)
Nils Wallménius5b769362007-08-06 13:08:36 +0000877 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000878 else
Nils Wallménius5b769362007-08-06 13:08:36 +0000879 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000880
Nils Wallménius5b769362007-08-06 13:08:36 +0000881 display_playlist_count(c->count, count_str, false);
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000882
883 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
884 (audio_status() & AUDIO_STATUS_PLAY) &&
885 c->playlist->started)
886 audio_flush_and_reload_tracks();
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000887 }
888
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000889 return 0;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000890}
891
892/*
893 * remove track at specified position
894 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000895static int remove_track_from_playlist(struct playlist_info* playlist,
896 int position, bool write)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000897{
898 int i;
Björn Stenberga108ec22004-01-14 00:13:04 +0000899 bool inserted;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000900
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000901 if (playlist->amount <= 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000902 return -1;
903
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000904 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
Björn Stenberga108ec22004-01-14 00:13:04 +0000905
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000906 /* shift indices now that track has been removed */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000907 for (i=position; i<playlist->amount; i++)
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000908 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000909 playlist->indices[i] = playlist->indices[i+1];
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000910#ifdef HAVE_DIRCACHE
Michael Sevakis16a9f842017-01-17 14:45:07 -0500911 if (playlist->dcfrefs)
912 playlist->dcfrefs[i] = playlist->dcfrefs[i+1];
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000913#endif
914 }
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000915
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000916 playlist->amount--;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000917
Björn Stenberga108ec22004-01-14 00:13:04 +0000918 if (inserted)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000919 playlist->num_inserted_tracks--;
Björn Stenberga108ec22004-01-14 00:13:04 +0000920 else
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000921 playlist->deleted = true;
Björn Stenberga108ec22004-01-14 00:13:04 +0000922
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000923 /* update stored indices if needed */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000924 if (position < playlist->index)
925 playlist->index--;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000926
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000927 if (position < playlist->first_index)
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000928 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000929 playlist->first_index--;
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000930 }
931
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000932 if (position <= playlist->last_insert_pos)
933 playlist->last_insert_pos--;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000934
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000935 if (write && playlist->control_fd >= 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000936 {
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000937 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
938 position, -1, NULL, NULL, NULL);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000939
940 if (result < 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000941 return result;
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +0000942
943 sync_control(playlist, false);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000944 }
Andree Buschmann4df825b2011-04-07 20:33:00 +0000945
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000946 return 0;
947}
948
949/*
950 * randomly rearrange the array of indices for the playlist. If start_current
951 * is true then update the index to the new index of the current playing track
952 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000953static int randomise_playlist(struct playlist_info* playlist,
954 unsigned int seed, bool start_current,
955 bool write)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000956{
957 int count;
958 int candidate;
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000959 unsigned int current = playlist->indices[playlist->index];
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000960
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000961 /* seed 0 is used to identify sorted playlist for resume purposes */
962 if (seed == 0)
963 seed = 1;
964
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000965 /* seed with the given seed */
966 srand(seed);
967
968 /* randomise entire indices list */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000969 for(count = playlist->amount - 1; count >= 0; count--)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000970 {
971 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
Linus Nielsen Feltzing1c9ab1b2004-11-06 23:37:21 +0000972 candidate = rand() % (count + 1);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000973
974 /* now swap the values at the 'count' and 'candidate' positions */
Michael Sevakis16a9f842017-01-17 14:45:07 -0500975 int indextmp = playlist->indices[candidate];
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000976 playlist->indices[candidate] = playlist->indices[count];
Michael Sevakis16a9f842017-01-17 14:45:07 -0500977 playlist->indices[count] = indextmp;
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000978#ifdef HAVE_DIRCACHE
Michael Sevakis16a9f842017-01-17 14:45:07 -0500979 if (playlist->dcfrefs)
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000980 {
Michael Sevakis16a9f842017-01-17 14:45:07 -0500981 struct dircache_fileref dcftmp = playlist->dcfrefs[candidate];
982 playlist->dcfrefs[candidate] = playlist->dcfrefs[count];
983 playlist->dcfrefs[count] = dcftmp;
Miika Pekkarinen735f4532005-11-17 19:31:29 +0000984 }
985#endif
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000986 }
987
988 if (start_current)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000989 find_and_set_playlist_index(playlist, current);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000990
991 /* indices have been moved so last insert position is no longer valid */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000992 playlist->last_insert_pos = -1;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000993
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000994 playlist->seed = seed;
995 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
996 playlist->shuffle_modified = true;
Björn Stenberga108ec22004-01-14 00:13:04 +0000997
Hardeep Sidhu58bafee2003-12-09 08:18:03 +0000998 if (write)
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000999 {
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001000 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
1001 playlist->first_index, NULL, NULL, NULL);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001002 }
Andree Buschmann4df825b2011-04-07 20:33:00 +00001003
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001004 return 0;
1005}
1006
1007/*
1008 * Sort the array of indices for the playlist. If start_current is true then
1009 * set the index to the new index of the current song.
Jonathan Gordona70d6022010-02-05 23:33:31 +00001010 * Also while going to unshuffled mode set the first_index to 0.
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001011 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001012static int sort_playlist(struct playlist_info* playlist, bool start_current,
1013 bool write)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001014{
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001015 unsigned int current = playlist->indices[playlist->index];
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001016
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001017 if (playlist->amount > 0)
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001018 qsort((void*)playlist->indices, playlist->amount,
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001019 sizeof(playlist->indices[0]), compare);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001020
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001021#ifdef HAVE_DIRCACHE
1022 /** We need to re-check the song names from disk because qsort can't
1023 * sort two arrays at once :/
1024 * FIXME: Please implement a better way to do this. */
Michael Sevakis16a9f842017-01-17 14:45:07 -05001025 copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001026 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1027#endif
1028
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001029 if (start_current)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001030 find_and_set_playlist_index(playlist, current);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001031
1032 /* indices have been moved so last insert position is no longer valid */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001033 playlist->last_insert_pos = -1;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001034
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001035 if (!playlist->num_inserted_tracks && !playlist->deleted)
1036 playlist->shuffle_modified = false;
1037 if (write && playlist->control_fd >= 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001038 {
Jonathan Gordona70d6022010-02-05 23:33:31 +00001039 playlist->first_index = 0;
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001040 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1041 playlist->first_index, -1, NULL, NULL, NULL);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001042 }
Andree Buschmann4df825b2011-04-07 20:33:00 +00001043
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001044 return 0;
1045}
1046
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001047/* Calculate how many steps we have to really step when skipping entries
1048 * marked as bad.
1049 */
1050static int calculate_step_count(const struct playlist_info *playlist, int steps)
1051{
1052 int i, count, direction;
1053 int index;
1054 int stepped_count = 0;
1055
1056 if (steps < 0)
Miika Pekkarinencaee4b32005-11-21 13:23:06 +00001057 {
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001058 direction = -1;
Miika Pekkarinencaee4b32005-11-21 13:23:06 +00001059 count = -steps;
1060 }
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001061 else
Miika Pekkarinencaee4b32005-11-21 13:23:06 +00001062 {
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001063 direction = 1;
Miika Pekkarinencaee4b32005-11-21 13:23:06 +00001064 count = steps;
1065 }
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001066
1067 index = playlist->index;
1068 i = 0;
Miika Pekkarinend1704f62005-11-21 11:14:51 +00001069 do {
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001070 /* Boundary check */
1071 if (index < 0)
1072 index += playlist->amount;
1073 if (index >= playlist->amount)
1074 index -= playlist->amount;
1075
1076 /* Check if we found a bad entry. */
1077 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1078 {
1079 steps += direction;
1080 /* Are all entries bad? */
1081 if (stepped_count++ > playlist->amount)
1082 break ;
1083 }
1084 else
1085 i++;
Miika Pekkarinend1704f62005-11-21 11:14:51 +00001086
1087 index += direction;
Miika Pekkarinen4394cc72005-11-21 11:31:15 +00001088 } while (i <= count);
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001089
1090 return steps;
1091}
1092
Boris Gjenerobda8a962011-12-15 20:58:14 +00001093#if CONFIG_CODEC == SWCODEC
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001094/* Marks the index of the track to be skipped that is "steps" away from
1095 * current playing track.
1096 */
1097void playlist_skip_entry(struct playlist_info *playlist, int steps)
1098{
1099 int index;
1100
1101 if (playlist == NULL)
1102 playlist = &current_playlist;
1103
Hardeep Sidhued41ba02006-05-05 08:32:07 +00001104 /* need to account for already skipped tracks */
1105 steps = calculate_step_count(playlist, steps);
1106
1107 index = playlist->index + steps;
1108 if (index < 0)
1109 index += playlist->amount;
1110 else if (index >= playlist->amount)
1111 index -= playlist->amount;
1112
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001113 playlist->indices[index] |= PLAYLIST_SKIPPED;
1114}
Boris Gjenerobda8a962011-12-15 20:58:14 +00001115#endif /* CONFIG_CODEC == SWCODEC */
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001116
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001117/*
1118 * returns the index of the track that is "steps" away from current playing
1119 * track.
1120 */
Hardeep Sidhu965c86a2005-06-25 06:28:55 +00001121static int get_next_index(const struct playlist_info* playlist, int steps,
Hardeep Sidhu74d082c2005-06-25 04:46:25 +00001122 int repeat_mode)
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001123{
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001124 int current_index = playlist->index;
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001125 int next_index = -1;
1126
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001127 if (playlist->amount <= 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001128 return -1;
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001129
Hardeep Sidhu74d082c2005-06-25 04:46:25 +00001130 if (repeat_mode == -1)
1131 repeat_mode = global_settings.repeat_mode;
1132
Jonathan Gordon594099f2006-12-27 01:51:33 +00001133 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
Hardeep Sidhu74d082c2005-06-25 04:46:25 +00001134 repeat_mode = REPEAT_ALL;
1135
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001136 steps = calculate_step_count(playlist, steps);
Hardeep Sidhu74d082c2005-06-25 04:46:25 +00001137 switch (repeat_mode)
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001138 {
Hardeep Sidhu74d082c2005-06-25 04:46:25 +00001139 case REPEAT_SHUFFLE:
1140 /* Treat repeat shuffle just like repeat off. At end of playlist,
1141 play will be resumed in playlist_next() */
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001142 case REPEAT_OFF:
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001143 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001144 current_index = rotate_index(playlist, current_index);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001145 next_index = current_index+steps;
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001146 if ((next_index < 0) || (next_index >= playlist->amount))
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001147 next_index = -1;
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001148 else
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001149 next_index = (next_index+playlist->first_index) %
1150 playlist->amount;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001151
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001152 break;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001153 }
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001154
1155 case REPEAT_ONE:
Daniel Stenberg0c021de2007-02-17 23:07:39 +00001156#ifdef AB_REPEAT_ENABLE
Linus Nielsen Feltzing0ad617c2005-08-21 23:01:12 +00001157 case REPEAT_AB:
1158#endif
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001159 next_index = current_index;
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001160 break;
1161
1162 case REPEAT_ALL:
1163 default:
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001164 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001165 next_index = (current_index+steps) % playlist->amount;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001166 while (next_index < 0)
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001167 next_index += playlist->amount;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001168
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001169 if (steps >= playlist->amount)
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001170 {
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001171 int i, index;
1172
1173 index = next_index;
1174 next_index = -1;
1175
1176 /* second time around so skip the queued files */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001177 for (i=0; i<playlist->amount; i++)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001178 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001179 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1180 index = (index+1) % playlist->amount;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001181 else
1182 {
1183 next_index = index;
1184 break;
1185 }
1186 }
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001187 }
1188 break;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001189 }
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001190 }
1191
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001192 /* No luck if the whole playlist was bad. */
1193 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1194 return -1;
1195
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001196 return next_index;
1197}
1198
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001199/*
1200 * Search for the seek track and set appropriate indices. Used after shuffle
1201 * to make sure the current index is still pointing to correct track.
1202 */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001203static void find_and_set_playlist_index(struct playlist_info* playlist,
1204 unsigned int seek)
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001205{
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001206 int i;
1207
1208 /* Set the index to the current song */
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001209 for (i=0; i<playlist->amount; i++)
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001210 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001211 if (playlist->indices[i] == seek)
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001212 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001213 playlist->index = playlist->first_index = i;
1214
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001215 break;
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001216 }
1217 }
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001218}
1219
1220/*
1221 * used to sort track indices. Sort order is as follows:
1222 * 1. Prepended tracks (in prepend order)
1223 * 2. Playlist/directory tracks (in playlist order)
1224 * 3. Inserted/Appended tracks (in insert order)
1225 */
1226static int compare(const void* p1, const void* p2)
1227{
Jean-Philippe Bernardy5203f062005-02-12 12:57:16 +00001228 unsigned long* e1 = (unsigned long*) p1;
1229 unsigned long* e2 = (unsigned long*) p2;
1230 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1231 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001232
1233 if (flags1 == flags2)
1234 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1235 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1236 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1237 return -1;
1238 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1239 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1240 return 1;
1241 else if (flags1 && flags2)
1242 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
Björn Stenbergc78e1b02003-01-09 00:55:00 +00001243 else
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001244 return *e1 - *e2;
1245}
1246
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001247#ifdef HAVE_DIRCACHE
1248/**
1249 * Thread to update filename pointers to dircache on background
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001250 * without affecting playlist load up performance. This thread also flushes
1251 * any pending control commands when the disk spins up.
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001252 */
Thomas Martitz470989b2014-03-14 23:15:16 +01001253static void playlist_flush_callback(void)
Jonathan Gordon327f8452008-04-30 09:23:12 +00001254{
1255 struct playlist_info *playlist;
1256 playlist = &current_playlist;
1257 if (playlist->control_fd >= 0)
1258 {
1259 if (playlist->num_cached > 0)
1260 {
Michael Sevakisb15aa472011-02-14 11:27:45 +00001261 mutex_lock(playlist->control_mutex);
Jonathan Gordon327f8452008-04-30 09:23:12 +00001262 flush_cached_control(playlist);
Michael Sevakisb15aa472011-02-14 11:27:45 +00001263 mutex_unlock(playlist->control_mutex);
Jonathan Gordon327f8452008-04-30 09:23:12 +00001264 }
1265 sync_control(playlist, true);
1266 }
Jonathan Gordon327f8452008-04-30 09:23:12 +00001267}
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001268
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001269static void playlist_thread(void)
1270{
Michael Sevakisa9b2fb52007-10-16 01:25:17 +00001271 struct queue_event ev;
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001272 bool dirty_pointers = false;
1273 static char tmp[MAX_PATH+1];
1274
1275 struct playlist_info *playlist;
1276 int index;
1277 int seek;
1278 bool control_file;
1279
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001280 int sleep_time = 5;
1281
Frank Gevaerts46573012008-10-07 19:37:33 +00001282#ifdef HAVE_DISK_STORAGE
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001283 if (global_settings.disk_spindown > 1 &&
1284 global_settings.disk_spindown <= 5)
1285 sleep_time = global_settings.disk_spindown - 1;
Nils Wallménius0bfa3e72007-08-01 08:50:44 +00001286#endif
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001287
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001288 while (1)
1289 {
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001290 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001291
1292 switch (ev.id)
1293 {
1294 case PLAYLIST_LOAD_POINTERS:
1295 dirty_pointers = true;
1296 break ;
1297
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001298 /* Start the background scanning after either the disk spindown
1299 timeout or 5s, whichever is less */
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001300 case SYS_TIMEOUT:
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001301 {
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001302 playlist = &current_playlist;
Jonathan Gordon327f8452008-04-30 09:23:12 +00001303 if (playlist->control_fd >= 0)
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001304 {
1305 if (playlist->num_cached > 0)
Frank Gevaerts2f8a0082008-11-01 16:14:28 +00001306 register_storage_idle_func(playlist_flush_callback);
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001307 }
1308
Michael Sevakis16a9f842017-01-17 14:45:07 -05001309 if (!playlist->dcfrefs || playlist->amount <= 0)
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001310 break ;
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001311
1312 /* Check if previously loaded pointers are intact. */
Michael Sevakis16a9f842017-01-17 14:45:07 -05001313 if (!dirty_pointers)
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001314 break ;
1315
Michael Sevakis16a9f842017-01-17 14:45:07 -05001316 struct dircache_info info;
1317 dircache_get_info(&info);
1318
1319 if (info.status != DIRCACHE_READY)
1320 break ;
1321
1322 trigger_cpu_boost();
1323
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001324 for (index = 0; index < playlist->amount
1325 && queue_empty(&playlist_queue); index++)
1326 {
Michael Sevakis16a9f842017-01-17 14:45:07 -05001327 /* Process only pointers that are superficially stale. */
Michael Sevakis7373cf52017-01-18 04:39:35 -05001328 if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0)
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001329 continue ;
1330
1331 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1332 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1333
1334 /* Load the filename from playlist file. */
1335 if (get_filename(playlist, index, seek, control_file, tmp,
1336 sizeof(tmp)) < 0)
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001337 {
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001338 break ;
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001339 }
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001340
Michael Sevakis16a9f842017-01-17 14:45:07 -05001341 /* Obtain the dircache file entry cookie. */
1342 dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF,
1343 &playlist->dcfrefs[index], tmp);
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001344
1345 /* And be on background so user doesn't notice any delays. */
1346 yield();
1347 }
Michael Sevakis16a9f842017-01-17 14:45:07 -05001348
1349 cancel_cpu_boost();
1350
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001351 if (index == playlist->amount)
1352 dirty_pointers = false;
1353
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001354 break ;
Miika Pekkarinen2bc133d2011-06-21 17:42:31 +00001355 }
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001356
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001357 case SYS_USB_CONNECTED:
1358 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1359 usb_wait_for_disconnect(&playlist_queue);
1360 break ;
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001361 }
1362 }
1363}
1364#endif
1365
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001366/*
1367 * gets pathname for track at seek index
1368 */
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001369static int get_filename(struct playlist_info* playlist, int index, int seek,
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001370 bool control_file, char *buf, int buf_length)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001371{
1372 int fd;
1373 int max = -1;
1374 char tmp_buf[MAX_PATH+1];
1375 char dir_buf[MAX_PATH+1];
Magnus Holmgren6e068252007-11-13 18:49:20 +00001376 bool utf8 = playlist->utf8;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001377
1378 if (buf_length > MAX_PATH+1)
1379 buf_length = MAX_PATH+1;
1380
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001381#ifdef HAVE_DIRCACHE
Michael Sevakis16a9f842017-01-17 14:45:07 -05001382 if (playlist->dcfrefs)
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001383 {
Michael Sevakis16a9f842017-01-17 14:45:07 -05001384 max = dircache_get_fileref_path(&playlist->dcfrefs[index],
1385 tmp_buf, sizeof(tmp_buf));
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001386 }
Michael Sevakis16a9f842017-01-17 14:45:07 -05001387#endif /* HAVE_DIRCACHE */
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001388
1389 if (playlist->in_ram && !control_file && max < 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001390 {
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001391 max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001392 }
Miika Pekkarinen735f4532005-11-17 19:31:29 +00001393 else if (max < 0)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001394 {
Michael Sevakisb15aa472011-02-14 11:27:45 +00001395 mutex_lock(playlist->control_mutex);
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +00001396
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001397 if (control_file)
Magnus Holmgren6e068252007-11-13 18:49:20 +00001398 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001399 fd = playlist->control_fd;
Magnus Holmgren6e068252007-11-13 18:49:20 +00001400 utf8 = true;
1401 }
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001402 else
1403 {
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001404 if(-1 == playlist->fd)
1405 playlist->fd = open(playlist->filename, O_RDONLY);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001406
Hardeep Sidhu107ebc52004-01-26 17:05:21 +00001407 fd = playlist->fd;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001408 }
1409
1410 if(-1 != fd)
1411 {
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001412
Björn Stenberg30d8f612005-01-20 16:24:26 +00001413 if (lseek(fd, seek, SEEK_SET) != seek)
1414 max = -1;
1415 else
Magnus Holmgrena515b102006-11-29 19:22:44 +00001416 {
1417 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1418
Michael Sevakis16a9f842017-01-17 14:45:07 -05001419 if (max > 0)
Magnus Holmgrena515b102006-11-29 19:22:44 +00001420 {
Michael Sevakis16a9f842017-01-17 14:45:07 -05001421 /* playlist file may end without a new line - terminate buffer */
1422 tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
1423
Magnus Holmgren6e068252007-11-13 18:49:20 +00001424 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1425 * be as large as tmp_buf.
Magnus Holmgrena515b102006-11-29 19:22:44 +00001426 */
Michael Sevakis16a9f842017-01-17 14:45:07 -05001427 if (!utf8)
1428 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
Magnus Holmgrena515b102006-11-29 19:22:44 +00001429 }
1430 }
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001431 }
1432
Michael Sevakisb15aa472011-02-14 11:27:45 +00001433 mutex_unlock(playlist->control_mutex);
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +00001434
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001435 if (max < 0)
1436 {
William Wilgus929ea732018-12-26 12:39:53 -06001437 if (usb_detect() == USB_INSERTED)
1438 ; /* ignore error on usb plug */
1439 else if (control_file)
Nils Wallménius01729e72008-08-15 08:27:39 +00001440 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001441 else
Nils Wallménius01729e72008-08-15 08:27:39 +00001442 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001443
1444 return max;
1445 }
1446 }
1447
Nils Wallménius3d4701a2009-07-14 13:57:45 +00001448 strlcpy(dir_buf, playlist->filename, playlist->dirlen);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001449
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001450 return format_track_path(buf, tmp_buf, buf_length, dir_buf);
Michael Sevakis16a9f842017-01-17 14:45:07 -05001451
1452 (void)index;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001453}
1454
Anton Oleynikovd102e1f2005-11-02 22:32:04 +00001455static int get_next_directory(char *dir){
Nick Peskettbe108172012-03-19 09:56:38 +00001456 return get_next_dir(dir, true);
Anton Oleynikovd102e1f2005-11-02 22:32:04 +00001457}
1458
1459static int get_previous_directory(char *dir){
Nick Peskettbe108172012-03-19 09:56:38 +00001460 return get_next_dir(dir, false);
Anton Oleynikovd102e1f2005-11-02 22:32:04 +00001461}
1462
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001463/*
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001464 * search through all the directories (starting with the current) to find
1465 * one that has tracks to play
1466 */
Nick Peskettbe108172012-03-19 09:56:38 +00001467static int get_next_dir(char *dir, bool is_forward)
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001468{
1469 struct playlist_info* playlist = &current_playlist;
1470 int result = -1;
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001471 char *start_dir = NULL;
1472 bool exit = false;
1473 struct tree_context* tc = tree_get_context();
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001474 int saved_dirfilter = *(tc->dirfilter);
Nick Peskettbe108172012-03-19 09:56:38 +00001475 unsigned int base_len;
1476
1477 if (global_settings.constrain_next_folder)
1478 {
1479 /* constrain results to directories below user's start directory */
1480 strcpy(dir, global_settings.start_directory);
1481 base_len = strlen(dir);
1482
1483 /* strip any trailing slash from base directory */
1484 if (base_len > 0 && dir[base_len - 1] == '/')
1485 {
1486 base_len--;
1487 dir[base_len] = '\0';
1488 }
1489 }
1490 else
1491 {
1492 /* start from root directory */
1493 dir[0] = '\0';
1494 base_len = 0;
1495 }
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001496
1497 /* process random folder advance */
Jonathan Gordon27ad51f2006-10-09 10:54:17 +00001498 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1499 {
Thomas Martitz2c241602010-12-06 22:26:31 +00001500 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
Jonathan Gordon27ad51f2006-10-09 10:54:17 +00001501 if (fd >= 0)
1502 {
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001503 int folder_count = 0;
Jonathan Gordon27ad51f2006-10-09 10:54:17 +00001504 read(fd,&folder_count,sizeof(int));
Jonathan Gordon8f3175b2008-06-29 02:19:53 +00001505 if (folder_count)
Nick Peskettbe108172012-03-19 09:56:38 +00001506 {
1507 char buffer[MAX_PATH];
1508 /* give up looking for a directory after we've had four
1509 times as many tries as there are directories. */
1510 unsigned long allowed_tries = folder_count * 4;
1511 int i;
1512 srand(current_tick);
1513 *(tc->dirfilter) = SHOW_MUSIC;
1514 tc->sort_dir = global_settings.sort_dir;
1515 while (!exit && allowed_tries--)
1516 {
1517 i = rand() % folder_count;
1518 lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
1519 read(fd, buffer, MAX_PATH);
1520 /* is the current dir within our base dir and has music? */
1521 if ((base_len == 0 || !strncmp(buffer, dir, base_len))
1522 && check_subdir_for_music(buffer, "", false) == 0)
1523 exit = true;
1524 }
1525 close(fd);
1526 *(tc->dirfilter) = saved_dirfilter;
1527 tc->sort_dir = global_settings.sort_dir;
1528 reload_directory();
1529 if (exit)
1530 {
1531 strcpy(dir,buffer);
1532 return 0;
1533 }
1534 }
1535 else
1536 close(fd);
Jonathan Gordon27ad51f2006-10-09 10:54:17 +00001537 }
1538 }
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001539
Nick Peskettbe108172012-03-19 09:56:38 +00001540 /* if the current file is within our base dir, use its dir instead */
1541 if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
Nils Wallménius3d4701a2009-07-14 13:57:45 +00001542 strlcpy(dir, playlist->filename, playlist->dirlen);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001543
1544 /* use the tree browser dircache to load files */
Hardeep Sidhud0de1aa2006-04-18 16:24:12 +00001545 *(tc->dirfilter) = SHOW_ALL;
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001546
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001547 /* set up sorting/direction */
1548 tc->sort_dir = global_settings.sort_dir;
1549 if (!is_forward)
1550 {
Jens Arnolda0c91ae2008-10-08 17:23:59 +00001551 static const char sortpairs[] =
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001552 {
Jens Arnolda0c91ae2008-10-08 17:23:59 +00001553 [SORT_ALPHA] = SORT_ALPHA_REVERSED,
1554 [SORT_DATE] = SORT_DATE_REVERSED,
1555 [SORT_TYPE] = SORT_TYPE_REVERSED,
1556 [SORT_ALPHA_REVERSED] = SORT_ALPHA,
1557 [SORT_DATE_REVERSED] = SORT_DATE,
1558 [SORT_TYPE_REVERSED] = SORT_TYPE,
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001559 };
Jens Arnolda0c91ae2008-10-08 17:23:59 +00001560
1561 if ((unsigned)tc->sort_dir < sizeof(sortpairs))
1562 tc->sort_dir = sortpairs[tc->sort_dir];
Anton Oleynikovd102e1f2005-11-02 22:32:04 +00001563 }
1564
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001565 while (!exit)
1566 {
1567 struct entry *files;
1568 int num_files = 0;
1569 int i;
1570
1571 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1572 {
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001573 exit = true;
1574 result = -1;
1575 break;
1576 }
1577
William Wilgus3f110da2018-12-13 10:39:49 -06001578 tree_lock_cache(tc);
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001579 files = tree_get_entries(tc);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001580 num_files = tc->filesindir;
1581
1582 for (i=0; i<num_files; i++)
1583 {
1584 /* user abort */
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +00001585 if (action_userabort(TIMEOUT_NOBLOCK))
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001586 {
1587 result = -1;
1588 exit = true;
1589 break;
1590 }
Nick Peskettbe108172012-03-19 09:56:38 +00001591
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001592 if (files[i].attr & ATTR_DIRECTORY)
1593 {
1594 if (!start_dir)
1595 {
Jonathan Gordonf48cf622008-07-02 09:16:26 +00001596 result = check_subdir_for_music(dir, files[i].name, true);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001597 if (result != -1)
1598 {
1599 exit = true;
1600 break;
1601 }
1602 }
1603 else if (!strcmp(start_dir, files[i].name))
1604 start_dir = NULL;
1605 }
1606 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001607 tree_unlock_cache(tc);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001608
1609 if (!exit)
1610 {
Nick Peskettbe108172012-03-19 09:56:38 +00001611 /* we've already descended to the base dir with nothing found,
1612 check whether that contains music */
1613 if (strlen(dir) <= base_len)
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001614 {
Nick Peskettbe108172012-03-19 09:56:38 +00001615 result = check_subdir_for_music(dir, "", true);
1616 if (result == -1)
1617 /* there's no music files in the base directory,
1618 treat as a fatal error */
1619 result = -2;
1620 break;
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001621 }
1622 else
Nick Peskettbe108172012-03-19 09:56:38 +00001623 {
1624 /* move down to parent directory. current directory name is
1625 stored as the starting point for the search in parent */
1626 start_dir = strrchr(dir, '/');
1627 if (start_dir)
1628 {
1629 *start_dir = '\0';
1630 start_dir++;
1631 }
1632 else
1633 break;
1634 }
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001635 }
1636 }
1637
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001638 /* restore dirfilter */
1639 *(tc->dirfilter) = saved_dirfilter;
1640 tc->sort_dir = global_settings.sort_dir;
Anton Oleynikovd102e1f2005-11-02 22:32:04 +00001641
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001642 return result;
1643}
1644
1645/*
1646 * Checks if there are any music files in the dir or any of its
1647 * subdirectories. May be called recursively.
1648 */
Nils Wallménius48b52ae2008-10-08 16:32:01 +00001649static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001650{
1651 int result = -1;
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001652 size_t dirlen = strlen(dir);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001653 int num_files = 0;
1654 int i;
1655 struct entry *files;
1656 bool has_music = false;
1657 bool has_subdir = false;
1658 struct tree_context* tc = tree_get_context();
1659
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001660 if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
1661 MAX_PATH - dirlen)
1662 {
1663 return 0;
1664 }
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001665
1666 if (ft_load(tc, dir) < 0)
1667 {
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001668 return -2;
1669 }
1670
William Wilgus3f110da2018-12-13 10:39:49 -06001671 tree_lock_cache(tc);
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001672 files = tree_get_entries(tc);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001673 num_files = tc->filesindir;
William Wilgus3f110da2018-12-13 10:39:49 -06001674
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001675 for (i=0; i<num_files; i++)
1676 {
1677 if (files[i].attr & ATTR_DIRECTORY)
1678 has_subdir = true;
Jonathan Gordon36a2e302007-04-18 13:03:01 +00001679 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001680 {
1681 has_music = true;
1682 break;
1683 }
1684 }
1685
1686 if (has_music)
William Wilgus3f110da2018-12-13 10:39:49 -06001687 {
1688 tree_unlock_cache(tc);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001689 return 0;
William Wilgus3f110da2018-12-13 10:39:49 -06001690 }
1691
Jonathan Gordonf48cf622008-07-02 09:16:26 +00001692 if (has_subdir && recurse)
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001693 {
1694 for (i=0; i<num_files; i++)
1695 {
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +00001696 if (action_userabort(TIMEOUT_NOBLOCK))
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001697 {
1698 result = -2;
1699 break;
1700 }
1701
1702 if (files[i].attr & ATTR_DIRECTORY)
1703 {
Jonathan Gordonf48cf622008-07-02 09:16:26 +00001704 result = check_subdir_for_music(dir, files[i].name, true);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001705 if (!result)
1706 break;
1707 }
1708 }
1709 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001710 tree_unlock_cache(tc);
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001711
1712 if (result < 0)
1713 {
Magnus Holmgren97c6fd02005-07-30 18:12:25 +00001714 if (dirlen)
1715 {
1716 dir[dirlen] = '\0';
1717 }
1718 else
1719 {
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001720 strcpy(dir, PATH_ROOTSTR);
Magnus Holmgren97c6fd02005-07-30 18:12:25 +00001721 }
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001722
1723 /* we now need to reload our current directory */
1724 if(ft_load(tc, dir) < 0)
Nils Wallménius01729e72008-08-15 08:27:39 +00001725 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001726 }
Hardeep Sidhu71d22812005-07-01 11:25:16 +00001727 return result;
1728}
1729
1730/*
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001731 * Returns absolute path of track
1732 */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001733static ssize_t format_track_path(char *dest, char *src, int buf_length,
1734 const char *dir)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001735{
Michael Sevakis5c6ccb42017-01-07 07:12:10 -05001736 size_t len = 0;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001737
Michael Sevakis5c6ccb42017-01-07 07:12:10 -05001738 /* Look for the end of the string */
1739 while (1)
1740 {
1741 int c = src[len];
1742 if (c == '\n' || c == '\r' || c == '\0')
1743 break;
1744 len++;
1745 }
1746
1747 /* Now work back killing white space */
1748 while (len > 0)
1749 {
1750 int c = src[len - 1];
1751 if (c != '\t' && c != ' ')
1752 break;
1753 len--;
1754 }
1755
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001756 src[len] = '\0';
Thomas Jarosch15a5f9c2011-02-18 21:39:59 +00001757
Michael Sevakis5c6ccb42017-01-07 07:12:10 -05001758 /* Replace backslashes with forward slashes */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001759 path_correct_separators(src, src);
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001760
Michael Sevakis5c6ccb42017-01-07 07:12:10 -05001761 /* Drive letters have no meaning here; handle DOS style drive letter
1762 * and parse greedily so that:
1763 *
1764 * 1) "c:/foo" is fully qualified, use directory volume only
1765 * 2) "c:foo" is relative to current directory on C, use directory path
1766 *
1767 * Assume any volume on the beginning of the directory path is actually
1768 * the volume on which it resides. This may not be the case if the dir
1769 * param contains a path such as "/<1>/foo/../../<0>/bar", which refers
1770 * to "/<0>/bar" (aka "/bar" at this time). *fingers crossed*
1771 *
1772 * If any stripped drive spec was absolute, prepend the playlist
1773 * directory's volume spec, or root if none. Relative paths remain
1774 * relative and the playlist's directory fully qualifies them. Absolute
1775 * UNIX-style paths remain unaltered.
1776 */
1777 if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
1778 src[-1] == PATH_SEPCH)
1779 {
1780 #ifdef HAVE_MULTIVOLUME
1781 const char *p;
1782 path_strip_volume(dir, &p, false);
1783 dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
1784 #else
1785 dir = ""; /* only volume is root */
1786 #endif
1787 }
Michael Giacomellib30edcd2012-06-02 00:12:54 -04001788
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001789 len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
1790 if (len >= (size_t)buf_length)
1791 return -1; /* buffer too small */
Michael Giacomellib30edcd2012-06-02 00:12:54 -04001792
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001793 return len;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001794}
1795
1796/*
1797 * Display splash message showing progress of playlist/directory insertion or
1798 * save.
1799 */
Nils Wallménius5b769362007-08-06 13:08:36 +00001800static void display_playlist_count(int count, const unsigned char *fmt,
1801 bool final)
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001802{
Nils Wallménius5b769362007-08-06 13:08:36 +00001803 static long talked_tick = 0;
1804 long id = P2ID(fmt);
Steve Bavin32a95752007-10-19 15:31:42 +00001805 if(global_settings.talk_menu && id>=0)
Nils Wallménius5b769362007-08-06 13:08:36 +00001806 {
1807 if(final || (count && (talked_tick == 0
1808 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1809 {
1810 talked_tick = current_tick;
1811 talk_number(count, false);
1812 talk_id(id, true);
1813 }
1814 }
1815 fmt = P2STR(fmt);
1816
Nils Wallménius01729e72008-08-15 08:27:39 +00001817 splashf(0, fmt, count, str(LANG_OFF_ABORT));
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001818}
1819
1820/*
1821 * Display buffer full message
1822 */
1823static void display_buffer_full(void)
1824{
Nils Wallménius01729e72008-08-15 08:27:39 +00001825 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001826}
1827
1828/*
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001829 * Flush any cached control commands to disk. Called when playlist is being
Hardeep Sidhu58bafee2003-12-09 08:18:03 +00001830 * modified. Returns 0 on success and -1 on failure.
1831 */
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001832static int flush_cached_control(struct playlist_info* playlist)
Hardeep Sidhu58bafee2003-12-09 08:18:03 +00001833{
1834 int result = 0;
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001835 int i;
1836
Hardeep Sidhucab3a162006-05-11 19:14:18 +00001837 if (!playlist->num_cached)
1838 return 0;
1839
Hardeep Sidhu75a60fb2006-02-05 18:17:41 +00001840