2
0
mirror of https://github.com/koreader/koreader synced 2024-11-11 19:11:14 +00:00
koreader/plugins/archiveviewer.koplugin/main.lua

189 lines
6.1 KiB
Lua
Raw Normal View History

2023-06-21 04:32:24 +00:00
local BD = require("ui/bidi")
local CenterContainer = require("ui/widget/container/centercontainer")
local ConfirmBox = require("ui/widget/confirmbox")
local Menu = require("ui/widget/menu")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local ffiUtil = require("ffi/util")
local util = require("util")
local _ = require("gettext")
local Screen = require("device").screen
local T = ffiUtil.template
local ArchiveViewer = WidgetContainer:extend{
-- list_table is a flat table containing archive files and folders
-- key - a full path of the folder ("/" for root), for all folders and subfolders of any level
-- value - a subtable of subfolders and files in the folder
-- subtable key - a name of a subfolder ending with /, or a name of a file (without path)
-- subtable value - false for subfolders, or file size (string)
list_table = nil,
arc_type = nil,
arc_ext = {
cbz = true,
epub = true,
zip = true,
},
}
function ArchiveViewer:isSupported(file)
return self.arc_ext[util.getFileNameSuffix(file):lower()] and true or false
end
function ArchiveViewer:openArchiveViewer(file)
local _, filename = util.splitFilePathName(file)
local fileext = util.getFileNameSuffix(file):lower()
if fileext == "cbz" or fileext == "epub" or fileext == "zip" then
self.arc_type = "zip"
end
self.fm_updated = nil
self.list_table = {}
if self.arc_type == "zip" then
self:getZipListTable(file)
else -- add other archivers here
return
end
local item_table = self:getItemTable() -- root
local menu_container = CenterContainer:new{
dimen = Screen:getSize(),
}
local menu = Menu:new{
is_borderless = true,
is_popout = false,
title_multilines = true,
show_parent = menu_container,
onMenuSelect = function(self_menu, item)
if item.text:sub(-1) == "/" then -- folder
local title = item.path == "" and filename or filename.."/"..item.path
self_menu:switchItemTable(title, self:getItemTable(item.path))
else
self:extractFile(file, item.path)
end
end,
close_callback = function()
UIManager:close(menu_container)
if self.fm_updated then
self.ui:onRefresh()
end
end,
}
table.insert(menu_container, menu)
menu:switchItemTable(filename, item_table)
UIManager:show(menu_container)
end
function ArchiveViewer:getZipListTable(file)
local function parse_path(filepath, filesize)
if not filepath then return end
local path, name = util.splitFilePathName(filepath)
if path == "" then
path = "/"
end
if not self.list_table[path] then
self.list_table[path] = {}
end
if name == "" then -- some archivers include subfolder name as a separate entry ending with "/"
if path ~= "/" then
parse_path(path:sub(1,-2), false) -- filesize == false for subfolders
end
else
if self.list_table[path][name] == nil then
self.list_table[path][name] = filesize
parse_path(path:sub(1,-2), false) -- go up to include subfolders of the branch into the table
end
end
end
local std_out = io.popen("unzip ".."-qql \""..file.."\"")
for line in std_out:lines() do
-- entry datetime not used so far
2023-06-30 14:48:44 +00:00
local fsize, fname = string.match(line, "%s+(%d+)%s+[-0-9]+%s+[0-9:]+%s+(.+)")
2023-06-21 04:32:24 +00:00
parse_path(fname, fsize or 0)
end
end
function ArchiveViewer:getItemTable(path)
local prefix, item_table
if path == nil or path == "" then -- root
path = "/"
prefix = ""
item_table = {}
else
prefix = path
item_table = {
{
text = BD.mirroredUILayout() and BD.ltr("../ ⬆") or "⬆ ../",
path = util.splitFilePathName(path:sub(1,-2)),
},
}
end
local files, dirs = {}, {}
for name, v in pairs(self.list_table[path] or {}) do
if v then -- file
table.insert(files, {
text = name,
bidi_wrap_func = BD.filename,
path = prefix..name,
mandatory = util.getFriendlySize(tonumber(v)),
})
else -- folder
local dirname = name.."/"
table.insert(dirs, {
text = dirname,
bidi_wrap_func = BD.directory,
path = prefix..dirname,
mandatory = self:getItemDirMandatory(prefix..dirname),
})
end
end
local sorting = function(a, b) -- by name, folders first
return ffiUtil.strcoll(a.text, b.text)
end
table.sort(dirs, sorting)
table.sort(files, sorting)
for _, v in ipairs(dirs) do
table.insert(item_table, v)
end
for _, v in ipairs(files) do
table.insert(item_table, v)
end
return item_table
end
function ArchiveViewer:getItemDirMandatory(name)
local sub_dirs, dir_files = 0, 0
for _, v in pairs(self.list_table[name]) do
if v then
dir_files = dir_files + 1
else
sub_dirs = sub_dirs + 1
end
end
local text = T("%1 \u{F016}", dir_files)
if sub_dirs > 0 then
text = T("%1 \u{F114} ", sub_dirs) .. text
end
return text
end
function ArchiveViewer:extractFile(arcfile, filepath)
UIManager:show(ConfirmBox:new{
text = _("Extract this file?") .. "\n\n" .. filepath .. "\n\n" ..
_("If the file already exists, it will be overwritten."),
ok_text = _("Extract"),
ok_callback = function()
if self.arc_type == "zip" then
io.popen("unzip ".."-qo \""..arcfile.."\"".." ".."\""..filepath.."\""..
" -d ".."\""..util.splitFilePathName(arcfile).."\"")
else -- add other archivers here
return
end
self.fm_updated = true
end,
})
end
return ArchiveViewer