local dir = require "opendirectory" app.on('init', function() print("scanning experiences (this make take a while..)") app.opts.plugin_diskscan_dirname = app.opts.plugin_diskscan_dir:gsub("^[.]?[/]?","") -- swap between internal / external version of diskscan_dir if app.opts.plugin_diskscan_dir:match("^/zip/") then if util.fileExist(app.opts.plugin_diskscan_dir) then print("ℹ️ serving " .. app.opts.plugin_diskscan_dir .. " from local disk") app.opts.plugin_diskscan_dir = app.opts.plugin_diskscan_dir:gsub("^/zip/","") app.set('app.opts', app.opts) end else if not util.fileExist(app.opts.plugin_diskscan_dir) then print("ℹ️ serving " .. app.opts.plugin_diskscan_dir .. " from internal (executable)") app.opts.plugin_diskscan_dir = '/zip/' .. util.stripPath(app.opts.plugin_diskscan_dir) app.set('app.opts', app.opts) end end app.plugin.diskscan.scan(app.opts.plugin_diskscan_dir) end) -- this registers the plugin UI /funcs return { name = "Files", tab = " Files", info = '', opts = { { key='app.opts.plugin_diskscan_dir', title='Directory', info="Directory to scan for experiences (use '.' for current) ", default="./myverse", values='' } }, shouldHideFile = function(name) return name == ".." -- parent or name == "util" -- repository or name:match("^%.[a-zA-Z0-9_-]") -- hidden or name == "src" -- repository or name == "result" -- repository end, scan = function(scandir) local me = app.plugin.diskscan local seen = {symlinks={}} -- ensure rootdir as experience app.addExperience({ path = scandir, url = "/" .. util.stripPath(scandir), name = scandir:gsub(".*/",""), thumbnail = "", title = util.dehyphenate( scandir:gsub(".*/","") ), file = { ino = scandir }, id = scandir },scandir) me.scanDir(scandir, nil, seen ) -- deal with (circular) symlinks foreach( seen.symlinks, function(ino,experience) if not me.shouldHideFile(experience.name) then local refItem = seen[experience.id_target] if refItem then print("ℹ️ ".. experience.path ) print("✅ └─ plugin/diskscan: detected symlink..creating portal") experience.file = refItem.file experience.portal = refItem app.pub("scan_experience" .. (experience.dir and '' or '_file') , nil, experience) app.addExperience(experience,ino) end end end) -- cleanup file handles foreach( app.cache.items, function(k,v) -- remove file attributes (which are closed already) if v.file then v.file.stat = nil v.file.attr = nil v.file.state = nil end end) -- make sure to attach children-array to parents me.addChildren() -- sort & apply dirs to plugin options table.sort( app.cache.dirs) foreach( app.plugin, function(k,plugin) foreach( plugin.opts, function(k,opt) if opt.type == 'dir' then opt.values = app.cache.dirs end end) end) end, scanDir = function(scandir,parent,seen) local me = app.plugin.diskscan parent = parent or app.cache.items[scandir] app.cache.dirs[ scandir ] = util.stripPath(scandir) -- scan dirs local scanFile = function(path,stat,state,name,kind,ino,off,attr,fullpath) local experience = { path = fullpath, url = "/" .. util.stripPath(fullpath), name = name, title = util.dehyphenate( name ), sidecarfile = {}, thumbnail = '', tag = {}, id = ino, file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path} } experience.parent = parent if (kind == unix.DT_LNK) then experience.symlink = true local stat, err = unix.stat(fullpath) if stat then experience.id_target = stat:ino() -- stat follows symlink by default local realpath = unix.realpath(fullpath) state,err = unix.opendir(realpath) if not err then experience.symlink = true experience.dir = true state:close() end seen.symlinks[ ino ] = experience end end --local already_seen = seen[ino] and seen[ino].parent.file.ino == experience.parent.file.ino and true or false -- process once --if already_seen and not experience.symlink then return end me.checkSideCarThumbnail(experience) -- found directory if not me.shouldHideFile(name) and ( (kind == unix.DT_DIR and kind ~= unix.DT_LNK) or experience.dir ) then print("✅ ".. fullpath ) experience.dir = true app.pub("scan_experience", nil, experience) app.addExperience(experience,ino) -- scan children me.scanDir(fullpath,experience,seen,symlinks) end ---- found file if not me.shouldHideFile(name) and ( (kind ~= unix.DT_DIR and kind ~= unix.DT_LNK) or not experience.dir ) then trace("ℹ️ ".. fullpath) experience.tag.file = true app.pub("scan_experience_file", nil, experience) local size = attr:size() end -- remember file/parent relationship seen[ino] = experience end local dref, derr; dref, derr = dir.foreach( scandir, scanFile ) if not derr then dref:close() end end, addChildren = function() -- .children is a convenient foreach( app.cache.items, function(k,exp) exp.children = exp.children or {} if exp.parent then exp.parent.children = exp.parent.children or {} table.insert(exp.parent.children, exp.id) end end) end, checkSideCarThumbnail = function(experience) local fullpath = experience.path local name = experience.name -- check for directory thumbnail sidecarfile local thumbExt = {"png","jpg","webp"} foreach( thumbExt, function(k,ext) local files = { fullpath .. '.' .. ext, fullpath .. '/' .. name .. '.' .. ext } foreach( files, function(kk,thumbnailFile) if util.fileExist( thumbnailFile ) then experience.thumbnail = "/" .. util.stripPath(thumbnailFile) print("✅ └─ plugin/diskscan: detected .".. ext:lower() .. " thumbnail file: " .. thumbnailFile) end end) end) end }