forked from len0rd/rockbox
Added dynamic playlists. ON+PLAY->Playlist on a track, directory, or playlist from file browser to see available options.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3796 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
928a09e3f4
commit
9e4262081b
24 changed files with 2310 additions and 1005 deletions
477
apps/tree.c
477
apps/tree.c
|
|
@ -66,10 +66,6 @@ static int max_files_in_dir;
|
|||
static char *name_buffer;
|
||||
static int name_buffer_size; /* Size of allocated buffer */
|
||||
static int name_buffer_length; /* Currently used amount */
|
||||
struct entry {
|
||||
short attr; /* FAT attributes + file type flags */
|
||||
char *name;
|
||||
};
|
||||
|
||||
static struct entry *dircache;
|
||||
|
||||
|
|
@ -87,6 +83,8 @@ static int boot_size = 0;
|
|||
static int boot_cluster;
|
||||
static bool boot_changed = false;
|
||||
|
||||
static bool dirbrowse(char *root);
|
||||
|
||||
void browse_root(void)
|
||||
{
|
||||
#ifndef SIMULATOR
|
||||
|
|
@ -158,14 +156,13 @@ static int build_playlist(int start_index)
|
|||
int i;
|
||||
int start=start_index;
|
||||
|
||||
playlist_clear();
|
||||
|
||||
for(i = 0;i < filesindir;i++)
|
||||
{
|
||||
if(dircache[i].attr & TREE_ATTR_MPA)
|
||||
{
|
||||
DEBUGF("Adding %s\n", dircache[i].name);
|
||||
playlist_add(dircache[i].name);
|
||||
if (playlist_add(dircache[i].name) < 0)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -237,6 +234,133 @@ static void showfileline(int line, int direntry, bool scroll)
|
|||
}
|
||||
}
|
||||
|
||||
/* load sorted directory into dircache. returns NULL on failure. */
|
||||
struct entry* load_and_sort_directory(char *dirname, int dirfilter,
|
||||
int *num_files, bool *buffer_full)
|
||||
{
|
||||
int i;
|
||||
|
||||
DIR *dir = opendir(dirname);
|
||||
if(!dir)
|
||||
return NULL; /* not a directory */
|
||||
|
||||
name_buffer_length = 0;
|
||||
*buffer_full = false;
|
||||
|
||||
for ( i=0; i < max_files_in_dir; i++ ) {
|
||||
int len;
|
||||
struct dirent *entry = readdir(dir);
|
||||
struct entry* dptr = &dircache[i];
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
len = strlen(entry->d_name);
|
||||
|
||||
/* skip directories . and .. */
|
||||
if ((entry->attribute & ATTR_DIRECTORY) &&
|
||||
(((len == 1) &&
|
||||
(!strncmp(entry->d_name, ".", 1))) ||
|
||||
((len == 2) &&
|
||||
(!strncmp(entry->d_name, "..", 2))))) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip FAT volume ID */
|
||||
if (entry->attribute & ATTR_VOLUME_ID) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* filter out dotfiles and hidden files */
|
||||
if (dirfilter != SHOW_ALL &&
|
||||
((entry->d_name[0]=='.') ||
|
||||
(entry->attribute & ATTR_HIDDEN))) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
dptr->attr = entry->attribute;
|
||||
|
||||
/* mark mp? and m3u files as such */
|
||||
if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) {
|
||||
if (!strcasecmp(&entry->d_name[len-4], ".mp3") ||
|
||||
(!strcasecmp(&entry->d_name[len-4], ".mp2")) ||
|
||||
(!strcasecmp(&entry->d_name[len-4], ".mpa")))
|
||||
dptr->attr |= TREE_ATTR_MPA;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".m3u"))
|
||||
dptr->attr |= TREE_ATTR_M3U;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".cfg"))
|
||||
dptr->attr |= TREE_ATTR_CFG;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".wps"))
|
||||
dptr->attr |= TREE_ATTR_WPS;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".txt"))
|
||||
dptr->attr |= TREE_ATTR_TXT;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".lng"))
|
||||
dptr->attr |= TREE_ATTR_LNG;
|
||||
#ifdef HAVE_RECORDER_KEYPAD
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".fnt"))
|
||||
dptr->attr |= TREE_ATTR_FONT;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".ajz"))
|
||||
#else
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".mod"))
|
||||
#endif
|
||||
dptr->attr |= TREE_ATTR_MOD;
|
||||
else if (!strcasecmp(&entry->d_name[len-5], ".rock"))
|
||||
dptr->attr |= TREE_ATTR_ROCK;
|
||||
}
|
||||
|
||||
/* memorize/compare details about the boot file */
|
||||
if ((currdir[1] == 0) && !strcmp(entry->d_name, BOOTFILE)) {
|
||||
if (boot_size) {
|
||||
if ((entry->size != boot_size) ||
|
||||
(entry->startcluster != boot_cluster))
|
||||
boot_changed = true;
|
||||
}
|
||||
boot_size = entry->size;
|
||||
boot_cluster = entry->startcluster;
|
||||
}
|
||||
|
||||
/* filter out all non-playlist files */
|
||||
if ( dirfilter == SHOW_PLAYLIST &&
|
||||
(!(dptr->attr &
|
||||
(ATTR_DIRECTORY|TREE_ATTR_M3U))) ) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* filter out non-music files */
|
||||
if ( dirfilter == SHOW_MUSIC &&
|
||||
(!(dptr->attr &
|
||||
(ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* filter out non-supported files */
|
||||
if ( dirfilter == SHOW_SUPPORTED &&
|
||||
(!(dptr->attr & TREE_ATTR_MASK)) ) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len > name_buffer_size - name_buffer_length - 1) {
|
||||
/* Tell the world that we ran out of buffer space */
|
||||
*buffer_full = true;
|
||||
break;
|
||||
}
|
||||
dptr->name = &name_buffer[name_buffer_length];
|
||||
strcpy(dptr->name,entry->d_name);
|
||||
name_buffer_length += len + 1;
|
||||
}
|
||||
*num_files = i;
|
||||
closedir(dir);
|
||||
strncpy(lastdir,dirname,sizeof(lastdir));
|
||||
lastdir[sizeof(lastdir)-1] = 0;
|
||||
qsort(dircache,i,sizeof(struct entry),compare);
|
||||
|
||||
return dircache;
|
||||
}
|
||||
|
||||
static int showdir(char *path, int start)
|
||||
{
|
||||
|
|
@ -258,124 +382,9 @@ static int showdir(char *path, int start)
|
|||
|
||||
/* new dir? cache it */
|
||||
if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) {
|
||||
DIR *dir = opendir(path);
|
||||
if(!dir)
|
||||
return -1; /* not a directory */
|
||||
|
||||
name_buffer_length = 0;
|
||||
dir_buffer_full = false;
|
||||
|
||||
for ( i=0; i < max_files_in_dir; i++ ) {
|
||||
int len;
|
||||
struct dirent *entry = readdir(dir);
|
||||
struct entry* dptr = &dircache[i];
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
len = strlen(entry->d_name);
|
||||
|
||||
/* skip directories . and .. */
|
||||
if ((entry->attribute & ATTR_DIRECTORY) &&
|
||||
(((len == 1) &&
|
||||
(!strncmp(entry->d_name, ".", 1))) ||
|
||||
((len == 2) &&
|
||||
(!strncmp(entry->d_name, "..", 2))))) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip FAT volume ID */
|
||||
if (entry->attribute & ATTR_VOLUME_ID) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* filter out dotfiles and hidden files */
|
||||
if (global_settings.dirfilter != SHOW_ALL &&
|
||||
((entry->d_name[0]=='.') ||
|
||||
(entry->attribute & ATTR_HIDDEN))) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
dptr->attr = entry->attribute;
|
||||
|
||||
/* mark mp? and m3u files as such */
|
||||
if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) {
|
||||
if (!strcasecmp(&entry->d_name[len-4], ".mp3") ||
|
||||
(!strcasecmp(&entry->d_name[len-4], ".mp2")) ||
|
||||
(!strcasecmp(&entry->d_name[len-4], ".mpa")))
|
||||
dptr->attr |= TREE_ATTR_MPA;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".m3u"))
|
||||
dptr->attr |= TREE_ATTR_M3U;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".cfg"))
|
||||
dptr->attr |= TREE_ATTR_CFG;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".wps"))
|
||||
dptr->attr |= TREE_ATTR_WPS;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".txt"))
|
||||
dptr->attr |= TREE_ATTR_TXT;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".lng"))
|
||||
dptr->attr |= TREE_ATTR_LNG;
|
||||
#ifdef HAVE_RECORDER_KEYPAD
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".fnt"))
|
||||
dptr->attr |= TREE_ATTR_FONT;
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".ajz"))
|
||||
#else
|
||||
else if (!strcasecmp(&entry->d_name[len-4], ".mod"))
|
||||
#endif
|
||||
dptr->attr |= TREE_ATTR_MOD;
|
||||
else if (!strcasecmp(&entry->d_name[len-5], ".rock"))
|
||||
dptr->attr |= TREE_ATTR_ROCK;
|
||||
}
|
||||
|
||||
/* memorize/compare details about the boot file */
|
||||
if ((currdir[1] == 0) && !strcmp(entry->d_name, BOOTFILE)) {
|
||||
if (boot_size) {
|
||||
if ((entry->size != boot_size) ||
|
||||
(entry->startcluster != boot_cluster))
|
||||
boot_changed = true;
|
||||
}
|
||||
boot_size = entry->size;
|
||||
boot_cluster = entry->startcluster;
|
||||
}
|
||||
|
||||
/* filter out all non-playlist files */
|
||||
if ( global_settings.dirfilter == SHOW_PLAYLIST &&
|
||||
(!(dptr->attr &
|
||||
(ATTR_DIRECTORY|TREE_ATTR_M3U))) ) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* filter out non-music files */
|
||||
if ( global_settings.dirfilter == SHOW_MUSIC &&
|
||||
(!(dptr->attr &
|
||||
(ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* filter out non-supported files */
|
||||
if ( global_settings.dirfilter == SHOW_SUPPORTED &&
|
||||
(!(dptr->attr & TREE_ATTR_MASK)) ) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len > name_buffer_size - name_buffer_length - 1) {
|
||||
/* Tell the world that we ran out of buffer space */
|
||||
dir_buffer_full = true;
|
||||
break;
|
||||
}
|
||||
dptr->name = &name_buffer[name_buffer_length];
|
||||
strcpy(dptr->name,entry->d_name);
|
||||
name_buffer_length += len + 1;
|
||||
}
|
||||
filesindir = i;
|
||||
closedir(dir);
|
||||
strncpy(lastdir,path,sizeof(lastdir));
|
||||
lastdir[sizeof(lastdir)-1] = 0;
|
||||
qsort(dircache,filesindir,sizeof(struct entry),compare);
|
||||
if (!load_and_sort_directory(path, global_settings.dirfilter,
|
||||
&filesindir, &dir_buffer_full))
|
||||
return -1;
|
||||
|
||||
if ( dir_buffer_full || filesindir == max_files_in_dir ) {
|
||||
#ifdef HAVE_LCD_CHARCELLS
|
||||
|
|
@ -531,7 +540,7 @@ static int showdir(char *path, int start)
|
|||
return filesindir;
|
||||
}
|
||||
|
||||
bool ask_resume(void)
|
||||
static bool ask_resume(void)
|
||||
{
|
||||
#ifdef HAVE_LCD_CHARCELLS
|
||||
lcd_double_height(false);
|
||||
|
|
@ -570,92 +579,62 @@ bool ask_resume(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
void start_resume(void)
|
||||
/* load tracks from specified directory to resume play */
|
||||
void resume_directory(char *dir)
|
||||
{
|
||||
bool buffer_full;
|
||||
|
||||
if (!load_and_sort_directory(dir, global_settings.dirfilter, &filesindir,
|
||||
&buffer_full))
|
||||
return;
|
||||
lastdir[0] = 0;
|
||||
|
||||
build_playlist(0);
|
||||
}
|
||||
|
||||
/* Returns the current working directory and also writes cwd to buf if
|
||||
non-NULL. In case of error, returns NULL. */
|
||||
char *getcwd(char *buf, int size)
|
||||
{
|
||||
if (!buf)
|
||||
return currdir;
|
||||
else if (size > 0)
|
||||
{
|
||||
strncpy(buf, currdir, size);
|
||||
return buf;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Force a reload of the directory next time directory browser is called */
|
||||
void reload_directory(void)
|
||||
{
|
||||
reload_dir = true;
|
||||
}
|
||||
|
||||
static void start_resume(void)
|
||||
{
|
||||
if ( global_settings.resume &&
|
||||
global_settings.resume_index != -1 ) {
|
||||
int len = strlen(global_settings.resume_file);
|
||||
|
||||
DEBUGF("Resume file %s\n",global_settings.resume_file);
|
||||
DEBUGF("Resume index %X offset %X\n",
|
||||
global_settings.resume_index,
|
||||
global_settings.resume_offset);
|
||||
DEBUGF("Resume shuffle %s seed %X\n",
|
||||
global_settings.playlist_shuffle?"on":"off",
|
||||
global_settings.resume_seed);
|
||||
|
||||
/* playlist? */
|
||||
if (!strcasecmp(&global_settings.resume_file[len-4], ".m3u")) {
|
||||
char* slash;
|
||||
if (!ask_resume())
|
||||
return;
|
||||
|
||||
if (playlist_resume() != -1)
|
||||
{
|
||||
playlist_start(global_settings.resume_index,
|
||||
global_settings.resume_offset);
|
||||
|
||||
/* check that the file exists */
|
||||
int fd = open(global_settings.resume_file, O_RDONLY);
|
||||
if(fd<0)
|
||||
return;
|
||||
close(fd);
|
||||
|
||||
if (!ask_resume())
|
||||
return;
|
||||
|
||||
slash = strrchr(global_settings.resume_file,'/');
|
||||
if (slash) {
|
||||
*slash=0;
|
||||
play_list(global_settings.resume_file,
|
||||
slash+1,
|
||||
global_settings.resume_index,
|
||||
true, /* the index is AFTER shuffle */
|
||||
global_settings.resume_offset,
|
||||
global_settings.resume_seed,
|
||||
global_settings.resume_first_index,
|
||||
global_settings.queue_resume,
|
||||
global_settings.queue_resume_index);
|
||||
*slash='/';
|
||||
}
|
||||
else {
|
||||
/* check that the dir exists */
|
||||
DIR* dir = opendir(global_settings.resume_file);
|
||||
if(!dir)
|
||||
return;
|
||||
closedir(dir);
|
||||
|
||||
if (!ask_resume())
|
||||
return;
|
||||
|
||||
play_list("/",
|
||||
global_settings.resume_file,
|
||||
global_settings.resume_index,
|
||||
true,
|
||||
global_settings.resume_offset,
|
||||
global_settings.resume_seed,
|
||||
global_settings.resume_first_index,
|
||||
global_settings.queue_resume,
|
||||
global_settings.queue_resume_index);
|
||||
}
|
||||
status_set_playmode(STATUS_PLAY);
|
||||
status_draw(true);
|
||||
wps_show();
|
||||
}
|
||||
else {
|
||||
if (!ask_resume())
|
||||
return;
|
||||
|
||||
if (showdir(global_settings.resume_file, 0) < 0 )
|
||||
return;
|
||||
|
||||
lastdir[0] = '\0';
|
||||
|
||||
build_playlist(global_settings.resume_index);
|
||||
play_list(global_settings.resume_file,
|
||||
NULL,
|
||||
global_settings.resume_index,
|
||||
true,
|
||||
global_settings.resume_offset,
|
||||
global_settings.resume_seed,
|
||||
global_settings.resume_first_index,
|
||||
global_settings.queue_resume,
|
||||
global_settings.queue_resume_index);
|
||||
}
|
||||
|
||||
status_set_playmode(STATUS_PLAY);
|
||||
status_draw(true);
|
||||
wps_show();
|
||||
else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -751,19 +730,33 @@ static bool handle_on(int* ds, int* dc, int numentries, int tree_max_on_screen)
|
|||
|
||||
case BUTTON_PLAY:
|
||||
case BUTTON_RC_PLAY:
|
||||
case BUTTON_ON | BUTTON_PLAY:
|
||||
case BUTTON_ON | BUTTON_PLAY: {
|
||||
int onplay_result;
|
||||
|
||||
if (currdir[1])
|
||||
snprintf(buf, sizeof buf, "%s/%s",
|
||||
currdir, dircache[dircursor+dirstart].name);
|
||||
else
|
||||
snprintf(buf, sizeof buf, "/%s",
|
||||
dircache[dircursor+dirstart].name);
|
||||
if (onplay(buf, dircache[dircursor+dirstart].attr))
|
||||
reload_dir = 1;
|
||||
onplay_result = onplay(buf,
|
||||
dircache[dircursor+dirstart].attr);
|
||||
switch (onplay_result)
|
||||
{
|
||||
case ONPLAY_OK:
|
||||
used = true;
|
||||
break;
|
||||
case ONPLAY_RELOAD_DIR:
|
||||
reload_dir = 1;
|
||||
used = true;
|
||||
break;
|
||||
case ONPLAY_START_PLAY:
|
||||
used = false; /* this will enable the wps */
|
||||
break;
|
||||
}
|
||||
exit = true;
|
||||
used = true;
|
||||
break;
|
||||
|
||||
}
|
||||
case BUTTON_ON | BUTTON_REL:
|
||||
case BUTTON_ON | TREE_PREV | BUTTON_REL:
|
||||
case BUTTON_ON | TREE_NEXT | BUTTON_REL:
|
||||
|
|
@ -793,7 +786,7 @@ static bool handle_on(int* ds, int* dc, int numentries, int tree_max_on_screen)
|
|||
return used;
|
||||
}
|
||||
|
||||
bool dirbrowse(char *root)
|
||||
static bool dirbrowse(char *root)
|
||||
{
|
||||
int numentries=0;
|
||||
char buf[MAX_PATH];
|
||||
|
|
@ -934,41 +927,36 @@ bool dirbrowse(char *root)
|
|||
lcd_stop_scroll();
|
||||
switch ( file->attr & TREE_ATTR_MASK ) {
|
||||
case TREE_ATTR_M3U:
|
||||
if ( global_settings.resume ) {
|
||||
if (currdir[1])
|
||||
snprintf(global_settings.resume_file,
|
||||
MAX_PATH, "%s/%s",
|
||||
currdir, file->name);
|
||||
else
|
||||
snprintf(global_settings.resume_file,
|
||||
MAX_PATH, "/%s", file->name);
|
||||
}
|
||||
play_list(currdir, file->name, 0, false, 0,
|
||||
seed, 0, 0, -1);
|
||||
start_index = 0;
|
||||
play = true;
|
||||
break;
|
||||
|
||||
case TREE_ATTR_MPA:
|
||||
if ( global_settings.resume )
|
||||
strncpy(global_settings.resume_file,
|
||||
currdir, MAX_PATH);
|
||||
|
||||
start_index =
|
||||
build_playlist(dircursor+dirstart);
|
||||
|
||||
/* when shuffling dir.: play all files even if the
|
||||
file selected by user is not the first one */
|
||||
if (global_settings.playlist_shuffle
|
||||
&& !global_settings.play_selected)
|
||||
if (playlist_create(currdir, file->name) != -1)
|
||||
{
|
||||
if (global_settings.playlist_shuffle)
|
||||
playlist_shuffle(seed, -1);
|
||||
start_index = 0;
|
||||
|
||||
/* it is important that we get back the index
|
||||
in the (shuffled) list and store that */
|
||||
start_index = play_list(currdir, NULL,
|
||||
start_index, false,
|
||||
0, seed, 0, 0, -1);
|
||||
play = true;
|
||||
playlist_start(start_index,0);
|
||||
play = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TREE_ATTR_MPA:
|
||||
if (playlist_create(currdir, NULL) != -1)
|
||||
{
|
||||
start_index =
|
||||
build_playlist(dircursor+dirstart);
|
||||
if (global_settings.playlist_shuffle)
|
||||
{
|
||||
start_index =
|
||||
playlist_shuffle(seed,start_index);
|
||||
|
||||
/* when shuffling dir.: play all files
|
||||
even if the file selected by user is
|
||||
not the first one */
|
||||
if (!global_settings.play_selected)
|
||||
start_index = 0;
|
||||
}
|
||||
|
||||
playlist_start(start_index, 0);
|
||||
play = true;
|
||||
}
|
||||
break;
|
||||
|
||||
/* wps config file */
|
||||
|
|
@ -1055,9 +1043,6 @@ bool dirbrowse(char *root)
|
|||
shuffled list in case shuffle is enabled */
|
||||
global_settings.resume_index = start_index;
|
||||
global_settings.resume_offset = 0;
|
||||
global_settings.resume_first_index =
|
||||
playlist_first_index();
|
||||
global_settings.resume_seed = seed;
|
||||
settings_save();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue