Update KOSyncClient

pull/2197/head
Zijie He 8 years ago
parent c0cc3b677c
commit 820a39c8f7

@ -99,7 +99,10 @@ function Document:discardChange()
self.is_edited = false
end
-- calculate partial digest of the document
-- calculate partial digest of the document and store in its docsettings to avoid document saving
-- feature to change its checksum.
--
-- To the calculating mechanism itself.
-- since only PDF documents could be modified by KOReader by appending data
-- at the end of the files when highlighting, we use a non-even sampling
-- algorithm which samples with larger weight at file head and much smaller
@ -109,23 +112,31 @@ end
-- 1048576, 4194304, 16777216, 67108864, 268435456 or 1073741824, appending data
-- by highlighting in KOReader may change the digest value.
function Document:fastDigest()
local md5 = require("ffi/MD5")
local lshift = bit.lshift
if not self.file then return end
local file = io.open(self.file, 'rb')
if file then
local step, size = 1024, 1024
local m = md5.new()
for i = -1, 10 do
file:seek("set", lshift(step, 2*i))
local sample = file:read(size)
if sample then
m:update(sample)
else
break
local docsettings = require("docsettings"):open(self.file)
local result = docsettings:readSetting("partial_md5_checksum")
if not result then
local md5 = require("ffi/MD5")
local lshift = bit.lshift
local step, size = 1024, 1024
local m = md5.new()
for i = -1, 10 do
file:seek("set", lshift(step, 2*i))
local sample = file:read(size)
if sample then
m:update(sample)
else
break
end
end
result = m:sum()
docsettings:saveSetting("partial_md5_checksum", result)
end
docsettings:close()
file:close()
return m:sum()
return result
end
end

@ -0,0 +1,33 @@
-- A set of functions to extend math.random and math.randomseed.
local random = {}
-- Use current time as seed to randomlize.
function random.seed()
math.randomseed(os.time())
end
random.seed()
-- Return a UUID (v4, random).
function random.uuid(with_dash)
local array = {}
for i = 1, 16 do
table.insert(array, math.random(256) - 1)
end
-- The 13th character should be 4.
array[7] = bit.band(array[7], 79)
array[7] = bit.bor(array[7], 64)
-- The 17th character should be 8 / 9 / a / b.
array[9] = bit.band(array[9], 191)
array[9] = bit.bor(array[9], 128)
if with_dash then
return string.format("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
unpack(array))
else
return string.format("%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
unpack(array))
end
end
return random

@ -93,8 +93,15 @@ function KOSyncClient:authorize(username, password)
end
end
function KOSyncClient:update_progress(username, password,
document, progress, percentage, device, callback)
function KOSyncClient:update_progress(
username,
password,
document,
progress,
percentage,
device,
device_id,
callback)
self.client:reset_middlewares()
self.client:enable('Format.JSON')
self.client:enable("GinClient")
@ -109,6 +116,7 @@ function KOSyncClient:update_progress(username, password,
progress = progress,
percentage = percentage,
device = device,
device_id = device_id,
})
end)
if ok then
@ -123,8 +131,11 @@ function KOSyncClient:update_progress(username, password,
if UIManager.looper then UIManager:setInputTimeout() end
end
function KOSyncClient:get_progress(username, password,
document, callback)
function KOSyncClient:get_progress(
username,
password,
document,
callback)
self.client:reset_middlewares()
self.client:enable('Format.JSON')
self.client:enable("GinClient")

@ -28,12 +28,14 @@
"progress",
"percentage",
"device",
"device_id",
],
"payload" : [
"document",
"progress",
"percentage",
"device",
"device_id",
],
"expected_status" : [200, 202, 401]
},

