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
};
/* custom string representation of activity */
#define MAKE_ACT_STR(act) ((char[3]){'>', 'A'+ (act), 0x0})
void beep_play(unsigned int frequency, unsigned int duration,
unsigned int amplitude);

View file

@ -1611,8 +1611,33 @@ static bool list_viewers(void)
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)
{
#ifdef HAVE_TAGCACHE
if (!prepare_database_sel(param))
return false;
#endif
int ret = filetype_load_plugin((const char*)param, selected_file);
if (ret == PLUGIN_USB_CONNECTED)
onplay_result = ONPLAY_RELOAD_DIR;
@ -1717,10 +1742,8 @@ static int clipboard_callback(int action,
#ifdef HAVE_TAGCACHE
if (context == CONTEXT_ID3DB)
{
if (((selected_file_attr & FILE_ATTR_MASK) ==
FILE_ATTR_AUDIO) &&
(this_item == &track_info_item ||
this_item == &pictureflow_item))
if (this_item == &track_info_item ||
this_item == &pictureflow_item)
return action;
return ACTION_EXIT_MENUITEM;
}
@ -1895,6 +1918,10 @@ static int hotkey_tree_pl_insert_shuffled(void)
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)
return ONPLAY_START_PLAY;

View file

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

View file

@ -103,6 +103,7 @@ int plugin_open(const char *plugin, const char *parameter);
#include "buflib.h"
#include "buffering.h"
#include "tagcache.h"
#include "tagtree.h"
#include "viewport.h"
#include "ata_idle_notify.h"
#include "settings_list.h"
@ -157,7 +158,7 @@ int plugin_open(const char *plugin, const char *parameter);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* 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
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 (*onplay_show_playlist_cat_menu)(const char* track_name, int attr,
void (*add_to_pl_cb));
#ifdef HAVE_TAGCACHE
bool (*tagtree_subentries_do_action)(bool (*action_cb)(const char *file_name));
#endif
};
/* plugin header */

View file

@ -19,6 +19,11 @@
*
****************************************************************************/
#include "plugin.h"
#include "lib/id3.h"
#ifdef HAVE_TAGCACHE
#include "lib/mul_id3.h"
#endif
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
@ -35,12 +40,16 @@ struct dir_stats {
enum props_types {
PROPS_FILE = 0,
PROPS_ID3,
PROPS_MUL_ID3,
PROPS_DIR
};
static int props_type = PROPS_FILE;
static struct mp3entry id3;
#ifdef HAVE_TAGCACHE
static int mul_id3_count;
#endif
static char str_filename[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",
tm.tm_hour, tm.tm_min, tm.tm_sec);
int fd = rb->open(selected_file, O_RDONLY);
if (fd >= 0)
{
if (rb->get_metadata(&id3, fd, selected_file))
props_type = PROPS_ID3;
rb->close(fd);
}
if (retrieve_id3(&id3, selected_file, false))
props_type = PROPS_ID3;
found = true;
break;
}
@ -369,6 +372,19 @@ static bool determine_file_or_dir(void)
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)
{
static struct dir_stats stats =
@ -380,40 +396,62 @@ enum plugin_status plugin_start(const void* parameter)
};
const char *file = parameter;
if(!parameter || (file[0] != '/')) return PLUGIN_ERROR;
if(!parameter)
return PLUGIN_ERROR;
#ifdef HAVE_TOUCHSCREEN
rb->touchscreen_set_mode(rb->global_settings->touch_mode);
#endif
const char* file_name = rb->strrchr(file, '/') + 1;
int dirlen = (file_name - file);
rb->strlcpy(str_dirname, file, dirlen + 1);
rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
if(!determine_file_or_dir())
#ifdef HAVE_TAGCACHE
if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))) /* db table selected */
{
/* weird: we couldn't find the entry. This Should Never Happen (TM) */
rb->splashf(0, "File/Dir not found: %s", file);
rb->action_userabort(TIMEOUT_BLOCK);
return PLUGIN_OK;
}
props_type = PROPS_MUL_ID3;
init_mul_id3();
mul_id3_count = 0;
/* get the info depending on its_a_dir */
if(!(props_type == PROPS_DIR ? dir_properties(file, &stats) : file_properties(file)))
{
/* something went wrong (to do: tell user what it was (nesting,...) */
rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
rb->action_userabort(TIMEOUT_BLOCK);
return PLUGIN_OK;
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;
int dirlen = (file_name - file);
rb->strlcpy(str_dirname, file, dirlen + 1);
rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
if(!determine_file_or_dir())
{
/* weird: we couldn't find the entry. This Should Never Happen (TM) */
rb->splashf(0, "File/Dir not found: %s", file);
rb->action_userabort(TIMEOUT_BLOCK);
return PLUGIN_OK;
}
/* get the info depending on its_a_dir */
if(!(props_type == PROPS_DIR ? dir_properties(file, &stats) : file_properties(file)))
{
/* something went wrong (to do: tell user what it was (nesting,...) */
rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
rb->action_userabort(TIMEOUT_BLOCK);
return PLUGIN_OK;
}
}
else
return PLUGIN_ERROR;
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, true, NULL);
bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0, &tm) :
browse_file_or_dir(&stats);
bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0, &tm) :
(props_type == PROPS_MUL_ID3 ? rb->browse_id3(&id3, 0, 0, NULL) :
browse_file_or_dir(&stats));
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);

View file

@ -407,7 +407,7 @@ static int get_tag(int *tag)
static int get_clause(int *condition)
{
/* one or two operator conditionals */
/* one or two operator conditionals */
#define OPS2VAL(op1, op2) ((int)op1 << 8 | (int)op2)
#define CLAUSE(op1, op2, symbol) {OPS2VAL(op1, op2), symbol }
@ -2160,6 +2160,27 @@ static bool insert_all_playlist(struct tree_context *c,
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,
const char* playlist, bool new_playlist)
{
@ -2167,6 +2188,7 @@ static bool tagtree_insert_selection(int position, bool queue,
int dirlevel = tc->dirlevel;
int selected_item = tc->selected_item;
int newtable;
int ret;
show_search_progress(
#ifdef HAVE_DISK_STORAGE
@ -2176,71 +2198,101 @@ static bool tagtree_insert_selection(int position, bool queue,
#endif
, 0);
/* We need to set the table to allsubentries. */
newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
/* Insert a single track? */
if (newtable == PLAYTRACK)
if (newtable == PLAYTRACK) /* Insert a single track? */
{
if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
{
logf("tagtree_get_filename failed");
return false;
}
playlist_insert_track(NULL, buf, position, queue, 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)
splash(HZ, ID2P(LANG_END_PLAYLIST));
else
{
logf("insert_all_playlist");
if (!insert_all_playlist(tc, playlist, new_playlist, position, queue))
if (tc->filesindir <= 0)
splash(HZ, ID2P(LANG_END_PLAYLIST));
else if (!insert_all_playlist(tc, playlist, new_playlist, position, queue))
splash(HZ*2, ID2P(LANG_FAILED));
}
/* Finally return the dirlevel to its original value. */
while (tc->dirlevel > dirlevel)
tagtree_exit(tc, false);
tc->selected_item = selected_item;
tagtree_load(tc);
return true;
reset_tc_to_prev(dirlevel, selected_item);
return ret;
}
/* 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)
{

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_icon(struct tree_context* c);
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