A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 295 lines 9.5 kB view raw
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 23FPP 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 54static 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 64static 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%lx\n", (unsigned long)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 185static 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 231static 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 263enum 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}