summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Soffke <christian.soffke@gmail.com>2024-07-25 20:49:20 +0200
committerChristian Soffke <christian.soffke@gmail.com>2024-07-28 20:18:41 +0200
commit545271c4de82c3b5da05ca91629d0de150c90d21 (patch)
tree51b579da8c05bdabeb64c426f883e1dca2f89782
parent6d19214eeb13835f3343675417651e2eb03e270f (diff)
downloadrockbox-545271c4de82c3b5da05ca91629d0de150c90d21.tar.gz
rockbox-545271c4de82c3b5da05ca91629d0de150c90d21.zip
plugins: extract redundant functionality from stats/properties
Also: - Enables cpu boosting for Stats (*much* faster on some players) - Displays file size in Stats Change-Id: I888ba054e1f8c2985fbf8dca36e11e6ef130e7ca
-rw-r--r--apps/plugins/lib/mul_id3.c188
-rw-r--r--apps/plugins/lib/mul_id3.h32
-rw-r--r--apps/plugins/properties.c155
-rw-r--r--apps/plugins/stats.c207
-rw-r--r--manual/plugins/stats.tex7
5 files changed, 252 insertions, 337 deletions
diff --git a/apps/plugins/lib/mul_id3.c b/apps/plugins/lib/mul_id3.c
index edf44f7282..c4b511c626 100644
--- a/apps/plugins/lib/mul_id3.c
+++ b/apps/plugins/lib/mul_id3.c
@@ -40,6 +40,13 @@ struct multiple_tracks_id3 {
40 40
41static struct multiple_tracks_id3 mul_id3; 41static struct multiple_tracks_id3 mul_id3;
42 42
43static const int32_t units[] =
44{
45 LANG_BYTE,
46 LANG_KIBIBYTE,
47 LANG_MEBIBYTE,
48 LANG_GIBIBYTE
49};
43 50
44/* Calculate modified FNV hash of string 51/* Calculate modified FNV hash of string
45 * has good avalanche behaviour and uniform distribution 52 * has good avalanche behaviour and uniform distribution
@@ -130,9 +137,7 @@ void collect_id3(struct mp3entry *id3, bool is_first_track)
130 mul_id3.filesize += id3->filesize; 137 mul_id3.filesize += id3->filesize;
131} 138}
132 139
133/* (!) Note scale factor applied to returned metadata: 140/* (!) Note unit conversion below
134 * - Unit for filesize will be KiB instead of Bytes
135 * - Unit for length will be s instead of ms
136 * 141 *
137 * Use result only as input for browse_id3, 142 * Use result only as input for browse_id3,
138 * with the track_ct parameter set to > 1. 143 * with the track_ct parameter set to > 1.
@@ -159,8 +164,8 @@ void finalize_id3(struct mp3entry *id3)
159 id3->track_string = NULL; 164 id3->track_string = NULL;
160 id3->year_string = NULL; 165 id3->year_string = NULL;
161 id3->year = mul_id3.year; 166 id3->year = mul_id3.year;
162 mul_id3.length /= 1000; 167 mul_id3.length /= 1000; /* convert from ms to s */
163 mul_id3.filesize >>= 10; 168 mul_id3.filesize >>= 10; /* convert from B to KiB */
164 id3->length = mul_id3.length > ULONG_MAX ? 0 : mul_id3.length; 169 id3->length = mul_id3.length > ULONG_MAX ? 0 : mul_id3.length;
165 id3->filesize = mul_id3.filesize > INT_MAX ? 0 : mul_id3.filesize; 170 id3->filesize = mul_id3.filesize > INT_MAX ? 0 : mul_id3.filesize;
166 id3->frequency = mul_id3.frequency; 171 id3->frequency = mul_id3.frequency;
@@ -172,3 +177,176 @@ void finalize_id3(struct mp3entry *id3)
172 id3->track_level = 0; 177 id3->track_level = 0;
173 id3->album_level = 0; 178 id3->album_level = 0;
174} 179}
180
181unsigned long human_size(unsigned long long byte_count, int32_t *unit_lang_id)
182{
183 const size_t n = sizeof(units)/sizeof(units[0]);
184 unsigned int i;
185
186 /* margin set at 10K boundary: 10239 B +1 => 10 KB */
187 for(i = 0; i < n-1 && byte_count >= 10*1024; i++)
188 byte_count >>= 10; /* div by 1024 */
189
190 *unit_lang_id = units[i];
191 return (unsigned long)byte_count;
192}
193
194/* missing filetype attribute for images */
195static const char *image_exts[] = {"bmp","jpg","jpe","jpeg","png","ppm"};
196/* and videos */
197static const char *video_exts[] = {"mpg","mpeg","mpv","m2v"};
198
199static void prn(const char *str, int y)
200{
201 rb->lcd_puts(0, y, str);
202#ifdef HAVE_REMOTE_LCD
203 rb->lcd_remote_puts(0, y, str);
204#endif
205}
206
207void display_dir_stats(struct dir_stats *stats)
208{
209 char buf[32];
210 int32_t lang_size_unit;
211 unsigned long display_size = human_size(stats->byte_count, &lang_size_unit);
212 rb->lcd_clear_display();
213#ifdef HAVE_REMOTE_LCD
214 rb->lcd_remote_clear_display();
215#endif
216 rb->snprintf(buf, sizeof(buf), "Files: %d (%lu %s)", stats->file_count,
217 display_size, rb->str(lang_size_unit));
218 prn(buf, 0);
219 rb->snprintf(buf, sizeof(buf), "Audio: %d", stats->audio_file_count);
220 prn(buf, 1);
221 if (stats->count_all)
222 {
223 rb->snprintf(buf, sizeof(buf), "Playlists: %d", stats->m3u_file_count);
224 prn(buf, 2);
225 rb->snprintf(buf, sizeof(buf), "Images: %d", stats->img_file_count);
226 prn(buf, 3);
227 rb->snprintf(buf, sizeof(buf), "Videos: %d", stats->vid_file_count);
228 prn(buf, 4);
229 rb->snprintf(buf, sizeof(buf), "Directories: %d", stats->dir_count);
230 prn(buf, 5);
231 rb->snprintf(buf, sizeof(buf), "Max files in Dir: %d",
232 stats->max_files_in_dir);
233 prn(buf, 6);
234 }
235 else
236 {
237 rb->snprintf(buf, sizeof(buf), "Directories: %d", stats->dir_count);
238 prn(buf, 2);
239 }
240 rb->lcd_update();
241#ifdef HAVE_REMOTE_LCD
242 rb->lcd_remote_update();
243#endif
244
245}
246
247/* Recursively scans directories in search of files
248 * and informs the user of the progress.
249 */
250bool collect_dir_stats(struct dir_stats *stats, bool (*id3_cb)(const char*))
251{
252 bool result = true;
253 unsigned int files_in_dir = 0;
254 static unsigned int id3_count;
255 static unsigned long last_displayed, last_get_action;
256 struct dirent* entry;
257 int dirlen = rb->strlen(stats->dirname);
258 DIR* dir = rb->opendir(stats->dirname);
259 if (!dir)
260 {
261 rb->splashf(HZ*2, "open error: %s", stats->dirname);
262 return false;
263 }
264 else if (!stats->dirname[1]) /* root dir */
265 stats->dirname[0] = dirlen = 0;
266
267 /* walk through the directory content */
268 while(result && (0 != (entry = rb->readdir(dir))))
269 {
270 struct dirinfo info = rb->dir_get_info(dir, entry);
271 if (info.attribute & ATTR_DIRECTORY)
272 {
273 if (!rb->strcmp((char *)entry->d_name, ".") ||
274 !rb->strcmp((char *)entry->d_name, ".."))
275 continue; /* skip these */
276
277 rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen,
278 "/%s", entry->d_name); /* append name to current directory */
279 if (!id3_cb)
280 {
281 stats->dir_count++; /* new directory */
282 if (*rb->current_tick - last_displayed > (HZ/2))
283 {
284 if (last_displayed)
285 display_dir_stats(stats);
286 last_displayed = *(rb->current_tick);
287 }
288 }
289 result = collect_dir_stats(stats, id3_cb); /* recursion */
290 }
291 else if (!id3_cb)
292 {
293 char *ptr;
294 stats->file_count++; /* new file */
295 files_in_dir++;
296 stats->byte_count += info.size;
297
298 int attr = rb->filetype_get_attr(entry->d_name);
299 if (attr == FILE_ATTR_AUDIO)
300 stats->audio_file_count++;
301 else if (attr == FILE_ATTR_M3U)
302 stats->m3u_file_count++;
303 /* image or video file attributes have to be compared manually */
304 else if (stats->count_all &&
305 (ptr = rb->strrchr(entry->d_name,'.')))
306 {
307 unsigned int i;
308 ptr++;
309 for(i = 0; i < ARRAYLEN(image_exts); i++)
310 {
311 if(!rb->strcasecmp(ptr, image_exts[i]))
312 {
313 stats->img_file_count++;
314 break;
315 }
316 }
317 if (i >= ARRAYLEN(image_exts)) {
318 for(i = 0; i < ARRAYLEN(video_exts); i++) {
319 if(!rb->strcasecmp(ptr, video_exts[i])) {
320 stats->vid_file_count++;
321 break;
322 }
323 }
324 }
325 }
326 }
327 else if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_AUDIO)
328 {
329 rb->splash_progress(id3_count++, stats->audio_file_count,
330 "%s (%s)",
331 rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
332 rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen,
333 "/%s", entry->d_name); /* append name to current directory */
334 id3_cb(stats->dirname); /* allow metadata to be collected */
335 }
336
337 if (TIME_AFTER(*(rb->current_tick), last_get_action + HZ/8))
338 {
339 if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
340 {
341 stats->canceled = true;
342 result = false;
343 }
344 last_get_action = *(rb->current_tick);
345 }
346 rb->yield();
347 }
348 rb->closedir(dir);
349 if (stats->max_files_in_dir < files_in_dir)
350 stats->max_files_in_dir = files_in_dir;
351 return result;
352}
diff --git a/apps/plugins/lib/mul_id3.h b/apps/plugins/lib/mul_id3.h
index d08095de5c..1bb311c441 100644
--- a/apps/plugins/lib/mul_id3.h
+++ b/apps/plugins/lib/mul_id3.h
@@ -21,7 +21,39 @@
21#ifndef MUL_ID3_H 21#ifndef MUL_ID3_H
22#define MUL_ID3_H 22#define MUL_ID3_H
23 23
24struct dir_stats {
25 char dirname[MAX_PATH];
26 unsigned int dir_count;
27 unsigned int file_count;
28 unsigned int audio_file_count;
29 unsigned int m3u_file_count;
30 unsigned int img_file_count;
31 unsigned int vid_file_count;
32 unsigned int max_files_in_dir;
33 unsigned long long byte_count;
34 bool count_all;
35 bool canceled;
36};
37
38/* create mp3entry that contains matching metadata from multiple tracks */
24void collect_id3(struct mp3entry *id3, bool is_first_track); 39void collect_id3(struct mp3entry *id3, bool is_first_track);
25void finalize_id3(struct mp3entry *id3); 40void finalize_id3(struct mp3entry *id3);
26 41
42/* Traverse directory, collecting stats/track metadata.
43 *
44 * 1) If id3_cb is null, dir_properties calculates all dir stats, including the
45 * audio file count.
46 *
47 * 2) If id3_cb points to a function, dir_properties will call it for every audio
48 * file encountered, to allow the file's metadata to be collected. The displayed
49 * progress bar's maximum value is set to the audio file count.
50 * Stats are assumed to have already been generated by a preceding run.
51 *
52 * If the count_all parameter is set to false, images and videos are not counted,
53 * nor is the playlist, image, video or max file in dir count displayed.
54 */
55bool collect_dir_stats(struct dir_stats *stats, bool (*id3_cb)(const char*));
56void display_dir_stats(struct dir_stats *stats);
57unsigned long human_size(unsigned long long byte_count, int32_t *unit_lang_id);
58
27#endif /* MUL_ID3_H */ 59#endif /* MUL_ID3_H */
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index 1156bd5ca9..c932474f8c 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -25,15 +25,6 @@
25 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) 25 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
26#endif 26#endif
27 27
28struct dir_stats {
29 char dirname[MAX_PATH];
30 unsigned int dir_count;
31 unsigned int file_count;
32 unsigned int audio_file_count;
33 unsigned long long byte_count;
34 bool canceled;
35};
36
37enum props_types { 28enum props_types {
38 PROPS_FILE = 0, 29 PROPS_FILE = 0,
39 PROPS_PLAYLIST, 30 PROPS_PLAYLIST,
@@ -42,7 +33,7 @@ enum props_types {
42 PROPS_DIR 33 PROPS_DIR
43}; 34};
44 35
45static int props_type = PROPS_FILE; 36static int props_type;
46 37
47static struct mp3entry id3; 38static struct mp3entry id3;
48static int mul_id3_count; 39static int mul_id3_count;
@@ -57,8 +48,8 @@ static char str_audio_filecount[64];
57static char str_date[64]; 48static char str_date[64];
58static char str_time[64]; 49static char str_time[64];
59 50
60static unsigned long nsize; 51static unsigned long display_size;
61static int32_t size_unit; 52static int32_t lang_size_unit;
62static struct tm tm; 53static struct tm tm;
63 54
64#define NUM_FILE_PROPERTIES 5 55#define NUM_FILE_PROPERTIES 5
@@ -86,26 +77,6 @@ static const unsigned char* const props_dir[] =
86 ID2P(LANG_MENU_SHOW_ID3_INFO), str_audio_filecount, 77 ID2P(LANG_MENU_SHOW_ID3_INFO), str_audio_filecount,
87}; 78};
88 79
89static const int32_t units[] =
90{
91 LANG_BYTE,
92 LANG_KIBIBYTE,
93 LANG_MEBIBYTE,
94 LANG_GIBIBYTE
95};
96
97static unsigned int human_size_log(unsigned long long size)
98{
99 const size_t n = sizeof(units)/sizeof(units[0]);
100
101 unsigned int i;
102 /* margin set at 10K boundary: 10239 B +1 => 10 KB */
103 for(i=0; i < n-1 && size >= 10*1024; i++)
104 size >>= 10; /* div by 1024 */
105
106 return i;
107}
108
109static bool file_properties(const char* selected_file) 80static bool file_properties(const char* selected_file)
110{ 81{
111 bool found = false; 82 bool found = false;
@@ -118,12 +89,9 @@ static bool file_properties(const char* selected_file)
118 struct dirinfo info = rb->dir_get_info(dir, entry); 89 struct dirinfo info = rb->dir_get_info(dir, entry);
119 if(!rb->strcmp(entry->d_name, str_filename)) 90 if(!rb->strcmp(entry->d_name, str_filename))
120 { 91 {
121 unsigned int log; 92 display_size = human_size(info.size, &lang_size_unit);
122 log = human_size_log((unsigned long)info.size);
123 nsize = ((unsigned long)info.size) >> (log*10);
124 size_unit = units[log];
125 rb->snprintf(str_size, sizeof str_size, "%lu %s", 93 rb->snprintf(str_size, sizeof str_size, "%lu %s",
126 nsize, rb->str(size_unit)); 94 display_size, rb->str(lang_size_unit));
127 rb->gmtime_r(&info.mtime, &tm); 95 rb->gmtime_r(&info.mtime, &tm);
128 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d", 96 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
129 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); 97 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
@@ -141,99 +109,10 @@ static bool file_properties(const char* selected_file)
141 return found; 109 return found;
142} 110}
143 111
144/* Recursively scans directories in search of files
145 * and informs the user of the progress.
146 */
147static bool _dir_properties(struct dir_stats *stats, bool (*id3_cb)(const char*))
148{
149 bool result = true;
150 static unsigned long last_lcd_update, last_get_action;
151 struct dirent* entry;
152 int dirlen = rb->strlen(stats->dirname);
153 DIR* dir = rb->opendir(stats->dirname);
154 if (!dir)
155 {
156 rb->splashf(HZ*2, "open error: %s", stats->dirname);
157 return false;
158 }
159
160 /* walk through the directory content */
161 while(result && (0 != (entry = rb->readdir(dir))))
162 {
163 struct dirinfo info = rb->dir_get_info(dir, entry);
164 if (info.attribute & ATTR_DIRECTORY)
165 {
166 if (!rb->strcmp((char *)entry->d_name, ".") ||
167 !rb->strcmp((char *)entry->d_name, ".."))
168 continue; /* skip these */
169
170 rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen,
171 "/%s", entry->d_name); /* append name to current directory */
172
173 if (!id3_cb)
174 {
175 stats->dir_count++; /* new directory */
176 if (*rb->current_tick - last_lcd_update > (HZ/2))
177 {
178 unsigned int log;
179 last_lcd_update = *(rb->current_tick);
180 rb->lcd_clear_display();
181 rb->lcd_putsf(0, 0, "Directories: %d", stats->dir_count);
182 rb->lcd_putsf(0, 1, "Files: %d (Audio: %d)",
183 stats->file_count, stats->audio_file_count);
184 log = human_size_log(stats->byte_count);
185 rb->lcd_putsf(0, 2, "Size: %lu %s",
186 (unsigned long)(stats->byte_count >> (10*log)),
187 rb->str(units[log]));
188 rb->lcd_update();
189 }
190 }
191
192 result = _dir_properties(stats, id3_cb); /* recursion */
193 }
194 else if (!id3_cb)
195 {
196 stats->file_count++; /* new file */
197 stats->byte_count += info.size;
198 if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_AUDIO)
199 stats->audio_file_count++;
200 }
201 else if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_AUDIO)
202 {
203 rb->splash_progress(mul_id3_count, stats->audio_file_count, "%s (%s)",
204 rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
205 rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen,
206 "/%s", entry->d_name); /* append name to current directory */
207 id3_cb(stats->dirname); /* allow metadata to be collected */
208 }
209 112
210 if (TIME_AFTER(*(rb->current_tick), last_get_action + HZ/8))
211 {
212 if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
213 {
214 stats->canceled = true;
215 result = false;
216 }
217 last_get_action = *(rb->current_tick);
218 }
219 rb->yield();
220 }
221 rb->closedir(dir);
222 return result;
223}
224
225/* 1) If id3_cb is null, dir_properties calculates all dir stats, including the
226 * audio file count.
227 *
228 * 2) If id3_cb points to a function, dir_properties will call it for every audio
229 * file encountered, to allow the file's metadata to be collected. The displayed
230 * progress bar's maximum value is set to the audio file count.
231 * Stats are assumed to have already been generated by a preceding run.
232 */
233static bool dir_properties(const char* selected_file, struct dir_stats *stats, 113static bool dir_properties(const char* selected_file, struct dir_stats *stats,
234 bool (*id3_cb)(const char*)) 114 bool (*id3_cb)(const char*))
235{ 115{
236 unsigned int log;
237 bool success; 116 bool success;
238 117
239 rb->strlcpy(stats->dirname, selected_file, sizeof(stats->dirname)); 118 rb->strlcpy(stats->dirname, selected_file, sizeof(stats->dirname));
@@ -243,8 +122,8 @@ static bool dir_properties(const char* selected_file, struct dir_stats *stats,
243#ifdef HAVE_ADJUSTABLE_CPU_FREQ 122#ifdef HAVE_ADJUSTABLE_CPU_FREQ
244 rb->cpu_boost(true); 123 rb->cpu_boost(true);
245#endif 124#endif
246 success = _dir_properties(stats, id3_cb); 125 success = collect_dir_stats(stats, id3_cb);
247 126
248#ifdef HAVE_ADJUSTABLE_CPU_FREQ 127#ifdef HAVE_ADJUSTABLE_CPU_FREQ
249 rb->cpu_boost(false); 128 rb->cpu_boost(false);
250#endif 129#endif
@@ -259,10 +138,9 @@ static bool dir_properties(const char* selected_file, struct dir_stats *stats,
259 rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count); 138 rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count);
260 rb->snprintf(str_audio_filecount, sizeof str_filecount, "%d", 139 rb->snprintf(str_audio_filecount, sizeof str_filecount, "%d",
261 stats->audio_file_count); 140 stats->audio_file_count);
262 log = human_size_log(stats->byte_count); 141 display_size = human_size(stats->byte_count, &lang_size_unit);
263 nsize = (long) (stats->byte_count >> (log*10)); 142 rb->snprintf(str_size, sizeof str_size, "%lu %s", display_size,
264 size_unit = units[log]; 143 rb->str(lang_size_unit));
265 rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit));
266 } 144 }
267 return true; 145 return true;
268} 146}
@@ -320,8 +198,8 @@ static int speak_property_selection(int selected_item, void *data)
320 rb->talk_file_or_spell(str_dirname, str_filename, NULL, true); 198 rb->talk_file_or_spell(str_dirname, str_filename, NULL, true);
321 break; 199 break;
322 case LANG_PROPERTIES_SIZE: 200 case LANG_PROPERTIES_SIZE:
323 rb->talk_number(nsize, true); 201 rb->talk_number(display_size, true);
324 rb->talk_id(size_unit, true); 202 rb->talk_id(lang_size_unit, true);
325 break; 203 break;
326 case LANG_PROPERTIES_DATE: 204 case LANG_PROPERTIES_DATE:
327 rb->talk_date(&tm, true); 205 rb->talk_date(&tm, true);
@@ -399,10 +277,8 @@ static int browse_file_or_dir(struct dir_stats *stats)
399 277
400static bool determine_file_or_dir(void) 278static bool determine_file_or_dir(void)
401{ 279{
402 DIR* dir;
403 struct dirent* entry; 280 struct dirent* entry;
404 281 DIR* dir = rb->opendir(str_dirname);
405 dir = rb->opendir(str_dirname);
406 if (dir) 282 if (dir)
407 { 283 {
408 while(0 != (entry = rb->readdir(dir))) 284 while(0 != (entry = rb->readdir(dir)))
@@ -482,7 +358,6 @@ enum plugin_status plugin_start(const void* parameter)
482{ 358{
483 int ret; 359 int ret;
484 static struct dir_stats stats; 360 static struct dir_stats stats;
485
486 const char *file = parameter; 361 const char *file = parameter;
487 if(!parameter) 362 if(!parameter)
488 return PLUGIN_ERROR; 363 return PLUGIN_ERROR;
@@ -490,12 +365,10 @@ enum plugin_status plugin_start(const void* parameter)
490#ifdef HAVE_TOUCHSCREEN 365#ifdef HAVE_TOUCHSCREEN
491 rb->touchscreen_set_mode(rb->global_settings->touch_mode); 366 rb->touchscreen_set_mode(rb->global_settings->touch_mode);
492#endif 367#endif
493
494 if (file[0] == '/') /* single file or folder selected */ 368 if (file[0] == '/') /* single file or folder selected */
495 { 369 {
496 const char* file_name = rb->strrchr(file, '/') + 1; 370 const char* file_name = rb->strrchr(file, '/') + 1;
497 int dirlen = (file_name - file); 371 const int dirlen = (file_name - file);
498
499 rb->strlcpy(str_dirname, file, dirlen + 1); 372 rb->strlcpy(str_dirname, file, dirlen + 1);
500 rb->snprintf(str_filename, sizeof str_filename, "%s", file + dirlen); 373 rb->snprintf(str_filename, sizeof str_filename, "%s", file + dirlen);
501 374
diff --git a/apps/plugins/stats.c b/apps/plugins/stats.c
index b48259a0e4..8a511bea42 100644
--- a/apps/plugins/stats.c
+++ b/apps/plugins/stats.c
@@ -19,184 +19,27 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "plugin.h" 21#include "plugin.h"
22#include "lib/pluginlib_actions.h" 22#include "lib/mul_id3.h" /* collect_dir_stats & display_dir_stats */
23 23
24
25static int files, dirs, audiofiles, m3ufiles, imagefiles, videofiles, largestdir;
26static int lasttick;
27static bool cancel;
28
29
30/* we use PLA */
31#define STATS_STOP PLA_EXIT
32
33#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
34 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
35 || (CONFIG_KEYPAD == IPOD_4G_PAD)
36#define STATS_STOP2 PLA_UP
37#else
38#define STATS_STOP2 PLA_CANCEL
39#endif
40
41/* this set the context to use with PLA */
42static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
43
44/* we don't have yet a filetype attribute for image files */
45static const char *image_exts[] = {"bmp","jpg","jpe","jpeg","png","ppm"};
46
47/* neither for video ones */
48static const char *video_exts[] = {"mpg","mpeg","mpv","m2v"};
49
50static void prn(const char *str, int y)
51{
52 rb->lcd_puts(0,y,str);
53#ifdef HAVE_REMOTE_LCD
54 rb->lcd_remote_puts(0,y,str);
55#endif
56}
57
58static void update_screen(void)
59{
60 char buf[32];
61
62 rb->lcd_clear_display();
63#ifdef HAVE_REMOTE_LCD
64 rb->lcd_remote_clear_display();
65#endif
66
67 rb->snprintf(buf, sizeof(buf), "Total Files: %d", files);
68 prn(buf,0);
69 rb->snprintf(buf, sizeof(buf), "Audio: %d", audiofiles);
70 prn(buf,1);
71 rb->snprintf(buf, sizeof(buf), "Playlists: %d", m3ufiles);
72 prn(buf,2);
73 rb->snprintf(buf, sizeof(buf), "Images: %d", imagefiles);
74 prn(buf,3);
75 rb->snprintf(buf, sizeof(buf), "Videos: %d", videofiles);
76 prn(buf,4);
77 rb->snprintf(buf, sizeof(buf), "Directories: %d", dirs);
78 prn(buf,5);
79 rb->snprintf(buf, sizeof(buf), "Max files in Dir: %d", largestdir);
80 prn(buf,6);
81
82 rb->lcd_update();
83#ifdef HAVE_REMOTE_LCD
84 rb->lcd_remote_update();
85#endif
86}
87
88static void traversedir(char* location, char* name)
89{
90 int button;
91 struct dirent *entry;
92 DIR* dir;
93 char fullpath[MAX_PATH];
94 int files_in_dir = 0;
95
96 rb->snprintf(fullpath, sizeof(fullpath), "%s/%s", location, name);
97 dir = rb->opendir(fullpath);
98 if (dir) {
99 entry = rb->readdir(dir);
100 while (entry) {
101 if (cancel)
102 break;
103 /* Skip .. and . */
104 if (rb->strcmp(entry->d_name, ".") && rb->strcmp(entry->d_name, ".."))
105 {
106 struct dirinfo info = rb->dir_get_info(dir, entry);
107 if (info.attribute & ATTR_DIRECTORY) {
108 traversedir(fullpath, entry->d_name);
109 dirs++;
110 }
111 else {
112 files_in_dir++; files++;
113
114 /* get the filetype from the filename */
115 int attr = rb->filetype_get_attr(entry->d_name);
116 switch (attr & FILE_ATTR_MASK)
117 {
118 case FILE_ATTR_AUDIO:
119 audiofiles++;
120 break;
121
122 case FILE_ATTR_M3U:
123 m3ufiles++;
124 break;
125
126 default:
127 {
128 /* use hardcoded filetype_exts to count
129 * image and video files until we get
130 * new attributes added to filetypes.h */
131 char *ptr = rb->strrchr(entry->d_name,'.');
132 if(ptr) {
133 unsigned i;
134 ptr++;
135 for(i=0;i<ARRAYLEN(image_exts);i++) {
136 if(!rb->strcasecmp(ptr,image_exts[i])) {
137 imagefiles++; break;
138 }
139 }
140
141 if (i >= ARRAYLEN(image_exts)) {
142 /* not found above - try video files */
143 for(i=0;i<ARRAYLEN(video_exts);i++) {
144 if(!rb->strcasecmp(ptr,video_exts[i])) {
145 videofiles++; break;
146 }
147 }
148 }
149 }
150 } /* default: */
151 } /* switch */
152 }
153 }
154
155 if (*rb->current_tick - lasttick > (HZ/2)) {
156 update_screen();
157 lasttick = *rb->current_tick;
158 button = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts,
159 ARRAYLEN(plugin_contexts));
160 if (button == STATS_STOP || button == STATS_STOP2) {
161 cancel = true;
162 break;
163 }
164 }
165
166 entry = rb->readdir(dir);
167 }
168 rb->closedir(dir);
169 }
170 if (largestdir < files_in_dir)
171 largestdir = files_in_dir;
172}
173
174/* this is the plugin entry point */
175enum plugin_status plugin_start(const void* parameter) 24enum plugin_status plugin_start(const void* parameter)
176{ 25{
177 int button;
178
179 (void)parameter; 26 (void)parameter;
180 27 int button, success;
181 files = 0; 28 static struct dir_stats stats;
182 dirs = 0; 29 stats.dirname[0] = '/';
183 audiofiles = 0; 30 stats.count_all = true;
184 m3ufiles = 0; 31#ifdef HAVE_ADJUSTABLE_CPU_FREQ
185 imagefiles = 0; 32 rb->cpu_boost(true);
186 videofiles = 0; 33#endif
187 largestdir = 0; 34 display_dir_stats(&stats);
188 cancel = false; 35 success = collect_dir_stats(&stats, NULL);
189 36#ifdef HAVE_ADJUSTABLE_CPU_FREQ
190 rb->splash(HZ, "Counting..."); 37 rb->cpu_boost(false);
191 update_screen(); 38#endif
192 lasttick = *rb->current_tick; 39 if (!success)
193
194 traversedir("", "");
195 if (cancel) {
196 rb->splash(HZ, "Aborted");
197 return PLUGIN_OK; 40 return PLUGIN_OK;
198 } 41
199 update_screen(); 42 display_dir_stats(&stats);
200#ifdef HAVE_BACKLIGHT 43#ifdef HAVE_BACKLIGHT
201#ifdef HAVE_REMOTE_LCD 44#ifdef HAVE_REMOTE_LCD
202 rb->remote_backlight_on(); 45 rb->remote_backlight_on();
@@ -204,21 +47,15 @@ enum plugin_status plugin_start(const void* parameter)
204 rb->backlight_on(); 47 rb->backlight_on();
205#endif 48#endif
206 rb->splash(HZ, "Done"); 49 rb->splash(HZ, "Done");
207 update_screen(); 50 display_dir_stats(&stats);
208 while (1) { 51 while (1) {
209 52 switch (button = rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK))
210 button = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, 53 {
211 ARRAYLEN(plugin_contexts)); 54 case ACTION_STD_CANCEL:
212 switch (button) {
213 case STATS_STOP:
214 case STATS_STOP2:
215 return PLUGIN_OK; 55 return PLUGIN_OK;
216 break;
217 default: 56 default:
218 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { 57 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
219 return PLUGIN_USB_CONNECTED; 58 return PLUGIN_USB_CONNECTED;
220 }
221 break;
222 } 59 }
223 } 60 }
224 return PLUGIN_OK; 61 return PLUGIN_OK;
diff --git a/manual/plugins/stats.tex b/manual/plugins/stats.tex
index eddad8e652..f47451fcff 100644
--- a/manual/plugins/stats.tex
+++ b/manual/plugins/stats.tex
@@ -5,9 +5,4 @@ The stats plugin counts the directories and files
5(the total number as well as the number 5(the total number as well as the number
6of audio, playlist, image and video files) 6of audio, playlist, image and video files)
7on your \dap{}. 7on your \dap{}.
8Press 8Press \ActionStdCancel{} to exit the plugin.
9\nopt{IPOD_4G_PAD,IPOD_3G_PAD}{\PluginCancel{} or \PluginExit{}}
10\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu}
11to abort counting and
12exit the plugin. Press it again to quit after counting has
13finished.