Patch #5179 by Sebastian Henriksen and Hardeep Sidhu - Playlist catalog
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10232 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
d4100c4cb1
commit
da0525f54f
|
@ -13,6 +13,7 @@ menu.c
|
|||
misc.c
|
||||
onplay.c
|
||||
playlist.c
|
||||
playlist_catalog.c
|
||||
playlist_menu.c
|
||||
playlist_viewer.c
|
||||
plugin.c
|
||||
|
|
|
@ -8571,3 +8571,88 @@
|
|||
*: "Export modifications"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CATALOG
|
||||
desc: in onplay menu
|
||||
user:
|
||||
<source>
|
||||
*: "Playlist catalog"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Playlist catalog"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Playlist catalog"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CATALOG_ADD_TO
|
||||
desc: in onplay playlist catalog submenu
|
||||
user:
|
||||
<source>
|
||||
*: "Add to playlist"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Add to playlist"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Add to playlist"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CATALOG_ADD_TO_NEW
|
||||
desc: in onplay playlist catalog submenu
|
||||
user:
|
||||
<source>
|
||||
*: "Add to new playlist"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Add to new playlist"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Add to new playlist"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CATALOG_VIEW
|
||||
desc: in onplay playlist catalog submenu
|
||||
user:
|
||||
<source>
|
||||
*: "View catalog"
|
||||
</source>
|
||||
<dest>
|
||||
*: "View catalog"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "View catalog"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CATALOG_NO_DIRECTORY
|
||||
desc: error message when playlist catalog directory doesn't exist
|
||||
user:
|
||||
<source>
|
||||
*: "%s doesn't exist"
|
||||
</source>
|
||||
<dest>
|
||||
*: "%s doesn't exist"
|
||||
</dest>
|
||||
<voice>
|
||||
*: ""
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CATALOG_NO_PLAYLISTS
|
||||
desc: error message when no playlists for playlist catalog
|
||||
user:
|
||||
<source>
|
||||
*: "No playlists"
|
||||
</source>
|
||||
<dest>
|
||||
*: "No playlists"
|
||||
</dest>
|
||||
<voice>
|
||||
*: ""
|
||||
</voice>
|
||||
</phrase>
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "eq_menu.h"
|
||||
#endif
|
||||
#include "playlist_menu.h"
|
||||
#include "playlist_catalog.h"
|
||||
|
||||
static int context;
|
||||
static char* selected_file = NULL;
|
||||
|
@ -222,6 +223,50 @@ static bool view_playlist(void)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool cat_add_to_a_playlist(void)
|
||||
{
|
||||
return catalog_add_to_a_playlist(selected_file, selected_file_attr,
|
||||
false);
|
||||
}
|
||||
|
||||
bool cat_add_to_a_new_playlist(void)
|
||||
{
|
||||
return catalog_add_to_a_playlist(selected_file, selected_file_attr, true);
|
||||
}
|
||||
|
||||
static bool cat_playlist_options(void)
|
||||
{
|
||||
struct menu_item items[3];
|
||||
int m, i=0, result;
|
||||
bool ret = false;
|
||||
|
||||
if ((audio_status() & AUDIO_STATUS_PLAY && context == CONTEXT_WPS) ||
|
||||
context == CONTEXT_TREE)
|
||||
{
|
||||
if (context == CONTEXT_WPS)
|
||||
{
|
||||
items[i].desc = ID2P(LANG_CATALOG_VIEW);
|
||||
items[i].function = catalog_view_playlists;
|
||||
i++;
|
||||
}
|
||||
|
||||
items[i].desc = ID2P(LANG_CATALOG_ADD_TO);
|
||||
items[i].function = cat_add_to_a_playlist;
|
||||
i++;
|
||||
items[i].desc = ID2P(LANG_CATALOG_ADD_TO_NEW);
|
||||
items[i].function = cat_add_to_a_new_playlist;
|
||||
i++;
|
||||
}
|
||||
|
||||
m = menu_init( items, i, NULL, NULL, NULL, NULL );
|
||||
result = menu_show(m);
|
||||
if(result >= 0)
|
||||
ret = items[result].function();
|
||||
menu_exit(m);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sub-menu for playlist options */
|
||||
static bool playlist_options(void)
|
||||
{
|
||||
|
@ -773,9 +818,9 @@ static int onplay_callback(int key, int menu)
|
|||
int onplay(char* file, int attr, int from)
|
||||
{
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
struct menu_item items[13]; /* increase this if you add entries! */
|
||||
struct menu_item items[14]; /* increase this if you add entries! */
|
||||
#else
|
||||
struct menu_item items[11];
|
||||
struct menu_item items[12];
|
||||
#endif
|
||||
int m, i=0, result;
|
||||
#ifdef HAVE_LCD_COLOR
|
||||
|
@ -803,6 +848,9 @@ int onplay(char* file, int attr, int from)
|
|||
items[i].desc = ID2P(LANG_PLAYLIST);
|
||||
items[i].function = playlist_options;
|
||||
i++;
|
||||
items[i].desc = ID2P(LANG_CATALOG);
|
||||
items[i].function = cat_playlist_options;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (context == CONTEXT_WPS)
|
||||
|
|
256
apps/playlist.c
256
apps/playlist.c
|
@ -133,6 +133,13 @@
|
|||
|
||||
#define PLAYLIST_DISPLAY_COUNT 10
|
||||
|
||||
struct directory_search_context {
|
||||
struct playlist_info* playlist;
|
||||
int position;
|
||||
bool queue;
|
||||
int count;
|
||||
};
|
||||
|
||||
static bool changing_dir = false;
|
||||
|
||||
static struct playlist_info current_playlist;
|
||||
|
@ -151,9 +158,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
|
|||
static int add_track_to_playlist(struct playlist_info* playlist,
|
||||
const char *filename, int position,
|
||||
bool queue, int seek_pos);
|
||||
static int add_directory_to_playlist(struct playlist_info* playlist,
|
||||
const char *dirname, int *position,
|
||||
bool queue, int *count, bool recurse);
|
||||
static int directory_search_callback(char* filename, void* context);
|
||||
static int remove_track_from_playlist(struct playlist_info* playlist,
|
||||
int position, bool write);
|
||||
static int randomise_playlist(struct playlist_info* playlist,
|
||||
|
@ -681,121 +686,46 @@ static int add_track_to_playlist(struct playlist_info* playlist,
|
|||
}
|
||||
|
||||
/*
|
||||
* Insert directory into playlist. May be called recursively.
|
||||
* Callback for playlist_directory_tracksearch to insert track into
|
||||
* playlist.
|
||||
*/
|
||||
static int add_directory_to_playlist(struct playlist_info* playlist,
|
||||
const char *dirname, int *position,
|
||||
bool queue, int *count, bool recurse)
|
||||
static int directory_search_callback(char* filename, void* context)
|
||||
{
|
||||
char buf[MAX_PATH+1];
|
||||
unsigned char *count_str;
|
||||
int result = 0;
|
||||
int num_files = 0;
|
||||
int i;
|
||||
struct entry *files;
|
||||
struct tree_context* tc = tree_get_context();
|
||||
int dirfilter = *(tc->dirfilter);
|
||||
struct directory_search_context* c =
|
||||
(struct directory_search_context*) context;
|
||||
int insert_pos;
|
||||
|
||||
/* use the tree browser dircache to load files */
|
||||
*(tc->dirfilter) = SHOW_ALL;
|
||||
insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
|
||||
c->queue, -1);
|
||||
|
||||
if (ft_load(tc, dirname) < 0)
|
||||
{
|
||||
gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
|
||||
*(tc->dirfilter) = dirfilter;
|
||||
if (insert_pos < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
files = (struct entry*) tc->dircache;
|
||||
num_files = tc->filesindir;
|
||||
|
||||
/* we've overwritten the dircache so tree browser will need to be
|
||||
reloaded */
|
||||
reload_directory();
|
||||
|
||||
if (queue)
|
||||
count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
|
||||
else
|
||||
count_str = str(LANG_PLAYLIST_INSERT_COUNT);
|
||||
|
||||
for (i=0; i<num_files; i++)
|
||||
|
||||
(c->count)++;
|
||||
|
||||
/* Make sure tracks are inserted in correct order if user requests
|
||||
INSERT_FIRST */
|
||||
if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
|
||||
c->position = insert_pos + 1;
|
||||
|
||||
if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
|
||||
{
|
||||
/* user abort */
|
||||
if (button_get(false) == SETTINGS_CANCEL)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
unsigned char* count_str;
|
||||
|
||||
if (files[i].attr & ATTR_DIRECTORY)
|
||||
{
|
||||
if (recurse)
|
||||
{
|
||||
/* recursively add directories */
|
||||
snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
|
||||
result = add_directory_to_playlist(playlist, buf, position,
|
||||
queue, count, recurse);
|
||||
if (result < 0)
|
||||
break;
|
||||
if (c->queue)
|
||||
count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
|
||||
else
|
||||
count_str = str(LANG_PLAYLIST_INSERT_COUNT);
|
||||
|
||||
/* we now need to reload our current directory */
|
||||
if(ft_load(tc, dirname) < 0)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
files = (struct entry*) tc->dircache;
|
||||
num_files = tc->filesindir;
|
||||
if (!num_files)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
|
||||
{
|
||||
int insert_pos;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
|
||||
|
||||
insert_pos = add_track_to_playlist(playlist, buf, *position,
|
||||
queue, -1);
|
||||
if (insert_pos < 0)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
|
||||
/* Make sure tracks are inserted in correct order if user requests
|
||||
INSERT_FIRST */
|
||||
if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
|
||||
*position = insert_pos + 1;
|
||||
|
||||
if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
|
||||
{
|
||||
display_playlist_count(*count, count_str);
|
||||
|
||||
if (*count == PLAYLIST_DISPLAY_COUNT &&
|
||||
(audio_status() & AUDIO_STATUS_PLAY) &&
|
||||
playlist->started)
|
||||
audio_flush_and_reload_tracks();
|
||||
}
|
||||
|
||||
/* let the other threads work */
|
||||
yield();
|
||||
}
|
||||
display_playlist_count(c->count, count_str);
|
||||
|
||||
if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
|
||||
(audio_status() & AUDIO_STATUS_PLAY) &&
|
||||
c->playlist->started)
|
||||
audio_flush_and_reload_tracks();
|
||||
}
|
||||
|
||||
/* restore dirfilter */
|
||||
*(tc->dirfilter) = dirfilter;
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2811,9 +2741,9 @@ int playlist_insert_directory(struct playlist_info* playlist,
|
|||
const char *dirname, int position, bool queue,
|
||||
bool recurse)
|
||||
{
|
||||
int count = 0;
|
||||
int result;
|
||||
unsigned char *count_str;
|
||||
struct directory_search_context context;
|
||||
|
||||
if (!playlist)
|
||||
playlist = ¤t_playlist;
|
||||
|
@ -2829,18 +2759,23 @@ int playlist_insert_directory(struct playlist_info* playlist,
|
|||
else
|
||||
count_str = str(LANG_PLAYLIST_INSERT_COUNT);
|
||||
|
||||
display_playlist_count(count, count_str);
|
||||
display_playlist_count(0, count_str);
|
||||
|
||||
context.playlist = playlist;
|
||||
context.position = position;
|
||||
context.queue = queue;
|
||||
context.count = 0;
|
||||
|
||||
cpu_boost(true);
|
||||
|
||||
result = add_directory_to_playlist(playlist, dirname, &position, queue,
|
||||
&count, recurse);
|
||||
result = playlist_directory_tracksearch(dirname, recurse,
|
||||
directory_search_callback, &context);
|
||||
|
||||
sync_control(playlist, false);
|
||||
|
||||
cpu_boost(false);
|
||||
|
||||
display_playlist_count(count, count_str);
|
||||
display_playlist_count(context.count, count_str);
|
||||
|
||||
if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
|
||||
audio_flush_and_reload_tracks();
|
||||
|
@ -3403,3 +3338,98 @@ int playlist_save(struct playlist_info* playlist, char *filename)
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search specified directory for tracks and notify via callback. May be
|
||||
* called recursively.
|
||||
*/
|
||||
int playlist_directory_tracksearch(const char* dirname, bool recurse,
|
||||
int (*callback)(char*, void*),
|
||||
void* context)
|
||||
{
|
||||
char buf[MAX_PATH+1];
|
||||
int result = 0;
|
||||
int num_files = 0;
|
||||
int i;
|
||||
struct entry *files;
|
||||
struct tree_context* tc = tree_get_context();
|
||||
int old_dirfilter = *(tc->dirfilter);
|
||||
|
||||
if (!callback)
|
||||
return -1;
|
||||
|
||||
/* use the tree browser dircache to load files */
|
||||
*(tc->dirfilter) = SHOW_ALL;
|
||||
|
||||
if (ft_load(tc, dirname) < 0)
|
||||
{
|
||||
gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
|
||||
*(tc->dirfilter) = old_dirfilter;
|
||||
return -1;
|
||||
}
|
||||
|
||||
files = (struct entry*) tc->dircache;
|
||||
num_files = tc->filesindir;
|
||||
|
||||
/* we've overwritten the dircache so tree browser will need to be
|
||||
reloaded */
|
||||
reload_directory();
|
||||
|
||||
for (i=0; i<num_files; i++)
|
||||
{
|
||||
/* user abort */
|
||||
if (button_get(false) == SETTINGS_CANCEL)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (files[i].attr & ATTR_DIRECTORY)
|
||||
{
|
||||
if (recurse)
|
||||
{
|
||||
/* recursively add directories */
|
||||
snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
|
||||
result = playlist_directory_tracksearch(buf, recurse,
|
||||
callback, context);
|
||||
if (result < 0)
|
||||
break;
|
||||
|
||||
/* we now need to reload our current directory */
|
||||
if(ft_load(tc, dirname) < 0)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
files = (struct entry*) tc->dircache;
|
||||
num_files = tc->filesindir;
|
||||
if (!num_files)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
|
||||
|
||||
if (callback(buf, context) != 0)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* let the other threads work */
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* restore dirfilter */
|
||||
*(tc->dirfilter) = old_dirfilter;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -158,5 +158,8 @@ char *playlist_get_name(const struct playlist_info* playlist, char *buf,
|
|||
int playlist_get_track_info(struct playlist_info* playlist, int index,
|
||||
struct playlist_track_info* info);
|
||||
int playlist_save(struct playlist_info* playlist, char *filename);
|
||||
int playlist_directory_tracksearch(const char* dirname, bool recurse,
|
||||
int (*callback)(char*, void*),
|
||||
void* context);
|
||||
|
||||
#endif /* __PLAYLIST_H__ */
|
||||
|
|
|
@ -0,0 +1,504 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "action.h"
|
||||
#include "dir.h"
|
||||
#include "file.h"
|
||||
#include "filetree.h"
|
||||
#include "kernel.h"
|
||||
#include "keyboard.h"
|
||||
#include "lang.h"
|
||||
#include "list.h"
|
||||
#include "misc.h"
|
||||
#include "onplay.h"
|
||||
#include "playlist.h"
|
||||
#include "settings.h"
|
||||
#include "splash.h"
|
||||
#include "sprintf.h"
|
||||
#include "tree.h"
|
||||
#include "yesno.h"
|
||||
|
||||
#define PLAYLIST_CATALOG_CFG ROCKBOX_DIR "/playlist_catalog.config"
|
||||
#define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists"
|
||||
#define MAX_PLAYLISTS 400
|
||||
#define PLAYLIST_DISPLAY_COUNT 10
|
||||
|
||||
/* Use for recursive directory search */
|
||||
struct add_track_context {
|
||||
int fd;
|
||||
int count;
|
||||
};
|
||||
|
||||
/* keep track of most recently used playlist */
|
||||
static char most_recent_playlist[MAX_PATH];
|
||||
|
||||
/* directory where our playlists our stored (configured in
|
||||
PLAYLIST_CATALOG_CFG) */
|
||||
static char playlist_dir[MAX_PATH];
|
||||
static int playlist_dir_length;
|
||||
static bool playlist_dir_exists = false;
|
||||
|
||||
/* Retrieve playlist directory from config file and verify it exists */
|
||||
static int initialize_catalog(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
int f;
|
||||
DIR* dir;
|
||||
bool default_dir = true;
|
||||
|
||||
f = open(PLAYLIST_CATALOG_CFG, O_RDONLY);
|
||||
if (f >= 0)
|
||||
{
|
||||
char buf[MAX_PATH+5];
|
||||
|
||||
while (read_line(f, buf, sizeof(buf)))
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
|
||||
/* directory config is of the format: "dir: /path/to/dir" */
|
||||
if (settings_parseline(buf, &name, &value) &&
|
||||
!strncasecmp(name, "dir:", strlen(name)) &&
|
||||
strlen(value) > 0)
|
||||
{
|
||||
strncpy(playlist_dir, value, strlen(value));
|
||||
default_dir = false;
|
||||
}
|
||||
}
|
||||
|
||||
close(f);
|
||||
}
|
||||
|
||||
/* fall back to default directory if no or invalid config */
|
||||
if (default_dir)
|
||||
strncpy(playlist_dir, PLAYLIST_CATALOG_DEFAULT_DIR,
|
||||
sizeof(playlist_dir));
|
||||
|
||||
playlist_dir_length = strlen(playlist_dir);
|
||||
|
||||
dir = opendir(playlist_dir);
|
||||
if (dir)
|
||||
{
|
||||
playlist_dir_exists = true;
|
||||
closedir(dir);
|
||||
memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!playlist_dir_exists)
|
||||
{
|
||||
gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_DIRECTORY),
|
||||
playlist_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use the filetree functions to retrieve the list of playlists in the
|
||||
directory */
|
||||
static int create_playlist_list(char** playlists, int num_items,
|
||||
int* num_playlists)
|
||||
{
|
||||
int result = -1;
|
||||
int num_files = 0;
|
||||
int index = 0;
|
||||
int i;
|
||||
bool most_recent = false;
|
||||
struct entry *files;
|
||||
struct tree_context* tc = tree_get_context();
|
||||
int dirfilter = *(tc->dirfilter);
|
||||
|
||||
*num_playlists = 0;
|
||||
|
||||
/* use the tree browser dircache to load only playlists */
|
||||
*(tc->dirfilter) = SHOW_PLAYLIST;
|
||||
|
||||
if (ft_load(tc, playlist_dir) < 0)
|
||||
{
|
||||
gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_DIRECTORY),
|
||||
playlist_dir);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
files = (struct entry*) tc->dircache;
|
||||
num_files = tc->filesindir;
|
||||
|
||||
/* we've overwritten the dircache so tree browser will need to be
|
||||
reloaded */
|
||||
reload_directory();
|
||||
|
||||
/* if it exists, most recent playlist will always be index 0 */
|
||||
if (most_recent_playlist[0] != '\0')
|
||||
{
|
||||
index = 1;
|
||||
most_recent = true;
|
||||
}
|
||||
|
||||
for (i=0; i<num_files && index<num_items; i++)
|
||||
{
|
||||
if (files[i].attr & TREE_ATTR_M3U)
|
||||
{
|
||||
if (most_recent && !strncmp(files[i].name, most_recent_playlist,
|
||||
sizeof(most_recent_playlist)))
|
||||
{
|
||||
playlists[0] = files[i].name;
|
||||
most_recent = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
playlists[index] = files[i].name;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*num_playlists = index;
|
||||
|
||||
/* we couldn't find the most recent playlist, shift all playlists up */
|
||||
if (most_recent)
|
||||
{
|
||||
for (i=0; i<index-1; i++)
|
||||
playlists[i] = playlists[i+1];
|
||||
|
||||
(*num_playlists)--;
|
||||
|
||||
most_recent_playlist[0] = '\0';
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
exit:
|
||||
*(tc->dirfilter) = dirfilter;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Callback for gui_synclist */
|
||||
static char* playlist_callback_name(int selected_item, void* data,
|
||||
char* buffer)
|
||||
{
|
||||
char** playlists = (char**) data;
|
||||
|
||||
strncpy(buffer, playlists[selected_item], MAX_PATH);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Display all playlists in catalog. Selected "playlist" is returned.
|
||||
If "view" mode is set then we're not adding anything into playlist. */
|
||||
static int display_playlists(char* playlist, bool view)
|
||||
{
|
||||
int result = -1;
|
||||
int num_playlists = 0;
|
||||
int lastbutton = BUTTON_NONE;
|
||||
bool exit = false;
|
||||
char temp_buf[MAX_PATH];
|
||||
char* playlists[MAX_PLAYLISTS];
|
||||
struct gui_synclist playlist_lists;
|
||||
|
||||
if (create_playlist_list(playlists, sizeof(playlists),
|
||||
&num_playlists) != 0)
|
||||
return -1;
|
||||
|
||||
if (num_playlists <= 0)
|
||||
{
|
||||
gui_syncsplash(HZ*2, true, str(LANG_CATALOG_NO_PLAYLISTS));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!playlist)
|
||||
playlist = temp_buf;
|
||||
|
||||
gui_synclist_init(&playlist_lists, playlist_callback_name, playlists,
|
||||
false, 1);
|
||||
gui_synclist_set_nb_items(&playlist_lists, num_playlists);
|
||||
gui_synclist_draw(&playlist_lists);
|
||||
|
||||
while (!exit)
|
||||
{
|
||||
int button = button_get_w_tmo(HZ/2);
|
||||
char* sel_file;
|
||||
|
||||
gui_synclist_do_button(&playlist_lists, button);
|
||||
|
||||
sel_file = playlists[gui_synclist_get_sel_pos(&playlist_lists)];
|
||||
|
||||
switch (button)
|
||||
{
|
||||
case TREE_EXIT:
|
||||
#ifdef TREE_RC_EXIT
|
||||
case TREE_RC_EXIT:
|
||||
#endif
|
||||
#ifdef TREE_OFF
|
||||
case TREE_OFF:
|
||||
#endif
|
||||
exit = true;
|
||||
break;
|
||||
|
||||
#ifdef TREE_ENTER
|
||||
case TREE_ENTER:
|
||||
case TREE_ENTER | BUTTON_REPEAT:
|
||||
#endif
|
||||
#ifdef TREE_RC_RUN
|
||||
case TREE_RC_RUN:
|
||||
#endif
|
||||
case TREE_RUN:
|
||||
#ifdef TREE_RUN_PRE
|
||||
if (((button == TREE_RUN)
|
||||
#ifdef TREE_RC_RUN_PRE
|
||||
|| (button == TREE_RC_RUN))
|
||||
&& ((lastbutton != TREE_RC_RUN_PRE)
|
||||
#endif
|
||||
&& (lastbutton != TREE_RUN_PRE)))
|
||||
break;
|
||||
#endif
|
||||
|
||||
if (view)
|
||||
{
|
||||
/* In view mode, selecting a playlist starts playback */
|
||||
if (playlist_create(playlist_dir, sel_file) != -1)
|
||||
{
|
||||
if (global_settings.playlist_shuffle)
|
||||
playlist_shuffle(current_tick, -1);
|
||||
playlist_start(0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we found the playlist we want to add to */
|
||||
snprintf(playlist, MAX_PATH, "%s/%s", playlist_dir,
|
||||
sel_file);
|
||||
}
|
||||
|
||||
result = 0;
|
||||
exit = true;
|
||||
break;
|
||||
|
||||
case TREE_CONTEXT:
|
||||
#ifdef TREE_CONTEXT2
|
||||
case TREE_CONTEXT2:
|
||||
#endif
|
||||
#ifdef TREE_RC_CONTEXT
|
||||
case TREE_RC_CONTEXT:
|
||||
#endif
|
||||
/* context menu only available in view mode */
|
||||
if (view)
|
||||
{
|
||||
snprintf(playlist, MAX_PATH, "%s/%s", playlist_dir,
|
||||
sel_file);
|
||||
|
||||
if (onplay(playlist, TREE_ATTR_M3U,
|
||||
CONTEXT_TREE) != ONPLAY_OK)
|
||||
{
|
||||
result = 0;
|
||||
exit = true;
|
||||
}
|
||||
else
|
||||
gui_synclist_draw(&playlist_lists);
|
||||
}
|
||||
break;
|
||||
|
||||
case BUTTON_NONE:
|
||||
gui_syncstatusbar_draw(&statusbars, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(default_event_handler(button) == SYS_USB_CONNECTED)
|
||||
{
|
||||
result = -1;
|
||||
exit = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
lastbutton = button;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* display number of tracks inserted into playlists. Used for directory
|
||||
insert */
|
||||
static void display_insert_count(int count)
|
||||
{
|
||||
gui_syncsplash(0, true, str(LANG_PLAYLIST_INSERT_COUNT), count,
|
||||
#if CONFIG_KEYPAD == PLAYER_PAD
|
||||
str(LANG_STOP_ABORT)
|
||||
#else
|
||||
str(LANG_OFF_ABORT)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/* Add specified track into playlist. Callback from directory insert */
|
||||
static int add_track_to_playlist(char* filename, void* context)
|
||||
{
|
||||
struct add_track_context* c = (struct add_track_context*) context;
|
||||
|
||||
if (fdprintf(c->fd, "%s\n", filename) <= 0)
|
||||
return -1;
|
||||
|
||||
(c->count)++;
|
||||
|
||||
if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
|
||||
display_insert_count(c->count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add "sel" file into specified "playlist". How to insert depends on type
|
||||
of file */
|
||||
static int add_to_playlist(const char* playlist, char* sel, int sel_attr)
|
||||
{
|
||||
int fd;
|
||||
int result = -1;
|
||||
|
||||
fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND);
|
||||
if(fd < 0)
|
||||
return result;
|
||||
|
||||
/* In case we're in the playlist directory */
|
||||
reload_directory();
|
||||
|
||||
if ((sel_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
|
||||
{
|
||||
/* append the selected file */
|
||||
if (fdprintf(fd, "%s\n", sel) > 0)
|
||||
result = 0;
|
||||
}
|
||||
else if ((sel_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U)
|
||||
{
|
||||
/* append playlist */
|
||||
int f, fs, i;
|
||||
char buf[1024];
|
||||
|
||||
if(strcasecmp(playlist, sel) == 0)
|
||||
goto exit;
|
||||
|
||||
f = open(sel, O_RDONLY);
|
||||
if (f < 0)
|
||||
goto exit;
|
||||
|
||||
fs = filesize(f);
|
||||
|
||||
for (i=0; i<fs;)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = read(f, buf, sizeof(buf));
|
||||
if (n < 0)
|
||||
break;
|
||||
|
||||
if (write(fd, buf, n) < 0)
|
||||
break;
|
||||
|
||||
i += n;
|
||||
}
|
||||
|
||||
if (i >= fs)
|
||||
result = 0;
|
||||
|
||||
close(f);
|
||||
}
|
||||
else if (sel_attr & ATTR_DIRECTORY)
|
||||
{
|
||||
/* search directory for tracks and append to playlist */
|
||||
bool recurse = false;
|
||||
char *lines[] = {
|
||||
(char *)str(LANG_RECURSE_DIRECTORY_QUESTION),
|
||||
sel
|
||||
};
|
||||
struct text_message message={lines, 2};
|
||||
struct add_track_context context;
|
||||
|
||||
if (global_settings.recursive_dir_insert != RECURSE_ASK)
|
||||
recurse = (bool)global_settings.recursive_dir_insert;
|
||||
else
|
||||
{
|
||||
/* Ask if user wants to recurse directory */
|
||||
recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
|
||||
}
|
||||
|
||||
context.fd = fd;
|
||||
context.count = 0;
|
||||
|
||||
display_insert_count(0);
|
||||
|
||||
result = playlist_directory_tracksearch(sel, recurse,
|
||||
add_track_to_playlist, &context);
|
||||
|
||||
display_insert_count(context.count);
|
||||
}
|
||||
|
||||
exit:
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool catalog_view_playlists(void)
|
||||
{
|
||||
if (initialize_catalog() == -1)
|
||||
return false;
|
||||
|
||||
if (display_playlists(NULL, true) == -1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool catalog_add_to_a_playlist(char* sel, int sel_attr, bool new_playlist)
|
||||
{
|
||||
char playlist[MAX_PATH];
|
||||
|
||||
if (initialize_catalog() == -1)
|
||||
return false;
|
||||
|
||||
if (new_playlist)
|
||||
{
|
||||
snprintf(playlist, MAX_PATH, "%s/", playlist_dir);
|
||||
if (kbd_input(playlist, MAX_PATH))
|
||||
return false;
|
||||
|
||||
if(strlen(playlist) <= 4 ||
|
||||
strcasecmp(&playlist[strlen(playlist)-4], ".m3u"))
|
||||
strcat(playlist, ".m3u");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (display_playlists(playlist, false) == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (add_to_playlist(playlist, sel, sel_attr) == 0)
|
||||
{
|
||||
strncpy(most_recent_playlist, playlist+playlist_dir_length+1,
|
||||
sizeof(most_recent_playlist));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 Sebastian Henriksen, Hardeep Sidhu
|
||||
*
|
||||
* All files in this archive are subject to the GNU General Public License.
|
||||
* See the file COPYING in the source tree root for full license agreement.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef _PLAYLIST_CATALOG_H_
|
||||
#define _PLAYLIST_CATALOG_H_
|
||||
|
||||
/*
|
||||
* View list of playlists in catalog.
|
||||
* ret : true if no error
|
||||
*/
|
||||
bool catalog_view_playlists(void);
|
||||
|
||||
/*
|
||||
* Add something to a playlist (new or select from list of playlists in
|
||||
* catalog).
|
||||
* sel : the path of the music file, playlist or directory to add
|
||||
* sel_attr : the attributes that tell what type of file we're adding
|
||||
* new_playlist : whether we want to create a new playlist or add to an
|
||||
* existing one.
|
||||
* ret : true if the file was successfully added
|
||||
*/
|
||||
bool catalog_add_to_a_playlist(char* sel, int sel_attr, bool new_playlist);
|
||||
|
||||
#endif
|
|
@ -28,6 +28,7 @@
|
|||
#include "playlist_viewer.h"
|
||||
#include "talk.h"
|
||||
#include "lang.h"
|
||||
#include "playlist_catalog.h"
|
||||
#include "playlist_menu.h"
|
||||
|
||||
static bool save_playlist(void)
|
||||
|
@ -61,11 +62,12 @@ bool playlist_menu(void)
|
|||
bool result;
|
||||
|
||||
static const struct menu_item items[] = {
|
||||
{ ID2P(LANG_CREATE_PLAYLIST), create_playlist },
|
||||
{ ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer },
|
||||
{ ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
|
||||
{ ID2P(LANG_RECURSE_DIRECTORY), recurse_directory },
|
||||
{ ID2P(LANG_WARN_ERASEDYNPLAYLIST_MENU), warnon_option},
|
||||
{ ID2P(LANG_CREATE_PLAYLIST), create_playlist },
|
||||
{ ID2P(LANG_VIEW_DYNAMIC_PLAYLIST), playlist_viewer },
|
||||
{ ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
|
||||
{ ID2P(LANG_CATALOG), catalog_view_playlists },
|
||||
{ ID2P(LANG_RECURSE_DIRECTORY), recurse_directory },
|
||||
{ ID2P(LANG_WARN_ERASEDYNPLAYLIST_MENU), warnon_option },
|
||||
};
|
||||
|
||||
m = menu_init( items, sizeof items / sizeof(struct menu_item), NULL,
|
||||
|
|
Loading…
Reference in New Issue