A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 241 lines 8.0 kB view raw
1--[[ 2/*************************************************************************** 3 * __________ __ ___. 4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 * \/ \/ \/ \/ \/ 9 * $Id$ 10 * 11 * Copyright (C) 2017 William Wilgus 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 2 16 * of the License, or (at your option) any later version. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ****************************************************************************/ 22]] 23if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end 24require("printtable") 25local _lcd = require("lcd") 26local _timer = require("timer") 27 28-------------------------------------------------------------------------------- 29--[[ returns a sorted tables of directories and (another) of files 30-- path is the starting path; norecurse == true.. only that path will be searched 31-- findfile & finddir are definable search functions 32-- if not defined all files/dirs are returned if false is passed.. none 33-- or you can provide your own function see below.. 34-- f_t and d_t allow you to pass your own tables for re-use but isn't necessary 35]] 36local function get_files(path, norecurse, finddir, findfile, sort_by, f_t, d_t) 37 local quit = false 38 local sort_by_function -- forward declaration 39 local filepath_function -- forward declaration 40 local files = f_t or {} 41 local dirs = d_t or {} 42 43 local function f_filedir(name) 44 --default find function 45 -- example: return name:find(".mp3", 1, true) ~= nil 46 if name:len() <= 2 and (name == "." or name == "..") then 47 return false 48 end 49 return true 50 end 51 local function d_filedir(name) 52 --default discard function 53 return false 54 end 55 56 if finddir == nil then 57 finddir = f_filedir 58 elseif type(finddir) ~= "function" then 59 finddir = d_filedir 60 end 61 62 if findfile == nil then 63 findfile = f_filedir 64 elseif type(findfile) ~= "function" then 65 findfile = d_filedir 66 end 67 68 local function _get_files(path, cancelbtn) 69 local sep = "" 70 local filepath 71 local finfo_t 72 if string.sub(path, - 1) ~= "/" then sep = "/" end 73 for fname, isdir, finfo_t in luadir.dir(path, true) do 74 if isdir and finddir(fname) then 75 table.insert(dirs, path .. sep ..fname) 76 elseif not isdir and findfile(fname) then 77 filepath = filepath_function(path, sep, fname, finfo_t.attribute, finfo_t.size, finfo_t.time) 78 table.insert(files, filepath) 79 end 80 81 if rb.get_plugin_action(0) == cancelbtn then 82 return true 83 end 84 end 85 end 86 87 88 89 local function cmp_alphanum (op1, op2) 90 local type1= type(op1) 91 local type2 = type(op2) 92 93 if type1 ~= type2 then 94 return type1 < type2 95 else 96 if type1 == "string" then 97 op1 = op1:upper() 98 op2 = op2:upper() 99 return sort_by_function(op1, op2) 100 end 101 return op1 < op2 102 end 103 end 104 105 _lcd:splashf(1, "Searching for Files") 106 107 if sort_by == "name" then 108 sort_by_function = function(s1, s2) return s1 < s2 end 109 filepath_function = function(path, sep, fname, fattrib, fsize, ftime) 110 return string.format("%s%s%s;", path, sep, fname) 111 end 112 elseif sort_by == "size" then 113 filepath_function = function(path, sep, fname, fattrib, fsize, ftime) 114 return string.format("%s%s%s; At:%d, Sz:%d, Tm:%d", path, sep, fname, fattrib, fsize, ftime) 115 end 116 sort_by_function = function(s1, s2) 117 local v1, v2 118 v1 = string.match(s1, "SZ:(%d+)") 119 v2 = string.match(s2, "SZ:(%d+)") 120 if v1 or v2 then 121 return tonumber(v1 or 0) < tonumber(v2 or 0) 122 end 123 return s1 < s2 124 end 125 elseif sort_by == "date" then 126 filepath_function = function(path, sep, fname, fattrib, fsize, ftime) 127 return string.format("%s%s%s; At:%d, Sz:%d, Tm:%d", path, sep, fname, fattrib, fsize, ftime) 128 end 129 sort_by_function = function(s1, s2) 130 local v1, v2 131 v1 = string.match(s1, "TM:(%d+)") 132 v2 = string.match(s2, "TM:(%d+)") 133 if v1 or v2 then 134 return tonumber(v1 or 0) < tonumber(v2 or 0) 135 end 136 return s1 < s2 137 end 138 end 139 140 table.insert(dirs, path) -- root 141 142 for key,value in pairs(dirs) do 143 --luadir.dir may error out so we need to do the call protected 144 -- _get_files(value, CANCEL_BUTTON) 145 _, quit = pcall(_get_files, value, CANCEL_BUTTON) 146 147 if quit == true or norecurse then 148 break; 149 end 150 end 151 152 table.sort(files, cmp_alphanum) 153 table.sort(dirs, cmp_alphanum) 154 155 return dirs, files 156end -- get_files 157-------------------------------------------------------------------------------- 158 159-- uses print_table and get_files to display simple file browser 160-- sort_by "date" "name" "size" 161-- descending true/false 162function file_choose(dir, title, sort_by, descending) 163 local dstr, hstr = "" 164 if not title then 165 dstr = "%d items found in %0d.%02d seconds" 166 else 167 hstr = title 168 end 169 170 if not sort_by then sort_by = "name" end 171 sort_by = sort_by:lower() 172 173 -- returns whole seconds and remainder 174 local function tick2seconds(ticks) 175 local secs = (ticks / rb.HZ) 176 local csecs = (ticks - (secs * rb.HZ)) 177 return secs, csecs 178 end 179 180 local norecurse = true 181 local f_finddir = nil -- function to match directories; nil all, false none 182 local f_findfile = nil -- function to match files; nil all, false none 183 184 local p_settings = {wrap = true, hasheader = true} 185 186 local timer 187 local files = {} 188 local dirs = {} 189 local item = 1 190 _lcd:clear() 191 192 while item > 0 do 193 if not title then 194 timer = _timer() 195 end 196 197 dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, sort_by, dirs, files) 198 199 local parentdir = dirs[1] 200 for i = 1, #dirs do 201 dirs[i] = "\t" .. dirs[i] 202 end 203 204 if not descending then 205 for i = 1, #files do 206 -- only store file name .. strip attributes from end 207 table.insert(dirs, "\t" .. string.match(files[i], "[^;]+") or "?") 208 end 209 else 210 for i = #files, 1, -1 do 211 -- only store file name .. strip attributes from end 212 table.insert(dirs, "\t" .. string.match(files[i], "[^;]+") or "?") 213 end 214 end 215 for i=1, #files do files[i] = nil end -- empty table for reuse 216 217 if not title then 218 hstr = string.format(dstr, #dirs - 1, tick2seconds(timer:stop())) 219 end 220 221 table.insert(dirs, 1, hstr) 222 223 item = print_table(dirs, #dirs, p_settings) 224 225 -- If item was selected follow directory or return filename 226 if item > 0 then 227 dir = string.gsub(dirs[item], "%c+","") 228 if not rb.dir_exists("/" .. dir) then 229 return dir 230 end 231 end 232 233 if dir == parentdir then 234 dir = dir:sub(1, dir:match(".*()/") - 1) 235 if dir == "" then dir = "/" end 236 end 237 for i=1, #dirs do dirs[i] = nil end -- empty table for reuse 238 239 end 240end -- file_choose 241--------------------------------------------------------------------------------