diff options
Diffstat (limited to 'apps/plugins/properties.c')
-rw-r--r-- | apps/plugins/properties.c | 439 |
1 files changed, 195 insertions, 244 deletions
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c index fa3517726a..ce3c03694c 100644 --- a/apps/plugins/properties.c +++ b/apps/plugins/properties.c @@ -20,39 +20,50 @@ ****************************************************************************/ #include "plugin.h" +#ifdef HAVE_TAGCACHE +#include "lib/mul_id3.h" +#endif + +#if !defined(ARRAY_SIZE) + #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +struct dir_stats { + char dirname[MAX_PATH]; + int len; + unsigned int dir_count; + unsigned int file_count; + unsigned long long byte_count; +}; +enum props_types { + PROPS_FILE = 0, + PROPS_ID3, + PROPS_MUL_ID3, + PROPS_DIR +}; -bool its_a_dir = false; - -char str_filename[MAX_PATH]; -char str_dirname[MAX_PATH]; -char str_size[64]; -char str_dircount[64]; -char str_filecount[64]; -char str_date[64]; -char str_time[64]; - -char str_title[MAX_PATH]; -char str_composer[MAX_PATH]; -char str_artist[MAX_PATH]; -char str_albumartist[MAX_PATH]; -char str_album[MAX_PATH]; -char str_genre[MAX_PATH]; -char str_comment[MAX_PATH]; -char str_year[MAX_PATH]; -char str_discnum[MAX_PATH]; -char str_tracknum[MAX_PATH]; -char str_duration[32]; -char str_bitrate[32]; -char str_frequency[32]; - -unsigned nseconds; -unsigned long nsize; -int32_t size_unit; -struct tm tm; - -int num_properties; +static int props_type = PROPS_FILE; +static struct mp3entry id3; +#ifdef HAVE_TAGCACHE +static int mul_id3_count; +static int skipped_count; +#endif + +static char str_filename[MAX_PATH]; +static char str_dirname[MAX_PATH]; +static char str_size[64]; +static char str_dircount[64]; +static char str_filecount[64]; +static char str_date[64]; +static char str_time[64]; + +static unsigned long nsize; +static int32_t size_unit; +static struct tm tm; + +#define NUM_FILE_PROPERTIES 5 static const unsigned char* const props_file[] = { ID2P(LANG_PROPERTIES_PATH), str_dirname, @@ -60,20 +71,9 @@ static const unsigned char* const props_file[] = ID2P(LANG_PROPERTIES_SIZE), str_size, ID2P(LANG_PROPERTIES_DATE), str_date, ID2P(LANG_PROPERTIES_TIME), str_time, - ID2P(LANG_PROPERTIES_COMPOSER), str_composer, - ID2P(LANG_PROPERTIES_ARTIST), str_artist, - ID2P(LANG_PROPERTIES_ALBUMARTIST), str_albumartist, - ID2P(LANG_PROPERTIES_TITLE), str_title, - ID2P(LANG_PROPERTIES_ALBUM), str_album, - ID2P(LANG_PROPERTIES_GENRE), str_genre, - ID2P(LANG_PROPERTIES_COMMENT), str_comment, - ID2P(LANG_PROPERTIES_YEAR), str_year, - ID2P(LANG_PROPERTIES_DISCNUM), str_discnum, - ID2P(LANG_PROPERTIES_TRACKNUM), str_tracknum, - ID2P(LANG_PROPERTIES_DURATION), str_duration, - ID2P(LANG_PROPERTIES_BITRATE), str_bitrate, - ID2P(LANG_PROPERTIES_FREQUENCY), str_frequency, }; + +#define NUM_DIR_PROPERTIES 4 static const unsigned char* const props_dir[] = { ID2P(LANG_PROPERTIES_PATH), str_dirname, @@ -107,7 +107,6 @@ static bool file_properties(const char* selected_file) bool found = false; DIR* dir; struct dirent* entry; - static struct mp3entry id3; dir = rb->opendir(str_dirname); if (dir) @@ -128,81 +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); - num_properties = 5; - - int fd = rb->open(selected_file, O_RDONLY); - if (fd >= 0 && - rb->get_metadata(&id3, fd, selected_file)) - { - long dur = id3.length / 1000; /* seconds */ - rb->snprintf(str_composer, sizeof str_composer, - "%s", id3.composer ? id3.composer : ""); - rb->snprintf(str_artist, sizeof str_artist, - "%s", id3.artist ? id3.artist : ""); - rb->snprintf(str_albumartist, sizeof str_albumartist, - "%s", id3.albumartist ? id3.albumartist : ""); - rb->snprintf(str_title, sizeof str_title, - "%s", id3.title ? id3.title : ""); - rb->snprintf(str_album, sizeof str_album, - "%s", id3.album ? id3.album : ""); - rb->snprintf(str_genre, sizeof str_genre, - "%s", id3.genre_string ? id3.genre_string : ""); - rb->snprintf(str_comment, sizeof str_comment, - "%s", id3.comment ? id3.comment : ""); - - if (id3.year_string) - rb->snprintf(str_year, sizeof str_year, - "%s", id3.year_string); - else if (id3.year) - rb->snprintf(str_year, sizeof str_year, - "%d", id3.year); - else - rb->snprintf(str_year, sizeof str_year, - "%s", ""); - - if (id3.disc_string) - rb->snprintf(str_discnum, sizeof str_discnum, - "%s", id3.disc_string); - else if (id3.discnum) - rb->snprintf(str_discnum, sizeof str_discnum, - "%d", id3.discnum); - else - rb->snprintf(str_discnum, sizeof str_discnum, - "%s", ""); - - if (id3.track_string) - rb->snprintf(str_tracknum, sizeof str_tracknum, - "%s", id3.track_string); - else if(id3.tracknum) - rb->snprintf(str_tracknum, sizeof str_tracknum, - "%d", id3.tracknum); - else - rb->snprintf(str_tracknum, sizeof str_tracknum, - "%s", ""); - - rb->snprintf(str_bitrate, sizeof str_bitrate, - "%d kbps", id3.bitrate ? : 0); - rb->snprintf(str_frequency, sizeof str_frequency, - "%ld Hz", id3.frequency ? : 0); - num_properties += 12; - - if (dur > 0) - { - nseconds = dur; - if (dur < 3600) - rb->snprintf(str_duration, sizeof str_duration, - "%d:%02d", (int)(dur / 60), - (int)(dur % 60)); - else - rb->snprintf(str_duration, sizeof str_duration, - "%d:%02d:%02d", - (int)(dur / 3600), - (int)(dur % 3600 / 60), - (int)(dur % 60)); - num_properties++; - } - } - rb->close(fd); + if (!rb->mp3info(&id3, selected_file)) + props_type = PROPS_ID3; found = true; break; } @@ -212,15 +138,7 @@ static bool file_properties(const char* selected_file) return found; } -typedef struct { - char dirname[MAX_PATH]; - int len; - unsigned int dc; - unsigned int fc; - unsigned long long bc; -} DPS; - -static bool _dir_properties(DPS *dps) +static bool _dir_properties(struct dir_stats *stats) { /* recursively scan directories in search of files and informs the user of the progress */ @@ -231,11 +149,11 @@ static bool _dir_properties(DPS *dps) struct dirent* entry; result = true; - dirlen = rb->strlen(dps->dirname); - dir = rb->opendir(dps->dirname); + dirlen = rb->strlen(stats->dirname); + dir = rb->opendir(stats->dirname); if (!dir) { - rb->splashf(HZ*2, "%s", dps->dirname); + rb->splashf(HZ*2, "%s", stats->dirname); return false; /* open error */ } @@ -244,7 +162,7 @@ static bool _dir_properties(DPS *dps) { struct dirinfo info = rb->dir_get_info(dir, entry); /* append name to current directory */ - rb->snprintf(dps->dirname+dirlen, dps->len-dirlen, "/%s", + rb->snprintf(stats->dirname+dirlen, stats->len-dirlen, "/%s", entry->d_name); if (info.attribute & ATTR_DIRECTORY) @@ -253,29 +171,30 @@ static bool _dir_properties(DPS *dps) !rb->strcmp((char *)entry->d_name, "..")) continue; /* skip these */ - dps->dc++; /* new directory */ + stats->dir_count++; /* new directory */ if (*rb->current_tick - lasttick > (HZ/8)) { unsigned log; lasttick = *rb->current_tick; rb->lcd_clear_display(); rb->lcd_puts(0,0,"SCANNING..."); - rb->lcd_puts(0,1,dps->dirname); - rb->lcd_putsf(0,2,"Directories: %d", dps->dc); - rb->lcd_putsf(0,3,"Files: %d", dps->fc); - log = human_size_log(dps->bc); - rb->lcd_putsf(0,4,"Size: %lu %cB", (unsigned long)(dps->bc >> (10*log)), - rb->str(units[log])); + rb->lcd_puts(0,1,stats->dirname); + rb->lcd_putsf(0,2,"Directories: %d", stats->dir_count); + rb->lcd_putsf(0,3,"Files: %d", stats->file_count); + log = human_size_log(stats->byte_count); + rb->lcd_putsf(0,4,"Size: %lu %s", + (unsigned long)(stats->byte_count >> (10*log)), + rb->str(units[log])); rb->lcd_update(); } /* recursion */ - result = _dir_properties(dps); + result = _dir_properties(stats); } else { - dps->fc++; /* new file */ - dps->bc += info.size; + stats->file_count++; /* new file */ + stats->byte_count += info.size; } if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) result = false; @@ -285,17 +204,17 @@ static bool _dir_properties(DPS *dps) return result; } -static bool dir_properties(const char* selected_file, DPS *dps) +static bool dir_properties(const char* selected_file, struct dir_stats *stats) { unsigned log; - rb->strlcpy(dps->dirname, selected_file, MAX_PATH); + rb->strlcpy(stats->dirname, selected_file, MAX_PATH); #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); #endif - if (!_dir_properties(dps)) + if (!_dir_properties(stats)) { #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(false); @@ -308,13 +227,12 @@ static bool dir_properties(const char* selected_file, DPS *dps) #endif rb->strlcpy(str_dirname, selected_file, MAX_PATH); - rb->snprintf(str_dircount, sizeof str_dircount, "%d", dps->dc); - rb->snprintf(str_filecount, sizeof str_filecount, "%d", dps->fc); - log = human_size_log(dps->bc); - nsize = (long) (dps->bc >> (log*10)); + rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count); + rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count); + log = human_size_log(stats->byte_count); + nsize = (long) (stats->byte_count >> (log*10)); size_unit = units[log]; rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit)); - num_properties = 4; return true; } @@ -328,35 +246,20 @@ static const char * get_props(int selected_item, void* data, char *buffer, size_t buffer_len) { (void)data; - if(its_a_dir) - { - if(selected_item >= (int)(sizeof(props_dir) / sizeof(props_dir[0]))) - { - rb->strlcpy(buffer, "ERROR", buffer_len); - } - else - { - rb->strlcpy(buffer, p2str(props_dir[selected_item]), buffer_len); - } - } - else - { - if(selected_item >= (int)(sizeof(props_file) / sizeof(props_file[0]))) - { - rb->strlcpy(buffer, "ERROR", buffer_len); - } - else - { - rb->strlcpy(buffer, p2str(props_file[selected_item]), buffer_len); - } - } + if (PROPS_DIR == props_type) + rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ? "ERROR" : + (char *) p2str(props_dir[selected_item]), buffer_len); + else if (PROPS_FILE == props_type) + rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ? "ERROR" : + (char *) p2str(props_file[selected_item]), buffer_len); + return buffer; } static int speak_property_selection(int selected_item, void *data) { - DPS *dps = data; - int32_t id = P2ID((its_a_dir ? props_dir : props_file)[selected_item]); + struct dir_stats *stats = data; + int32_t id = P2ID((props_type == PROPS_DIR ? props_dir : props_file)[selected_item]); rb->talk_id(id, false); switch (id) { @@ -394,14 +297,11 @@ static int speak_property_selection(int selected_item, void *data) case LANG_PROPERTIES_TIME: rb->talk_time(&tm, true); break; - case LANG_PROPERTIES_DURATION: - rb->talk_value_decimal(nseconds, UNIT_TIME, 0, true); - break; case LANG_PROPERTIES_SUBDIRS: - rb->talk_number(dps->dc, true); + rb->talk_number(stats->dir_count, true); break; case LANG_PROPERTIES_FILES: - rb->talk_number(dps->fc, true); + rb->talk_number(stats->file_count, true); break; default: rb->talk_spell(props_file[selected_item + 1], true); @@ -410,33 +310,49 @@ static int speak_property_selection(int selected_item, void *data) return 0; } -enum plugin_status plugin_start(const void* parameter) +static int browse_file_or_dir(struct dir_stats *stats) { struct gui_synclist properties_lists; int button; - bool quit = false, usb = false; - const char *file = parameter; - if(!parameter) return PLUGIN_ERROR; -#ifdef HAVE_TOUCHSCREEN - rb->touchscreen_set_mode(rb->global_settings->touch_mode); -#endif - static DPS dps = { - .len = MAX_PATH, - .dc = 0, - .fc = 0, - .bc = 0, - }; + rb->gui_synclist_init(&properties_lists, &get_props, stats, false, 2, NULL); + rb->gui_synclist_set_title(&properties_lists, + rb->str(props_type == PROPS_DIR ? + LANG_PROPERTIES_DIRECTORY_PROPERTIES : + LANG_PROPERTIES_FILE_PROPERTIES), + NOICON); + rb->gui_synclist_set_icon_callback(&properties_lists, NULL); + if (rb->global_settings->talk_menu) + rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection); + rb->gui_synclist_set_nb_items(&properties_lists, + 2 * (props_type == PROPS_FILE ? NUM_FILE_PROPERTIES : + NUM_DIR_PROPERTIES)); + rb->gui_synclist_select_item(&properties_lists, 0); + rb->gui_synclist_draw(&properties_lists); + rb->gui_synclist_speak_item(&properties_lists); - /* determine if it's a file or a directory */ - bool found = false; + while(true) + { + button = rb->get_action(CONTEXT_LIST, HZ); + /* HZ so the status bar redraws corectly */ + if (rb->gui_synclist_do_button(&properties_lists,&button)) + continue; + switch(button) + { + case ACTION_STD_CANCEL: + return false; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + return true; + break; + } + } +} + +static bool determine_file_or_dir(void) +{ DIR* dir; struct dirent* entry; - char* ptr = rb->strrchr(file, '/') + 1; - int dirlen = (ptr - file); - - rb->strlcpy(str_dirname, file, dirlen + 1); - rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen); dir = rb->opendir(str_dirname); if (dir) @@ -446,69 +362,104 @@ enum plugin_status plugin_start(const void* parameter) if(!rb->strcmp(entry->d_name, str_filename)) { struct dirinfo info = rb->dir_get_info(dir, entry); - its_a_dir = info.attribute & ATTR_DIRECTORY ? true : false; - found = true; - break; + props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE; + rb->closedir(dir); + return true; } } rb->closedir(dir); } - /* now we know if it's a file or a dir or maybe something failed */ + return false; +} - if(!found) +#ifdef HAVE_TAGCACHE +bool mul_id3_add(const char *file_name) +{ + if (!file_name || rb->mp3info(&id3, file_name)) + skipped_count++; + else { - /* 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; + collect_id3(&id3, mul_id3_count == 0); + mul_id3_count++; } - /* get the info depending on its_a_dir */ - if(!(its_a_dir ? dir_properties(file, &dps) : file_properties(file))) + return true; +} +#endif + +enum plugin_status plugin_start(const void* parameter) +{ + static struct dir_stats stats = { - /* 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; - } + .len = MAX_PATH, + .dir_count = 0, + .file_count = 0, + .byte_count = 0, + }; - FOR_NB_SCREENS(i) - rb->viewportmanager_theme_enable(i, true, NULL); + const char *file = parameter; + if(!parameter) + return PLUGIN_ERROR; - rb->gui_synclist_init(&properties_lists, &get_props, &dps, false, 2, NULL); - rb->gui_synclist_set_title(&properties_lists, rb->str(its_a_dir ? LANG_PROPERTIES_DIRECTORY_PROPERTIES : LANG_PROPERTIES_FILE_PROPERTIES), NOICON); - rb->gui_synclist_set_icon_callback(&properties_lists, NULL); - if (rb->global_settings->talk_menu) - rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection); - rb->gui_synclist_set_nb_items(&properties_lists, num_properties * 2); - rb->gui_synclist_limit_scroll(&properties_lists, true); - rb->gui_synclist_select_item(&properties_lists, 0); - rb->gui_synclist_draw(&properties_lists); - rb->gui_synclist_speak_item(&properties_lists); +#ifdef HAVE_TOUCHSCREEN + rb->touchscreen_set_mode(rb->global_settings->touch_mode); +#endif - while(!quit) +#ifdef HAVE_TAGCACHE + if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))) /* db table selected */ { - button = rb->get_action(CONTEXT_LIST, HZ); - /* HZ so the status bar redraws corectly */ - if (rb->gui_synclist_do_button(&properties_lists,&button,LIST_WRAP_UNLESS_HELD)) - continue; - switch(button) + props_type = PROPS_MUL_ID3; + mul_id3_count = skipped_count = 0; + + if (!rb->tagtree_subentries_do_action(&mul_id3_add) || mul_id3_count == 0) + return PLUGIN_ERROR; + else if (mul_id3_count > 1) /* otherwise, the retrieved id3 can be used as-is */ + finalize_id3(&id3); + + if (skipped_count > 0) + rb->splashf(HZ*2, "Skipped %d", skipped_count); + } + 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()) { - case ACTION_STD_CANCEL: - quit = true; - break; - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - { - quit = true; - usb = true; - } - break; + /* 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, 1) : +#ifdef HAVE_TAGCACHE + props_type == PROPS_MUL_ID3 ? rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) : +#endif + browse_file_or_dir(&stats); FOR_NB_SCREENS(i) rb->viewportmanager_theme_undo(i, false); - return usb? PLUGIN_USB_CONNECTED: PLUGIN_OK; + return usb ? PLUGIN_USB_CONNECTED : PLUGIN_OK; } |