mirror of
https://github.com/koreader/koreader.git
synced 2025-12-24 12:14:05 +01:00
Option to synchronize files in local koreader folder with folder on Dropbox. All files that aren't on the local disk will be downloaded. Files with different sizes will also be downloaded and overwritten. The download process can be paused or stopped at any time (similar to downloading images in Wikipedia). Synchronize and settings are available after long press in dropbox account in Cloud storage. Limitations: Only one folder (without subfolders) can be synchronize with one folder on local storage.
684 lines
23 KiB
Lua
684 lines
23 KiB
Lua
local ButtonDialog = require("ui/widget/buttondialog")
|
|
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
|
|
local ConfirmBox = require("ui/widget/confirmbox")
|
|
local DataStorage = require("datastorage")
|
|
local DropBox = require("apps/cloudstorage/dropbox")
|
|
local Ftp = require("apps/cloudstorage/ftp")
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
local LuaSettings = require("luasettings")
|
|
local Menu = require("ui/widget/menu")
|
|
local UIManager = require("ui/uimanager")
|
|
local WebDav = require("apps/cloudstorage/webdav")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
local T = require("ffi/util").template
|
|
local _ = require("gettext")
|
|
local Screen = require("device").screen
|
|
|
|
local CloudStorage = Menu:extend{
|
|
cloud_servers = {
|
|
{
|
|
text = _("Add new cloud storage"),
|
|
title = _("Choose cloud type"),
|
|
url = "add",
|
|
editable = false,
|
|
},
|
|
},
|
|
no_title = false,
|
|
show_parent = nil,
|
|
is_popout = false,
|
|
is_borderless = true,
|
|
title = _("Cloud storage")
|
|
}
|
|
|
|
function CloudStorage:init()
|
|
self.cs_settings = self:readSettings()
|
|
self.show_parent = self
|
|
if self.item then
|
|
self.item_table = self:genItemTable(self.item)
|
|
self.choose_folder_mode = true
|
|
else
|
|
self.item_table = self:genItemTableFromRoot()
|
|
end
|
|
self.width = Screen:getWidth()
|
|
self.height = Screen:getHeight()
|
|
Menu.init(self)
|
|
if self.item then
|
|
self.item_table[1].callback()
|
|
end
|
|
end
|
|
|
|
function CloudStorage:genItemTableFromRoot()
|
|
local item_table = {}
|
|
table.insert(item_table, {
|
|
text = _("Add new cloud storage"),
|
|
callback = function()
|
|
self:selectCloudType()
|
|
end,
|
|
})
|
|
local added_servers = self.cs_settings:readSetting("cs_servers") or {}
|
|
for _, server in ipairs(added_servers) do
|
|
table.insert(item_table, {
|
|
text = server.name,
|
|
address = server.address,
|
|
username = server.username,
|
|
password = server.password,
|
|
type = server.type,
|
|
editable = true,
|
|
url = server.url,
|
|
sync_source_folder = server.sync_source_folder,
|
|
sync_dest_folder = server.sync_dest_folder,
|
|
callback = function()
|
|
self.type = server.type
|
|
self.password = server.password
|
|
self.address = server.address
|
|
self.username = server.username
|
|
self:openCloudServer(server.url)
|
|
end,
|
|
})
|
|
end
|
|
return item_table
|
|
end
|
|
|
|
function CloudStorage:genItemTable(item)
|
|
local item_table = {}
|
|
local added_servers = self.cs_settings:readSetting("cs_servers") or {}
|
|
for _, server in ipairs(added_servers) do
|
|
if server.name == item.text and server.password == item.password and server.type == item.type then
|
|
table.insert(item_table, {
|
|
text = server.name,
|
|
address = server.address,
|
|
username = server.username,
|
|
password = server.password,
|
|
type = server.type,
|
|
url = server.url,
|
|
callback = function()
|
|
self.type = server.type
|
|
self.password = server.password
|
|
self.address = server.address
|
|
self.username = server.username
|
|
self:openCloudServer(server.url)
|
|
end,
|
|
})
|
|
end
|
|
end
|
|
return item_table
|
|
end
|
|
|
|
function CloudStorage:selectCloudType()
|
|
local buttons = {
|
|
{
|
|
{
|
|
text = _("Dropbox"),
|
|
callback = function()
|
|
UIManager:close(self.cloud_dialog)
|
|
self:configCloud("dropbox")
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("FTP"),
|
|
callback = function()
|
|
UIManager:close(self.cloud_dialog)
|
|
self:configCloud("ftp")
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("WebDAV"),
|
|
callback = function()
|
|
UIManager:close(self.cloud_dialog)
|
|
self:configCloud("webdav")
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
self.cloud_dialog = ButtonDialogTitle:new{
|
|
title = _("Choose cloud storage type"),
|
|
title_align = "center",
|
|
buttons = buttons,
|
|
}
|
|
|
|
UIManager:show(self.cloud_dialog)
|
|
return true
|
|
end
|
|
|
|
function CloudStorage:openCloudServer(url)
|
|
local tbl
|
|
local NetworkMgr = require("ui/network/manager")
|
|
if self.type == "dropbox" then
|
|
if not NetworkMgr:isOnline() then
|
|
NetworkMgr:promptWifiOn()
|
|
return
|
|
end
|
|
tbl = DropBox:run(url, self.password, self.choose_folder_mode)
|
|
elseif self.type == "ftp" then
|
|
if not NetworkMgr:isConnected() then
|
|
NetworkMgr:promptWifiOn()
|
|
return
|
|
end
|
|
tbl = Ftp:run(self.address, self.username, self.password, url)
|
|
elseif self.type == "webdav" then
|
|
if not NetworkMgr:isConnected() then
|
|
NetworkMgr:promptWifiOn()
|
|
return
|
|
end
|
|
tbl = WebDav:run(self.address, self.username, self.password, url)
|
|
end
|
|
if tbl and #tbl > 0 then
|
|
self:switchItemTable(url, tbl)
|
|
return true
|
|
elseif not tbl then
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("Cannot fetch list of folder contents\nPlease check your configuration or network connection."),
|
|
timeout = 3,
|
|
})
|
|
table.remove(self.paths)
|
|
return false
|
|
else
|
|
UIManager:show(InfoMessage:new{ text = _("Empty folder") })
|
|
return false
|
|
end
|
|
end
|
|
|
|
function CloudStorage:onMenuSelect(item)
|
|
if item.callback then
|
|
if item.url ~= nil then
|
|
table.insert(self.paths, {
|
|
url = item.url,
|
|
})
|
|
end
|
|
item.callback()
|
|
elseif item.type == "file" then
|
|
self:downloadFile(item)
|
|
elseif item.type == "other" then
|
|
return true
|
|
else
|
|
table.insert(self.paths, {
|
|
url = item.url,
|
|
})
|
|
if not self:openCloudServer(item.url) then
|
|
table.remove(self.paths)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function CloudStorage:downloadFile(item)
|
|
local lastdir = G_reader_settings:readSetting("lastdir")
|
|
local cs_settings = self:readSettings()
|
|
local download_dir = cs_settings:readSetting("download_dir") or lastdir
|
|
local path = download_dir .. '/' .. item.text
|
|
if lfs.attributes(path) then
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("File already exists. Would you like to overwrite it?"),
|
|
ok_callback = function()
|
|
self:cloudFile(item, path)
|
|
end
|
|
})
|
|
else
|
|
self:cloudFile(item, path)
|
|
end
|
|
end
|
|
|
|
function CloudStorage:cloudFile(item, path)
|
|
local path_dir = path
|
|
local download_text = _("Downloading. This might take a moment.")
|
|
local buttons = {
|
|
{
|
|
{
|
|
text = _("Download file"),
|
|
callback = function()
|
|
if self.type == "dropbox" then
|
|
local callback_close = function()
|
|
self:onClose()
|
|
end
|
|
UIManager:scheduleIn(1, function()
|
|
DropBox:downloadFile(item, self.password, path_dir, callback_close)
|
|
end)
|
|
UIManager:close(self.download_dialog)
|
|
UIManager:show(InfoMessage:new{
|
|
text = download_text,
|
|
timeout = 1,
|
|
})
|
|
elseif self.type == "ftp" then
|
|
local callback_close = function()
|
|
self:onClose()
|
|
end
|
|
UIManager:scheduleIn(1, function()
|
|
Ftp:downloadFile(item, self.address, self.username, self.password, path_dir, callback_close)
|
|
end)
|
|
UIManager:close(self.download_dialog)
|
|
UIManager:show(InfoMessage:new{
|
|
text = download_text,
|
|
timeout = 1,
|
|
})
|
|
elseif self.type == "webdav" then
|
|
local callback_close = function()
|
|
self:onClose()
|
|
end
|
|
UIManager:scheduleIn(1, function()
|
|
WebDav:downloadFile(item, self.address, self.username, self.password, path_dir, callback_close)
|
|
end)
|
|
UIManager:close(self.download_dialog)
|
|
UIManager:show(InfoMessage:new{
|
|
text = download_text,
|
|
timeout = 1,
|
|
})
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Choose download directory"),
|
|
callback = function()
|
|
require("ui/downloadmgr"):new{
|
|
show_hidden = G_reader_settings:readSetting("show_hidden"),
|
|
onConfirm = function(path_download)
|
|
self.cs_settings:saveSetting("download_dir", path_download)
|
|
self.cs_settings:flush()
|
|
path_dir = path_download .. '/' .. item.text
|
|
end,
|
|
}:chooseDir()
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
self.download_dialog = ButtonDialog:new{
|
|
buttons = buttons
|
|
}
|
|
UIManager:show(self.download_dialog)
|
|
end
|
|
|
|
function CloudStorage:updateSyncFolder(item, source, dest)
|
|
local cs_settings = self:readSettings()
|
|
local cs_servers = cs_settings:readSetting("cs_servers") or {}
|
|
for _, server in ipairs(cs_servers) do
|
|
if server.name == item.text and server.password == item.password and server.type == item.type then
|
|
if source then
|
|
server.sync_source_folder = source
|
|
end
|
|
if dest then
|
|
server.sync_dest_folder = dest
|
|
end
|
|
break
|
|
end
|
|
end
|
|
cs_settings:saveSetting("cs_servers", cs_servers)
|
|
cs_settings:flush()
|
|
end
|
|
|
|
function CloudStorage:onMenuHold(item)
|
|
if item.type == "folder_long_press" then
|
|
local title = T(_("Select this directory?\n\n%1"), item.url)
|
|
local onConfirm = self.onConfirm
|
|
local button_dialog
|
|
button_dialog = ButtonDialogTitle:new{
|
|
title = title,
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Cancel"),
|
|
callback = function()
|
|
UIManager:close(button_dialog)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Select"),
|
|
callback = function()
|
|
if onConfirm then
|
|
onConfirm(item.url)
|
|
end
|
|
UIManager:close(button_dialog)
|
|
UIManager:close(self)
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(button_dialog)
|
|
end
|
|
if item.editable then
|
|
local cs_server_dialog
|
|
local buttons = {
|
|
{
|
|
{
|
|
text = _("Info"),
|
|
enabled = true,
|
|
callback = function()
|
|
UIManager:close(cs_server_dialog)
|
|
self:infoServer(item)
|
|
end
|
|
},
|
|
{
|
|
text = _("Edit"),
|
|
enabled = true,
|
|
callback = function()
|
|
UIManager:close(cs_server_dialog)
|
|
self:editCloudServer(item)
|
|
|
|
end
|
|
},
|
|
{
|
|
text = _("Delete"),
|
|
enabled = true,
|
|
callback = function()
|
|
UIManager:close(cs_server_dialog)
|
|
self:deleteCloudServer(item)
|
|
end
|
|
},
|
|
},
|
|
}
|
|
if item.type == "dropbox" then
|
|
table.insert(buttons, {
|
|
{
|
|
text = _("Synchronize now"),
|
|
enabled = item.sync_source_folder ~= nil and item.sync_dest_folder ~= nil,
|
|
callback = function()
|
|
UIManager:close(cs_server_dialog)
|
|
self:synchronizeCloud(item)
|
|
end
|
|
},
|
|
{
|
|
text = _("Synchronize settings"),
|
|
enabled = true,
|
|
callback = function()
|
|
UIManager:close(cs_server_dialog)
|
|
self:synchronizeSettings(item)
|
|
end
|
|
},
|
|
})
|
|
end
|
|
cs_server_dialog = ButtonDialog:new{
|
|
buttons = buttons
|
|
}
|
|
UIManager:show(cs_server_dialog)
|
|
return true
|
|
end
|
|
end
|
|
|
|
function CloudStorage:synchronizeCloud(item)
|
|
local Trapper = require("ui/trapper")
|
|
Trapper:wrap(function()
|
|
Trapper:setPausedText("Download paused.\nDo you want to continue or abort downloading files?")
|
|
local ok, downloaded_files, failed_files = pcall(self.downloadListFiles, self, item)
|
|
if ok and downloaded_files then
|
|
if not failed_files then failed_files = 0 end
|
|
local text
|
|
if downloaded_files == 0 and failed_files == 0 then
|
|
text = _("No files to download from dropbox.")
|
|
elseif downloaded_files > 0 and failed_files == 0 then
|
|
text = T(_("Successfuly downloaded %1 files from Dropbox to local storage."), downloaded_files)
|
|
else
|
|
text = T(_("Successfuly downloaded %1 files from Dropbox to local storage.\nFailed downloaded %2 files."),
|
|
downloaded_files, failed_files)
|
|
end
|
|
UIManager:show(InfoMessage:new{
|
|
text = text,
|
|
timeout = 3,
|
|
})
|
|
else
|
|
Trapper:reset() -- close any last widget not cleaned if error
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("No files to download from Dropbox.\nPlease check your configuration and connection."),
|
|
timeout = 3,
|
|
})
|
|
end
|
|
end)
|
|
end
|
|
|
|
function CloudStorage:downloadListFiles(item)
|
|
local local_files = {}
|
|
local path = item.sync_dest_folder
|
|
local UI = require("ui/trapper")
|
|
UI:info(_("Retrieving files…"))
|
|
|
|
local ok, iter, dir_obj = pcall(lfs.dir, path)
|
|
if ok then
|
|
for f in iter, dir_obj do
|
|
local filename = path .."/" .. f
|
|
local attributes = lfs.attributes(filename)
|
|
if attributes.mode == "file" then
|
|
local_files[f] = attributes.size
|
|
end
|
|
end
|
|
end
|
|
local remote_files = DropBox:showFiles(item.sync_source_folder, item.password)
|
|
if #remote_files == 0 then
|
|
UI:clear()
|
|
return false
|
|
end
|
|
local files_to_download = 0
|
|
for i, file in ipairs(remote_files) do
|
|
if not local_files[file.text] or local_files[file.text] ~= file.size then
|
|
files_to_download = files_to_download + 1
|
|
remote_files[i].download = true
|
|
end
|
|
end
|
|
|
|
if files_to_download == 0 then
|
|
UI:clear()
|
|
return 0
|
|
end
|
|
|
|
local response, go_on
|
|
local proccessed_files = 0
|
|
local success_files = 0
|
|
local unsuccess_files = 0
|
|
for _, file in ipairs(remote_files) do
|
|
if file.download then
|
|
proccessed_files = proccessed_files + 1
|
|
print(file.url)
|
|
local text = string.format("Downloading file (%d/%d):\n%s", proccessed_files, files_to_download, file.text)
|
|
go_on = UI:info(text)
|
|
if not go_on then
|
|
break
|
|
end
|
|
response = DropBox:downloadFileNoUI(file.url, item.password, item.sync_dest_folder .. "/" .. file.text)
|
|
if response then
|
|
success_files = success_files + 1
|
|
else
|
|
unsuccess_files = unsuccess_files + 1
|
|
end
|
|
end
|
|
end
|
|
UI:clear()
|
|
return success_files, unsuccess_files
|
|
end
|
|
|
|
function CloudStorage:synchronizeSettings(item)
|
|
local syn_dialog
|
|
local dropbox_sync_folder = item.sync_source_folder or "not set"
|
|
local local_sync_folder = item.sync_dest_folder or "not set"
|
|
syn_dialog = ButtonDialogTitle:new {
|
|
title = T(_("Dropbox folder:\n%1\nLocal folder:\n%2"), dropbox_sync_folder, local_sync_folder),
|
|
title_align = "center",
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Choose dropbox folder"),
|
|
callback = function()
|
|
UIManager:close(syn_dialog)
|
|
require("ui/cloudmgr"):new{
|
|
item = item,
|
|
onConfirm = function(path)
|
|
self:updateSyncFolder(item, path)
|
|
item.sync_source_folder = path
|
|
self:synchronizeSettings(item)
|
|
end,
|
|
}:chooseDir()
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Choose local folder"),
|
|
callback = function()
|
|
UIManager:close(syn_dialog)
|
|
require("ui/downloadmgr"):new{
|
|
onConfirm = function(path)
|
|
self:updateSyncFolder(item, nil, path)
|
|
item.sync_dest_folder = path
|
|
self:synchronizeSettings(item)
|
|
end,
|
|
}:chooseDir()
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Close"),
|
|
callback = function()
|
|
UIManager:close(syn_dialog)
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
UIManager:show(syn_dialog)
|
|
end
|
|
|
|
function CloudStorage:configCloud(type)
|
|
local callbackAdd = function(fields)
|
|
local cs_settings = self:readSettings()
|
|
local cs_servers = cs_settings:readSetting("cs_servers") or {}
|
|
if type == "dropbox" then
|
|
table.insert(cs_servers,{
|
|
name = fields[1],
|
|
password = fields[2],
|
|
type = "dropbox",
|
|
url = "/"
|
|
})
|
|
elseif type == "ftp" then
|
|
table.insert(cs_servers,{
|
|
name = fields[1],
|
|
address = fields[2],
|
|
username = fields[3],
|
|
password = fields[4],
|
|
url = fields[5],
|
|
type = "ftp",
|
|
})
|
|
elseif type == "webdav" then
|
|
table.insert(cs_servers,{
|
|
name = fields[1],
|
|
address = fields[2],
|
|
username = fields[3],
|
|
password = fields[4],
|
|
url = fields[5],
|
|
type = "webdav",
|
|
})
|
|
end
|
|
cs_settings:saveSetting("cs_servers", cs_servers)
|
|
cs_settings:flush()
|
|
self:init()
|
|
end
|
|
if type == "dropbox" then
|
|
DropBox:config(nil, callbackAdd)
|
|
end
|
|
if type == "ftp" then
|
|
Ftp:config(nil, callbackAdd)
|
|
end
|
|
if type == "webdav" then
|
|
WebDav:config(nil, callbackAdd)
|
|
end
|
|
end
|
|
|
|
function CloudStorage:editCloudServer(item)
|
|
local callbackEdit = function(updated_config, fields)
|
|
local cs_settings = self:readSettings()
|
|
local cs_servers = cs_settings:readSetting("cs_servers") or {}
|
|
if item.type == "dropbox" then
|
|
for i, server in ipairs(cs_servers) do
|
|
if server.name == updated_config.text and server.password == updated_config.password then
|
|
server.name = fields[1]
|
|
server.password = fields[2]
|
|
cs_servers[i] = server
|
|
break
|
|
end
|
|
end
|
|
elseif item.type == "ftp" then
|
|
for i, server in ipairs(cs_servers) do
|
|
if server.name == updated_config.text and server.address == updated_config.address then
|
|
server.name = fields[1]
|
|
server.address = fields[2]
|
|
server.username = fields[3]
|
|
server.password = fields[4]
|
|
server.url = fields[5]
|
|
cs_servers[i] = server
|
|
break
|
|
end
|
|
end
|
|
elseif item.type == "webdav" then
|
|
for i, server in ipairs(cs_servers) do
|
|
if server.name == updated_config.text and server.address == updated_config.address then
|
|
server.name = fields[1]
|
|
server.address = fields[2]
|
|
server.username = fields[3]
|
|
server.password = fields[4]
|
|
server.url = fields[5]
|
|
cs_servers[i] = server
|
|
break
|
|
end
|
|
end
|
|
end
|
|
cs_settings:saveSetting("cs_servers", cs_servers)
|
|
cs_settings:flush()
|
|
self:init()
|
|
end
|
|
if item.type == "dropbox" then
|
|
DropBox:config(item, callbackEdit)
|
|
elseif item.type == "ftp" then
|
|
Ftp:config(item, callbackEdit)
|
|
elseif item.type == "webdav" then
|
|
WebDav:config(item, callbackEdit)
|
|
end
|
|
end
|
|
|
|
function CloudStorage:deleteCloudServer(item)
|
|
local cs_settings = self:readSettings()
|
|
local cs_servers = cs_settings:readSetting("cs_servers") or {}
|
|
for i, server in ipairs(cs_servers) do
|
|
if server.name == item.text and server.password == item.password and server.type == item.type then
|
|
table.remove(cs_servers, i)
|
|
break
|
|
end
|
|
end
|
|
cs_settings:saveSetting("cs_servers", cs_servers)
|
|
cs_settings:flush()
|
|
self:init()
|
|
end
|
|
|
|
function CloudStorage:infoServer(item)
|
|
if item.type == "dropbox" then
|
|
DropBox:info(item.password)
|
|
elseif item.type == "ftp" then
|
|
Ftp:info(item)
|
|
elseif item.type == "webdav" then
|
|
WebDav:info(item)
|
|
end
|
|
end
|
|
|
|
function CloudStorage:readSettings()
|
|
self.cs_settings = LuaSettings:open(DataStorage:getSettingsDir().."/cloudstorage.lua")
|
|
return self.cs_settings
|
|
end
|
|
|
|
function CloudStorage:onReturn()
|
|
if #self.paths > 0 then
|
|
table.remove(self.paths)
|
|
local path = self.paths[#self.paths]
|
|
if path then
|
|
-- return to last path
|
|
self:openCloudServer(path.url)
|
|
else
|
|
-- return to root path
|
|
self:init()
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
return CloudStorage
|