database: make parent tables work with plugin

Enables the use of PictureFlow and the Properties plugin
with parent tables of ALLSUBENTRIES, such as an album
or album artist, instead of individual tracks.

Change-Id: I18c4779ed116a48c732ae32b9629e7e0d93ce7c8
This commit is contained in:
Christian Soffke 2023-01-10 18:50:05 +01:00
parent 4d53d1b52b
commit 32f365bf3c
7 changed files with 214 additions and 85 deletions

View file

@ -187,6 +187,9 @@ enum current_activity {
ACTIVITY_USBSCREEN ACTIVITY_USBSCREEN
}; };
/* custom string representation of activity */
#define MAKE_ACT_STR(act) ((char[3]){'>', 'A'+ (act), 0x0})
void beep_play(unsigned int frequency, unsigned int duration, void beep_play(unsigned int frequency, unsigned int duration,
unsigned int amplitude); unsigned int amplitude);

View file

@ -1611,8 +1611,33 @@ static bool list_viewers(void)
return false; return false;
} }
#ifdef HAVE_TAGCACHE
static bool prepare_database_sel(void *param)
{
if (context == CONTEXT_ID3DB &&
(selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
{
if (!strcmp(param, "properties"))
strmemccpy(selected_file_path, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER),
sizeof(selected_file_path));
else if (!tagtree_get_subentry_filename(selected_file_path, MAX_PATH))
{
onplay_result = ONPLAY_RELOAD_DIR;
return false;
}
selected_file = selected_file_path;
}
return true;
}
#endif
static bool onplay_load_plugin(void *param) static bool onplay_load_plugin(void *param)
{ {
#ifdef HAVE_TAGCACHE
if (!prepare_database_sel(param))
return false;
#endif
int ret = filetype_load_plugin((const char*)param, selected_file); int ret = filetype_load_plugin((const char*)param, selected_file);
if (ret == PLUGIN_USB_CONNECTED) if (ret == PLUGIN_USB_CONNECTED)
onplay_result = ONPLAY_RELOAD_DIR; onplay_result = ONPLAY_RELOAD_DIR;
@ -1717,10 +1742,8 @@ static int clipboard_callback(int action,
#ifdef HAVE_TAGCACHE #ifdef HAVE_TAGCACHE
if (context == CONTEXT_ID3DB) if (context == CONTEXT_ID3DB)
{ {
if (((selected_file_attr & FILE_ATTR_MASK) == if (this_item == &track_info_item ||
FILE_ATTR_AUDIO) && this_item == &pictureflow_item)
(this_item == &track_info_item ||
this_item == &pictureflow_item))
return action; return action;
return ACTION_EXIT_MENUITEM; return ACTION_EXIT_MENUITEM;
} }
@ -1895,6 +1918,10 @@ static int hotkey_tree_pl_insert_shuffled(void)
static int hotkey_tree_run_plugin(void *param) static int hotkey_tree_run_plugin(void *param)
{ {
#ifdef HAVE_TAGCACHE
if (!prepare_database_sel(param))
return ONPLAY_RELOAD_DIR;
#endif
if (filetype_load_plugin((const char*)param, selected_file) == PLUGIN_GOTO_WPS) if (filetype_load_plugin((const char*)param, selected_file) == PLUGIN_GOTO_WPS)
return ONPLAY_START_PLAY; return ONPLAY_START_PLAY;

View file

@ -825,6 +825,9 @@ static const struct plugin_api rockbox_api = {
splash_progress_set_delay, splash_progress_set_delay,
fix_path_part, fix_path_part,
onplay_show_playlist_cat_menu, onplay_show_playlist_cat_menu,
#if defined(HAVE_TAGCACHE)
tagtree_subentries_do_action,
#endif
}; };
static int plugin_buffer_handle; static int plugin_buffer_handle;

View file

@ -103,6 +103,7 @@ int plugin_open(const char *plugin, const char *parameter);
#include "buflib.h" #include "buflib.h"
#include "buffering.h" #include "buffering.h"
#include "tagcache.h" #include "tagcache.h"
#include "tagtree.h"
#include "viewport.h" #include "viewport.h"
#include "ata_idle_notify.h" #include "ata_idle_notify.h"
#include "settings_list.h" #include "settings_list.h"
@ -157,7 +158,7 @@ int plugin_open(const char *plugin, const char *parameter);
#define PLUGIN_MAGIC 0x526F634B /* RocK */ #define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */ /* increase this every time the api struct changes */
#define PLUGIN_API_VERSION 263 #define PLUGIN_API_VERSION 264
/* update this to latest version if a change to the api struct breaks /* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any backwards compatibility (and please take the opportunity to sort in any
@ -950,6 +951,9 @@ struct plugin_api {
void (*fix_path_part)(char* path, int offset, int count); void (*fix_path_part)(char* path, int offset, int count);
void (*onplay_show_playlist_cat_menu)(const char* track_name, int attr, void (*onplay_show_playlist_cat_menu)(const char* track_name, int attr,
void (*add_to_pl_cb)); void (*add_to_pl_cb));
#ifdef HAVE_TAGCACHE
bool (*tagtree_subentries_do_action)(bool (*action_cb)(const char *file_name));
#endif
}; };
/* plugin header */ /* plugin header */

View file

@ -19,6 +19,11 @@
* *
****************************************************************************/ ****************************************************************************/
#include "plugin.h" #include "plugin.h"
#include "lib/id3.h"
#ifdef HAVE_TAGCACHE
#include "lib/mul_id3.h"
#endif
#if !defined(ARRAY_SIZE) #if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
@ -35,12 +40,16 @@ struct dir_stats {
enum props_types { enum props_types {
PROPS_FILE = 0, PROPS_FILE = 0,
PROPS_ID3, PROPS_ID3,
PROPS_MUL_ID3,
PROPS_DIR PROPS_DIR
}; };
static int props_type = PROPS_FILE; static int props_type = PROPS_FILE;
static struct mp3entry id3; static struct mp3entry id3;
#ifdef HAVE_TAGCACHE
static int mul_id3_count;
#endif
static char str_filename[MAX_PATH]; static char str_filename[MAX_PATH];
static char str_dirname[MAX_PATH]; static char str_dirname[MAX_PATH];
@ -118,14 +127,8 @@ static bool file_properties(const char* selected_file)
rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d", rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
tm.tm_hour, tm.tm_min, tm.tm_sec); tm.tm_hour, tm.tm_min, tm.tm_sec);
int fd = rb->open(selected_file, O_RDONLY); if (retrieve_id3(&id3, selected_file, false))
if (fd >= 0)
{
if (rb->get_metadata(&id3, fd, selected_file))
props_type = PROPS_ID3; props_type = PROPS_ID3;
rb->close(fd);
}
found = true; found = true;
break; break;
} }
@ -369,6 +372,19 @@ static bool determine_file_or_dir(void)
return false; return false;
} }
#ifdef HAVE_TAGCACHE
bool mul_id3_add(const char *file_name)
{
if (!retrieve_id3(&id3, file_name, false))
return false;
collect_id3(&id3, mul_id3_count == 0);
mul_id3_count++;
return true;
}
#endif
enum plugin_status plugin_start(const void* parameter) enum plugin_status plugin_start(const void* parameter)
{ {
static struct dir_stats stats = static struct dir_stats stats =
@ -380,12 +396,30 @@ enum plugin_status plugin_start(const void* parameter)
}; };
const char *file = parameter; const char *file = parameter;
if(!parameter || (file[0] != '/')) return PLUGIN_ERROR; if(!parameter)
return PLUGIN_ERROR;
#ifdef HAVE_TOUCHSCREEN #ifdef HAVE_TOUCHSCREEN
rb->touchscreen_set_mode(rb->global_settings->touch_mode); rb->touchscreen_set_mode(rb->global_settings->touch_mode);
#endif #endif
#ifdef HAVE_TAGCACHE
if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))) /* db table selected */
{
props_type = PROPS_MUL_ID3;
init_mul_id3();
mul_id3_count = 0;
if (!rb->tagtree_subentries_do_action(&mul_id3_add) || mul_id3_count == 0)
return PLUGIN_ERROR;
if (mul_id3_count > 1) /* otherwise, the retrieved id3 can be used as-is */
write_id3_mul_tracks(&id3);
}
else
#endif
if (file[0] == '/') /* single track selected */
{
const char* file_name = rb->strrchr(file, '/') + 1; const char* file_name = rb->strrchr(file, '/') + 1;
int dirlen = (file_name - file); int dirlen = (file_name - file);
@ -408,12 +442,16 @@ enum plugin_status plugin_start(const void* parameter)
rb->action_userabort(TIMEOUT_BLOCK); rb->action_userabort(TIMEOUT_BLOCK);
return PLUGIN_OK; return PLUGIN_OK;
} }
}
else
return PLUGIN_ERROR;
FOR_NB_SCREENS(i) FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, true, NULL); rb->viewportmanager_theme_enable(i, true, NULL);
bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0, &tm) : bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0, &tm) :
browse_file_or_dir(&stats); (props_type == PROPS_MUL_ID3 ? rb->browse_id3(&id3, 0, 0, NULL) :
browse_file_or_dir(&stats));
FOR_NB_SCREENS(i) FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false); rb->viewportmanager_theme_undo(i, false);

