A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Feature] File Picker Plugin

allows viewers to pop a file browser to let the user pick a relevant file
which then gets run by the viewer

(only lua so far)

Change-Id: I7e6b4c2827fab5e9f596d336f546100636c4b871

authored by

William Wilgus and committed by
William Wilgus
e3097bf9 ef9490f6

+328
+14
apps/lang/english.lang
··· 16486 16486 *: none 16487 16487 </voice> 16488 16488 </phrase> 16489 + <phrase> 16490 + id: LANG_CHOOSE_FILE 16491 + desc: file_picker plugin ask user to select a file 16492 + user: core 16493 + <source> 16494 + *: "Choose File" 16495 + </source> 16496 + <dest> 16497 + *: "Choose File" 16498 + </dest> 16499 + <voice> 16500 + *: "Choose File" 16501 + </voice> 16502 + </phrase>
+1
apps/plugins/CATEGORIES
··· 35 35 duke3d,games 36 36 euroconverter,apps 37 37 fft,demos 38 + file_picker,viewers 38 39 fire,demos 39 40 fireworks,demos 40 41 firmware_flash,apps
+1
apps/plugins/SOURCES
··· 12 12 cue_playlist.c 13 13 dart_scorer.c 14 14 dict.c 15 + file_picker.c 15 16 jackpot.c 16 17 keybox.c 17 18 keyremap.c
+295
apps/plugins/file_picker.c
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2024 William Wilgus 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + /*File Picker Plugin 22 + 23 + FPP accepts several arguments to help in your file picking adventure 24 + * NOTE: anything with spaces should be quoted, -f and -l can't be used together * 25 + -r "<GOTO_PLUGIN_NAME>" doesn't need to be a full path demos/file.rock works 26 + the file (if choosen, cancel exits) will be passed as a parameter 27 + -t "<TITLE ( max 64 chars )>" 28 + -s "<START DIR>" 29 + -f "<.EXT (max 64 chars) >" the extension of the files you are looking for 30 + must have the '.' and ".*" accepts any file 31 + -l "<.EXT.EXT,.EXT .EXT (max 64 chars) >" list of extensions for files you are looking for 32 + each must have the '.' spaces and commas are ignored 33 + -a attrib flags eg FILE_ATTR_AUDIO 34 + -d disallow changing directories (hide directories doesn't allow changing from start dir) 35 + */ 36 + #include "plugin.h" 37 + #include "lang_enum.h" 38 + #include "lib/arg_helper.h" 39 + 40 + #if defined(DEBUG) || defined(SIMULATOR) 41 + #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n") 42 + #elif defined(ROCKBOX_HAS_LOGF) 43 + #define logf rb->logf 44 + #else 45 + #define logf(...) do { } while(0) 46 + #endif 47 + 48 + #define FIND_NODIRS 0x01 49 + #define FIND_ATTRIB 0x02 50 + #define FIND_WILDCARD 0x04 51 + #define FIND_EXT 0x08 52 + #define FIND_EXT_IN_LIST 0x10 53 + 54 + static struct fpp 55 + { 56 + char return_plugin[MAX_PATH]; 57 + char start_dir[MAX_PATH]; 58 + char file_ext[64]; 59 + char title[64]; 60 + int tree_attr; 61 + int flags; 62 + }fpp; 63 + 64 + static int arg_callback(char argchar, const char **parameter, void *userdata) 65 + { 66 + struct fpp *pfp = userdata; 67 + int ret; 68 + long num; 69 + const char* start = *parameter; 70 + while (*parameter[0] > '/' && ispunct(*parameter[0])) (*parameter)++; 71 + switch (tolower(argchar)) 72 + { 73 + case 'd' : 74 + pfp->flags |= FIND_NODIRS; 75 + logf ("Find no dirs"); 76 + break; 77 + case 'r' : /*return_plugin*/ 78 + logf ("trying PLUGIN_DIR..."); 79 + size_t l = rb->strlcpy(pfp->return_plugin, 80 + PLUGIN_DIR, 81 + sizeof(pfp->return_plugin)); 82 + 83 + ret = string_parse(parameter, 84 + pfp->return_plugin + l, 85 + sizeof(pfp->return_plugin) - l); 86 + 87 + if (ret && !rb->file_exists(pfp->return_plugin)) 88 + { 89 + logf("Failed"); 90 + *parameter = start; 91 + string_parse(parameter, pfp->return_plugin, 92 + sizeof(pfp->return_plugin)); 93 + } 94 + 95 + if (ret) 96 + { 97 + logf ("Ret plugin: Val: %s\n", pfp->return_plugin); 98 + logf("ate %d chars\n", ret); 99 + } 100 + break; 101 + case 't' : /* title */ 102 + ret = string_parse(parameter, pfp->title, sizeof(pfp->title)); 103 + if (ret) 104 + { 105 + logf ("Title: Val: %s\n", pfp->title); 106 + logf("ate %d chars\n", ret); 107 + } 108 + break; 109 + case 's' : /* start directory */ 110 + ret = string_parse(parameter, pfp->start_dir, sizeof(pfp->start_dir)); 111 + if (ret) 112 + { 113 + if (!rb->dir_exists(pfp->start_dir)) 114 + { 115 + rb->strlcpy(pfp->start_dir, PATH_ROOTSTR, sizeof(pfp->start_dir)); 116 + } 117 + 118 + logf ("Start dir: Val: %s\n", pfp->start_dir); 119 + logf("ate %d chars\n", ret); 120 + } 121 + break; 122 + case 'f' : /* file extension */ 123 + if (pfp->flags & FIND_EXT_IN_LIST) 124 + { 125 + rb->splash(HZ*5, "list extensions already active -f ignored"); 126 + break; 127 + } 128 + ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext)); 129 + if (ret) 130 + { 131 + if (pfp->file_ext[1] == '*') 132 + pfp->flags |= FIND_WILDCARD; 133 + else 134 + pfp->flags |= FIND_EXT; 135 + logf ("Extension: Val: %s\n", pfp->file_ext); 136 + logf("ate %d chars\n", ret); 137 + } 138 + break; 139 + case 'l' : /* file extension list */ 140 + if (pfp->flags & FIND_EXT) 141 + { 142 + rb->splash(HZ*5, "extension already active -l ignored"); 143 + break; 144 + } 145 + ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext)); 146 + if (ret) 147 + { 148 + char *wr = pfp->file_ext; 149 + char *rd = pfp->file_ext; 150 + while (*rd != '\0') /* copy the extensions */ 151 + { 152 + if (*rd == ' ' || *rd == ',' || *rd == ';') 153 + { 154 + /* ignore spaces, commas, and semicolons */ 155 + rd++; 156 + continue; 157 + } 158 + *wr++ = *rd++; 159 + } 160 + *wr = '\0'; 161 + pfp->flags |= FIND_EXT_IN_LIST; 162 + logf ("Extension List: Val: %s\n", pfp->file_ext); 163 + logf("ate %d chars\n", ret); 164 + } 165 + break; 166 + case 'a' : /* tree attribute */ 167 + ret = longnum_parse(parameter, &num, NULL); 168 + if (ret) 169 + { 170 + pfp->tree_attr = (num)&FILE_ATTR_MASK; 171 + pfp->flags |= FIND_ATTRIB; 172 + logf ("Attrib: Val: 0x%x\n", (uint32_t)num); 173 + logf("ate %d chars\n", ret); 174 + } 175 + break; 176 + default : 177 + rb->splashf(HZ, "Unknown switch '%c'",argchar); 178 + logf("Unknown switch '%c'",argchar); 179 + //return 0; 180 + } 181 + 182 + return 1; 183 + } 184 + 185 + static bool cb_show_item(char *name, int attr, struct tree_context *tc) 186 + { 187 + static int dirlevel = -1; 188 + if(attr & ATTR_DIRECTORY) 189 + { 190 + if (fpp.flags & FIND_NODIRS) 191 + { 192 + if (tc->dirlevel == dirlevel) 193 + return false; 194 + dirlevel = tc->dirlevel; 195 + if (rb->strcasestr(tc->currdir, fpp.start_dir) == NULL) 196 + { 197 + tc->is_browsing = false; /* exit immediately */ 198 + logf("exiting %d", tc->dirlevel); 199 + } 200 + } 201 + 202 + return true; 203 + } 204 + if (fpp.flags & FIND_WILDCARD) 205 + { 206 + return true; 207 + } 208 + if ((fpp.flags & FIND_ATTRIB) && (fpp.tree_attr & attr) != 0) 209 + { 210 + return true; 211 + } 212 + if (fpp.flags & FIND_EXT) 213 + { 214 + const char *p = rb->strrchr(name, '.' ); 215 + if (p != NULL && !rb->strcasecmp( p, fpp.file_ext)) 216 + return true; 217 + } 218 + 219 + if (fpp.flags & FIND_EXT_IN_LIST) 220 + { 221 + const char *p = rb->strrchr(name, '.' ); 222 + if (p != NULL && rb->strcasestr(fpp.file_ext, p) != NULL) 223 + return true; 224 + } 225 + 226 + logf("Excluded: %s", name); 227 + return false; 228 + 229 + } 230 + 231 + static int browse_file_dir(struct fpp *pfp) 232 + { 233 + char buf[MAX_PATH]; 234 + struct browse_context browse = { 235 + .dirfilter = SHOW_ALL, 236 + .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER, 237 + .title = pfp->title, 238 + .icon = Icon_Playlist, 239 + .buf = buf, 240 + .bufsize = sizeof(buf), 241 + .root = pfp->start_dir, 242 + .callback_show_item = &cb_show_item, 243 + }; 244 + 245 + if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS) 246 + { 247 + if (rb->file_exists(buf)) 248 + { 249 + logf("Loading %s", buf); 250 + return rb->plugin_open(pfp->return_plugin, buf); 251 + } 252 + else 253 + { 254 + logf("Error opening %s", buf); 255 + rb->splashf(HZ *2, "Error Opening %s", buf); 256 + return PLUGIN_ERROR; 257 + } 258 + } 259 + 260 + return PLUGIN_OK; 261 + } 262 + 263 + enum plugin_status plugin_start(const void* parameter) 264 + { 265 + if (!parameter) 266 + { 267 + rb->splash(HZ *2, "No Args"); 268 + return PLUGIN_ERROR; 269 + } 270 + 271 + argparse((const char*) parameter, -1, &fpp, &arg_callback); 272 + 273 + if (fpp.title[0] == '\0') 274 + { 275 + if (rb->global_settings->talk_menu) 276 + rb->talk_id(LANG_CHOOSE_FILE, true); 277 + 278 + if ((fpp.flags & FIND_EXT) || (fpp.flags & FIND_EXT_IN_LIST)) 279 + { 280 + rb->snprintf(fpp.title, sizeof(fpp.title), 281 + "%s (%s)", rb->str(LANG_CHOOSE_FILE), fpp.file_ext); 282 + if (rb->global_settings->talk_menu) 283 + rb->talk_spell(fpp.file_ext, true); 284 + } 285 + else 286 + { 287 + rb->snprintf(fpp.title, sizeof(fpp.title), "%s", rb->str(LANG_CHOOSE_FILE)); 288 + } 289 + rb->talk_force_enqueue_next(); 290 + } 291 + if (fpp.start_dir[0] == '\0' || !rb->dir_exists(fpp.start_dir)) 292 + rb->strcpy(fpp.start_dir, PATH_ROOTSTR); 293 + 294 + return browse_file_dir(&fpp); 295 + }
+17
apps/plugins/lua/rocklua.c
··· 241 241 rb->splash(10 * HZ, errstr); 242 242 #endif 243 243 } 244 + 245 + int browse_scripts(void) 246 + { 247 + static char buf[MAX_PATH]; 248 + const char *fname = rb->plugin_get_current_filename(); 249 + /* strip plugin dir to save space in the param buffer */ 250 + if (rb->strncmp(fname, PLUGIN_DIR, sizeof(PLUGIN_DIR) - 1) == 0) 251 + fname += sizeof(PLUGIN_DIR) - 1; /* leave slash */ 252 + /* -r return to this plugin, -f looking for lua files, 253 + -s start in lua_scripts, -d lock to that directory */ 254 + snprintf(buf, sizeof(buf), "-r'%s'-f'.lua'-s'%s'-d", 255 + fname, PLUGIN_DEMOS_DIR"/lua_scripts/"); 256 + return rb->plugin_open(VIEWERS_DIR "/file_picker.rock", buf); 257 + } 244 258 /***************** Plugin Entry Point *****************/ 245 259 enum plugin_status plugin_start(const void* parameter) 246 260 { ··· 249 263 if (parameter == NULL) 250 264 { 251 265 if (!Ls) 266 + { 252 267 rb->splash(HZ, "Play a .lua file!"); 268 + return browse_scripts(); 269 + } 253 270 } 254 271 else 255 272 {