@ -6,7 +6,7 @@ local DocSettings = require("docsettings")
local NetworkMgr = require("ui/network/manager")
local UIManager = require("ui/uimanager")
local Screen = require("device").screen
local Device = require("device")
local DeviceModel = require("device").model
local Event = require("ui/event")
local Math = require("optmath")
local DEBUG = require("dbg")
@ -14,12 +14,6 @@ local T = require("ffi/util").template
local _ = require("gettext")
local md5 = require("ffi/MD5")
local l10n = {
_("Unknown server error."),
_("Unauthorized"),
_("Username is already registered."),
}
local KOSync = InputContainer:new{
name = "kosync",
title = _("Register/login to KOReader server"),
@ -31,12 +25,18 @@ function KOSync:init()
self.kosync_username = settings.username
self.kosync_userkey = settings.userkey
self.kosync_auto_sync = not (settings.auto_sync == false)
self.kosync_device_id = G_reader_settings:readSetting("device_id")
assert(self.kosync_device_id)
self.ui:registerPostInitCallback(function()
if self.kosync_auto_sync then
UIManager:scheduleIn(1, function() self:getProgress() end)
end
end)
self.ui.menu:registerToMainMenu(self)
-- Make sure checksum has been calculated at the very first time a document has been opened, to
-- avoid document saving feature to impact the checksum, and eventually impact the document
-- identity in the progress sync feature.
self.view.document:fastDigest()
end
function KOSync:addToMainMenu(tab_item_table)
@ -55,19 +55,36 @@ function KOSync:addToMainMenu(tab_item_table)
end,
},
{
text = _("Auto sync"),
text = _("Auto sync now and future"),
checked_func = function() return self.kosync_auto_sync end,
callback = function()
self.kosync_auto_sync = not self.kosync_auto_sync
if self.kosync_auto_sync then
-- since we will update the progress when closing document, we should pull
-- current progress now to avoid to overwrite it silently.
self:getProgress(true)
else
-- since we won't update the progress when closing document, we should push
-- current progress now to avoid to lose it silently.
self:updateProgress(true)
end
end,
},
{
text = _("Sync now"),
text = _("Push progress from this device"),
enabled_func = function()
return self.kosync_userkey ~= nil
end,
callback = function()
self:updateProgress(true)
end,
},
{
text = _("Pull progress from other devices"),
enabled_func = function()
return self.kosync_userkey ~= nil
end,
callback = function()
self:updateProgress()
self:getProgress(true)
end,
},
@ -225,11 +242,15 @@ function KOSync:logout()
self:onSaveSettings()
end
local function roundPercent(percent)
return math.floor(percent * 10000) / 10000
end
function KOSync:getLastPercent()
if self.ui.document.info.has_pages then
return self.ui.paging:getLastPercent()
return roundPercent(self.ui.paging:getLastPercent())
else
return self.ui.rolling:getLastPercent()
return roundPercent(self.ui.rolling:getLastPercent())
end
end
@ -250,7 +271,21 @@ function KOSync:syncToProgress(progress)
end
end
function KOSync:updateProgress()
local function promptLogin()
UIManager:show(InfoMessage:new{
text = _("Please register / login before using progress synchronization feature."),
timeout = 3,
})
end
local function showSyncError()
UIManager:show(InfoMessage:new{
text = _("Something went wrong when syncing progress, please check your network connection and try again later."),
timeout = 3,
})
end
function KOSync:updateProgress(manual)
if self.kosync_username and self.kosync_userkey then
local KOSyncClient = require("KOSyncClient")
local client = KOSyncClient:new{
@ -260,15 +295,34 @@ function KOSync:updateProgress()
local doc_digest = self.view.document:fastDigest()
local progress = self:getLastProgress()
local percentage = self:getLastPercent()
local ok, err = pcall(client.update_progress, client,
self.kosync_username, self.kosync_userkey,
doc_digest, progress, percentage, Device.model,
local ok, err = pcall(client.update_progress,
client,
self.kosync_username,
self.kosync_userkey,
doc_digest,
progress,
percentage,
DeviceModel,
self.kosync_device_id,
function(ok, body)
DEBUG("update progress for", self.view.document.file, ok)
if manual then
if ok then
UIManager:show(InfoMessage:new{
text = _("Progress has been pushed."),
timeout = 3,
})
else
showSyncError()
end
end
end)
if not ok and err then
DEBUG("err:", err)
if not ok then
if manual then showSyncError() end
if err then DEBUG("err:", err) end
end
elseif manual then
promptLogin()
end
end
@ -280,34 +334,57 @@ function KOSync:getProgress(manual)
service_spec = self.path .. "/api.json"
}
local doc_digest = self.view.document:fastDigest()
local ok, err = pcall(client.get_progress, client,
self.kosync_username, self.kosync_userkey,
doc_digest, function(ok, body)
local ok, err = pcall(client.get_progress,
client,
self.kosync_username,
self.kosync_userkey,
doc_digest,
function(ok, body)
DEBUG("get progress for", self.view.document.file, ok, body)
if body and body.percentage then
local progress = self:getLastProgress()
local percentage = self:getLastPercent()
DEBUG("current progress", percentage)
if body.percentage > percentage
and tostring(body.progress) ~= tostring(progress) then
UIManager:show(ConfirmBox:new{
text = T(_("Sync to furthest location %1% from device '%2'?"),
Math.round(body.percentage*100), body.device),
ok_callback = function()
self:syncToProgress(body.progress)
end,
})
elseif manual and body.progress == progress then
if body then
if body.percentage then
if body.device ~= DeviceModel
or body.device_id ~= self.kosync_device_id then
body.percentage = roundPercent(body.percentage)
local progress = self:getLastProgress()
local percentage = self:getLastPercent()
DEBUG("current progress", percentage)
if body.percentage > percentage and body.progress ~= progress then
UIManager:show(ConfirmBox:new{
text = T(_("Sync to furthest location %1% from device '%2'?"),
Math.round(body.percentage*100), body.device),
ok_callback = function()
self:syncToProgress(body.progress)
end,
})
elseif manual then
UIManager:show(InfoMessage:new{
text = _("Already synchronized."),
timeout = 3,
})
end
elseif manual then
UIManager:show(InfoMessage:new{
text = _("Latest progress is coming from this device."),
timeout = 3,
})
end
elseif manual then
UIManager:show(InfoMessage:new{
text = _("Already synchronized."),
text = _("No progress found for this document."),
timeout = 3,
})
end
elseif manual then
showSyncError()
end
end)
if not ok and err then
DEBUG("err:", err)
if not ok then
if manual then showSyncError() end
if err then DEBUG("err:", err) end
end
elseif manual then
promptLogin()
end
end

@ -96,6 +96,11 @@ local lfs = require("libs/libkoreader-lfs")
local UIManager = require("ui/uimanager")
local Device = require("device")
local Font = require("ui/font")
local random = require("random")
if not G_reader_settings:readSetting("device_id") then
G_reader_settings:saveSetting("device_id", random.uuid())
end
-- read some global reader setting here:
-- font

@ -0,0 +1,33 @@
describe("random package tests", function()
local random
local function is_magic_char(c)
return c == "8" or c == "9" or c == "A" or c == "B"
end
setup(function()
random = require("frontend/random")
end)
it("should generate uuid without dash", function()
for i = 1, 10000 do
local uuid = random.uuid()
assert.Equals(uuid:len(), 32)
assert.Equals(uuid:sub(13, 13), "4")
assert.is_true(is_magic_char(uuid:sub(17, 17)))
end
end)
it("should generate uuid with dash", function()
for i = 1, 10000 do
local uuid = random.uuid(true)
assert.Equals(uuid:len(), 36)
assert.Equals(uuid:sub(9, 9), "-")
assert.Equals(uuid:sub(14, 14), "-")
assert.Equals(uuid:sub(19, 19), "-")
assert.Equals(uuid:sub(24, 24), "-")
assert.Equals(uuid:sub(15, 15), "4")
assert.is_true(is_magic_char(uuid:sub(20, 20)))
end
end)
end)
Loading…
Cancel
Save