View file

@ -2160,6 +2160,27 @@ static bool insert_all_playlist(struct tree_context *c,
return true; return true;
} }
static bool goto_allsubentries(int newtable)
{
int i = 0;
while (i < 2 && (newtable == NAVIBROWSE || newtable == ALLSUBENTRIES))
{
tagtree_enter(tc, false);
tagtree_load(tc);
newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
i++;
}
return (newtable == PLAYTRACK);
}
static void reset_tc_to_prev(int dirlevel, int selected_item)
{
while (tc->dirlevel > dirlevel)
tagtree_exit(tc, false);
tc->selected_item = selected_item;
tagtree_load(tc);
}
static bool tagtree_insert_selection(int position, bool queue, static bool tagtree_insert_selection(int position, bool queue,
const char* playlist, bool new_playlist) const char* playlist, bool new_playlist)
{ {
@ -2167,6 +2188,7 @@ static bool tagtree_insert_selection(int position, bool queue,
int dirlevel = tc->dirlevel; int dirlevel = tc->dirlevel;
int selected_item = tc->selected_item; int selected_item = tc->selected_item;
int newtable; int newtable;
int ret;
show_search_progress( show_search_progress(
#ifdef HAVE_DISK_STORAGE #ifdef HAVE_DISK_STORAGE
@ -2176,71 +2198,101 @@ static bool tagtree_insert_selection(int position, bool queue,
#endif #endif
, 0); , 0);
/* We need to set the table to allsubentries. */
newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
/* Insert a single track? */ if (newtable == PLAYTRACK) /* Insert a single track? */
if (newtable == PLAYTRACK)
{ {
if (tagtree_get_filename(tc, buf, sizeof buf) < 0) if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
{
logf("tagtree_get_filename failed");
return false; return false;
}
playlist_insert_track(NULL, buf, position, queue, true); playlist_insert_track(NULL, buf, position, queue, true);
return true; return true;
} }
if (newtable == NAVIBROWSE) ret = goto_allsubentries(newtable);
if (ret)
{ {
tagtree_enter(tc, false);
tagtree_load(tc);
newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
}
else if (newtable != ALLSUBENTRIES)
{
logf("unsupported table: %d", newtable);
return false;
}
/* Now the current table should be allsubentries. */
if (newtable != PLAYTRACK)
{
tagtree_enter(tc, false);
tagtree_load(tc);
newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
/* And now the newtable should be playtrack. */
if (newtable != PLAYTRACK)
{
logf("newtable: %d !!", newtable);
while (tc->dirlevel > dirlevel)
tagtree_exit(tc, false);
tagtree_load(tc);
return false;
}
}
if (tc->filesindir <= 0) if (tc->filesindir <= 0)
splash(HZ, ID2P(LANG_END_PLAYLIST)); splash(HZ, ID2P(LANG_END_PLAYLIST));
else else if (!insert_all_playlist(tc, playlist, new_playlist, position, queue))
{
logf("insert_all_playlist");
if (!insert_all_playlist(tc, playlist, new_playlist, position, queue))
splash(HZ*2, ID2P(LANG_FAILED)); splash(HZ*2, ID2P(LANG_FAILED));
} }
/* Finally return the dirlevel to its original value. */ reset_tc_to_prev(dirlevel, selected_item);
while (tc->dirlevel > dirlevel) return ret;
tagtree_exit(tc, false);
tc->selected_item = selected_item;
tagtree_load(tc);
return true;
} }
/* Execute action_cb for all subentries of the current table's
* selected item, handing over each entry's filename in the
* callback function parameter.
*/
bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name))
{
struct tagcache_search tcs;
int i, n;
unsigned long last_tick;
char buf[MAX_PATH];
int ret = true;
int dirlevel = tc->dirlevel;
int selected_item = tc->selected_item;
int newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
cpu_boost(true);
if (!goto_allsubentries(newtable))
ret = false;
else if (tagcache_search(&tcs, tag_filename))
{
last_tick = current_tick + HZ/2;
splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
n = tc->filesindir;
for (i = 0; i < n; i++)
{
splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
if (TIME_AFTER(current_tick, last_tick + HZ/4))
{
if (action_userabort(TIMEOUT_NOBLOCK))
break;
last_tick = current_tick;
}
if (!tagcache_retrieve(&tcs, tagtree_get_entry(tc, i)->extraseek,
tcs.type, buf, sizeof buf)
|| !action_cb(buf))
{
ret = false;
break;
}
yield();
}
tagcache_search_finish(&tcs);
}
else
{
splash(HZ, ID2P(LANG_TAGCACHE_BUSY));
ret = false;
}
reset_tc_to_prev(dirlevel, selected_item);
cpu_boost(false);
return ret;
}
/* Try to return first subentry's filename for current selection
*/
bool tagtree_get_subentry_filename(char *buf, size_t bufsize)
{
int ret = true;
int dirlevel = tc->dirlevel;
int selected_item = tc->selected_item;
int newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
if (!goto_allsubentries(newtable) || tagtree_get_filename(tc, buf, bufsize) < 0)
ret = false;
reset_tc_to_prev(dirlevel, selected_item);
return ret;
}
bool tagtree_current_playlist_insert(int position, bool queue) bool tagtree_current_playlist_insert(int position, bool queue)
{ {

View file

@ -45,6 +45,8 @@ char *tagtree_get_title(struct tree_context* c);
int tagtree_get_attr(struct tree_context* c); int tagtree_get_attr(struct tree_context* c);
int tagtree_get_icon(struct tree_context* c); int tagtree_get_icon(struct tree_context* c);
int tagtree_get_filename(struct tree_context* c, char *buf, int buflen); int tagtree_get_filename(struct tree_context* c, char *buf, int buflen);
bool tagtree_get_subentry_filename(char *buf, size_t bufsize);
bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name));
#endif #endif
#endif #endif