diff options
Diffstat (limited to 'apps/plugins/lib')
-rw-r--r-- | apps/plugins/lib/arg_helper.c | 25 | ||||
-rw-r--r-- | apps/plugins/lib/arg_helper.h | 4 | ||||
-rw-r--r-- | apps/plugins/lib/id3.c | 2 | ||||
-rw-r--r-- | apps/plugins/lib/mul_id3.c | 175 | ||||
-rw-r--r-- | apps/plugins/lib/mul_id3.h | 32 |
5 files changed, 224 insertions, 14 deletions
diff --git a/apps/plugins/lib/arg_helper.c b/apps/plugins/lib/arg_helper.c index 3ea5ba714d..63b7acbb2b 100644 --- a/apps/plugins/lib/arg_helper.c +++ b/apps/plugins/lib/arg_helper.c | |||
@@ -31,7 +31,7 @@ | |||
31 | #ifdef PLUGIN | 31 | #ifdef PLUGIN |
32 | #define strchr rb->strchr | 32 | #define strchr rb->strchr |
33 | #endif | 33 | #endif |
34 | int string_parse(const char **parameter, char* buf, size_t buf_sz) | 34 | int string_parse(const char **parameter, char *buf, size_t buf_sz) |
35 | { | 35 | { |
36 | /* fills buf with a string upto buf_sz, null terminates the buffer | 36 | /* fills buf with a string upto buf_sz, null terminates the buffer |
37 | * strings break on WS by default but can be enclosed in single or double quotes | 37 | * strings break on WS by default but can be enclosed in single or double quotes |
@@ -44,6 +44,11 @@ int string_parse(const char **parameter, char* buf, size_t buf_sz) | |||
44 | char stopchars[] = "\'\""; | 44 | char stopchars[] = "\'\""; |
45 | int skipped = 0; | 45 | int skipped = 0; |
46 | int found = 0; | 46 | int found = 0; |
47 | if (!parameter || !*parameter) | ||
48 | { | ||
49 | *buf = '\0'; | ||
50 | return 0; | ||
51 | } | ||
47 | const char* start = *parameter; | 52 | const char* start = *parameter; |
48 | 53 | ||
49 | if (strchr(stopchars, *start)) | 54 | if (strchr(stopchars, *start)) |
@@ -79,7 +84,7 @@ int string_parse(const char **parameter, char* buf, size_t buf_sz) | |||
79 | return found + skipped; | 84 | return found + skipped; |
80 | } | 85 | } |
81 | 86 | ||
82 | int char_parse(const char **parameter, char* character) | 87 | int char_parse(const char **parameter, char *character) |
83 | { | 88 | { |
84 | /* passes *character a single character eats remaining non-WS characters */ | 89 | /* passes *character a single character eats remaining non-WS characters */ |
85 | char buf[2]; | 90 | char buf[2]; |
@@ -95,6 +100,8 @@ int bool_parse(const char **parameter, bool *choice) | |||
95 | /* determine true false using the first character the rest are skipped/ignored */ | 100 | /* determine true false using the first character the rest are skipped/ignored */ |
96 | int found = 0; | 101 | int found = 0; |
97 | const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */ | 102 | const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */ |
103 | if (!parameter || !*parameter) | ||
104 | return 0; | ||
98 | const char* start = *parameter; | 105 | const char* start = *parameter; |
99 | 106 | ||
100 | 107 | ||
@@ -133,7 +140,9 @@ int longnum_parse(const char **parameter, long *number, long *decimal) | |||
133 | int neg = 0; | 140 | int neg = 0; |
134 | int digits = 0; | 141 | int digits = 0; |
135 | //logf ("n: %s\n", *parameter); | 142 | //logf ("n: %s\n", *parameter); |
136 | const char *start = *parameter; | 143 | if (!parameter || !*parameter) |
144 | return 0; | ||
145 | const char* start = *parameter; | ||
137 | 146 | ||
138 | if (*start == '-') | 147 | if (*start == '-') |
139 | { | 148 | { |
@@ -209,7 +218,8 @@ int num_parse(const char **parameter, int *number, int *decimal) | |||
209 | * Note: WS at beginning is stripped, **parameter starts at the first NON WS char | 218 | * Note: WS at beginning is stripped, **parameter starts at the first NON WS char |
210 | * return 0 for arg_callback to quit parsing immediately | 219 | * return 0 for arg_callback to quit parsing immediately |
211 | */ | 220 | */ |
212 | void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char argchar, const char **parameter)) | 221 | void argparse(const char *parameter, int parameter_len, void *userdata, |
222 | int (*arg_callback)(char argchar, const char **parameter, void *userdata)) | ||
213 | { | 223 | { |
214 | bool lastchr; | 224 | bool lastchr; |
215 | char argchar; | 225 | char argchar; |
@@ -222,7 +232,10 @@ void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char | |||
222 | { | 232 | { |
223 | if ((*parameter) == '\0') | 233 | if ((*parameter) == '\0') |
224 | return; | 234 | return; |
225 | logf ("%s\n",parameter); | 235 | |
236 | if (parameter_len < 0) { logf ("%s\n", parameter); } | ||
237 | else { logf ("%.*s\n", plen, parameter); } | ||
238 | |||
226 | argchar = *parameter; | 239 | argchar = *parameter; |
227 | lastchr = (*(parameter + 1) == '\0'); | 240 | lastchr = (*(parameter + 1) == '\0'); |
228 | while (*++parameter || lastchr) | 241 | while (*++parameter || lastchr) |
@@ -230,7 +243,7 @@ void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char | |||
230 | lastchr = false; | 243 | lastchr = false; |
231 | if (isspace(*parameter)) | 244 | if (isspace(*parameter)) |
232 | continue; /* eat spaces at beginning */ | 245 | continue; /* eat spaces at beginning */ |
233 | if (!arg_callback(argchar, ¶meter)) | 246 | if (!arg_callback(argchar, ¶meter, userdata)) |
234 | return; | 247 | return; |
235 | break; | 248 | break; |
236 | } | 249 | } |
diff --git a/apps/plugins/lib/arg_helper.h b/apps/plugins/lib/arg_helper.h index 2cf94ba1dd..638279ee42 100644 --- a/apps/plugins/lib/arg_helper.h +++ b/apps/plugins/lib/arg_helper.h | |||
@@ -54,7 +54,7 @@ int num_parse(const char **parameter, int *number, int *decimal); | |||
54 | * Note: WS at beginning is stripped, **parameter starts at the first NON WS char | 54 | * Note: WS at beginning is stripped, **parameter starts at the first NON WS char |
55 | * return 0 for arg_callback to quit parsing immediately | 55 | * return 0 for arg_callback to quit parsing immediately |
56 | */ | 56 | */ |
57 | void argparse(const char *parameter, int parameter_len, | 57 | void argparse(const char *parameter, int parameter_len, void *userdata, |
58 | int (*arg_callback)(char argchar, const char **parameter)); | 58 | int (*arg_callback)(char argchar, const char **parameter, void *userdata)); |
59 | 59 | ||
60 | #endif /* _LIB_ARG_HELPER_H_ */ | 60 | #endif /* _LIB_ARG_HELPER_H_ */ |
diff --git a/apps/plugins/lib/id3.c b/apps/plugins/lib/id3.c index b0202b1d9c..6af715b1fb 100644 --- a/apps/plugins/lib/id3.c +++ b/apps/plugins/lib/id3.c | |||
@@ -35,5 +35,5 @@ bool retrieve_id3(struct mp3entry *id3, const char* file) | |||
35 | } | 35 | } |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | return !rb->mp3info(id3, file); | 38 | return rb->get_metadata(id3, -1, file); |
39 | } | 39 | } |
diff --git a/apps/plugins/lib/mul_id3.c b/apps/plugins/lib/mul_id3.c index edf44f7282..3ab3a438c8 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 | ||
41 | static struct multiple_tracks_id3 mul_id3; | 41 | static struct multiple_tracks_id3 mul_id3; |
42 | 42 | ||
43 | static 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,163 @@ 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 | |||
181 | unsigned 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 */ | ||
195 | static const char *image_exts[] = {"bmp","jpg","jpe","jpeg","png","ppm"}; | ||
196 | /* and videos */ | ||
197 | static const char *video_exts[] = {"mpg","mpeg","mpv","m2v"}; | ||
198 | |||
199 | static void display_dir_stats_vp(struct dir_stats *stats, struct viewport *vp, | ||
200 | struct screen *display) | ||
201 | { | ||
202 | int32_t lang_size_unit; | ||
203 | unsigned long display_size = human_size(stats->byte_count, &lang_size_unit); | ||
204 | struct viewport *last_vp = display->set_viewport(vp); | ||
205 | display->clear_viewport(); | ||
206 | display->putsf(0, 0, "Files: %d (%lu %s)", stats->file_count, | ||
207 | display_size, rb->str(lang_size_unit)); | ||
208 | display->putsf(0, 1, "Audio: %d", stats->audio_file_count); | ||
209 | if (stats->count_all) | ||
210 | { | ||
211 | display->putsf(0, 2, "Playlists: %d", stats->m3u_file_count); | ||
212 | display->putsf(0, 3, "Images: %d", stats->img_file_count); | ||
213 | display->putsf(0, 4, "Videos: %d", stats->vid_file_count); | ||
214 | display->putsf(0, 5, "Directories: %d", stats->dir_count); | ||
215 | display->putsf(0, 6, "Max files in Dir: %d", stats->max_files_in_dir); | ||
216 | } | ||
217 | else | ||
218 | display->putsf(0, 2, "Directories: %d", stats->dir_count); | ||
219 | |||
220 | display->update_viewport(); | ||
221 | display->set_viewport(last_vp); | ||
222 | } | ||
223 | |||
224 | void display_dir_stats(struct dir_stats *stats) | ||
225 | { | ||
226 | struct viewport vps[NB_SCREENS]; | ||
227 | FOR_NB_SCREENS(i) | ||
228 | { | ||
229 | rb->viewport_set_defaults(&vps[i], i); | ||
230 | display_dir_stats_vp(stats, &vps[i], rb->screens[i]); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | /* Recursively scans directories in search of files | ||
235 | * and informs the user of the progress. | ||
236 | */ | ||
237 | bool collect_dir_stats(struct dir_stats *stats, bool (*id3_cb)(const char*)) | ||
238 | { | ||
239 | bool result = true; | ||
240 | unsigned int files_in_dir = 0; | ||
241 | static unsigned int id3_count; | ||
242 | static unsigned long last_displayed, last_get_action; | ||
243 | struct dirent* entry; | ||
244 | int dirlen = rb->strlen(stats->dirname); | ||
245 | DIR* dir = rb->opendir(stats->dirname); | ||
246 | if (!dir) | ||
247 | { | ||
248 | rb->splashf(HZ*2, "open error: %s", stats->dirname); | ||
249 | return false; | ||
250 | } | ||
251 | else if (!stats->dirname[1]) /* root dir */ | ||
252 | stats->dirname[0] = dirlen = 0; | ||
253 | |||
254 | /* walk through the directory content */ | ||
255 | while(result && (0 != (entry = rb->readdir(dir)))) | ||
256 | { | ||
257 | struct dirinfo info = rb->dir_get_info(dir, entry); | ||
258 | if (info.attribute & ATTR_DIRECTORY) | ||
259 | { | ||
260 | if (!rb->strcmp((char *)entry->d_name, ".") || | ||
261 | !rb->strcmp((char *)entry->d_name, "..")) | ||
262 | continue; /* skip these */ | ||
263 | |||
264 | rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen, | ||
265 | "/%s", entry->d_name); /* append name to current directory */ | ||
266 | if (!id3_cb) | ||
267 | { | ||
268 | stats->dir_count++; /* new directory */ | ||
269 | if (*rb->current_tick - last_displayed > (HZ/2)) | ||
270 | { | ||
271 | if (last_displayed) | ||
272 | display_dir_stats(stats); | ||
273 | last_displayed = *(rb->current_tick); | ||
274 | } | ||
275 | } | ||
276 | result = collect_dir_stats(stats, id3_cb); /* recursion */ | ||
277 | } | ||
278 | else if (!id3_cb) | ||
279 | { | ||
280 | char *ptr; | ||
281 | stats->file_count++; /* new file */ | ||
282 | files_in_dir++; | ||
283 | stats->byte_count += info.size; | ||
284 | |||
285 | int attr = rb->filetype_get_attr(entry->d_name); | ||
286 | if (attr == FILE_ATTR_AUDIO) | ||
287 | stats->audio_file_count++; | ||
288 | else if (attr == FILE_ATTR_M3U) | ||
289 | stats->m3u_file_count++; | ||
290 | /* image or video file attributes have to be compared manually */ | ||
291 | else if (stats->count_all && | ||
292 | (ptr = rb->strrchr(entry->d_name,'.'))) | ||
293 | { | ||
294 | unsigned int i; | ||
295 | ptr++; | ||
296 | for(i = 0; i < ARRAYLEN(image_exts); i++) | ||
297 | { | ||
298 | if(!rb->strcasecmp(ptr, image_exts[i])) | ||
299 | { | ||
300 | stats->img_file_count++; | ||
301 | break; | ||
302 | } | ||
303 | } | ||
304 | if (i >= ARRAYLEN(image_exts)) { | ||
305 | for(i = 0; i < ARRAYLEN(video_exts); i++) { | ||
306 | if(!rb->strcasecmp(ptr, video_exts[i])) { | ||
307 | stats->vid_file_count++; | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | else if (rb->filetype_get_attr(entry->d_name) == FILE_ATTR_AUDIO) | ||
315 | { | ||
316 | rb->splash_progress(id3_count++, stats->audio_file_count, | ||
317 | "%s (%s)", | ||
318 | rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT)); | ||
319 | rb->snprintf(stats->dirname + dirlen, sizeof(stats->dirname) - dirlen, | ||
320 | "/%s", entry->d_name); /* append name to current directory */ | ||
321 | id3_cb(stats->dirname); /* allow metadata to be collected */ | ||
322 | } | ||
323 | |||
324 | if (TIME_AFTER(*(rb->current_tick), last_get_action + HZ/8)) | ||
325 | { | ||
326 | if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) | ||
327 | { | ||
328 | stats->canceled = true; | ||
329 | result = false; | ||
330 | } | ||
331 | last_get_action = *(rb->current_tick); | ||
332 | } | ||
333 | rb->yield(); | ||
334 | } | ||
335 | rb->closedir(dir); | ||
336 | if (stats->max_files_in_dir < files_in_dir) | ||
337 | stats->max_files_in_dir = files_in_dir; | ||
338 | return result; | ||
339 | } | ||
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 | ||
24 | struct 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 */ | ||
24 | void collect_id3(struct mp3entry *id3, bool is_first_track); | 39 | void collect_id3(struct mp3entry *id3, bool is_first_track); |
25 | void finalize_id3(struct mp3entry *id3); | 40 | void 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 | */ | ||
55 | bool collect_dir_stats(struct dir_stats *stats, bool (*id3_cb)(const char*)); | ||
56 | void display_dir_stats(struct dir_stats *stats); | ||
57 | unsigned long human_size(unsigned long long byte_count, int32_t *unit_lang_id); | ||
58 | |||
27 | #endif /* MUL_ID3_H */ | 59 | #endif /* MUL_ID3_H */ |