Initial commit

This commit is contained in:
2026-09-25 15:29:54 +01:00
parent ce21e350a1
commit bd53601d8c
37 changed files with 5029 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
local SQ3 = require("lua-ljsqlite3/init")
local DataStorage = require("datastorage")
local logger = require("logger")
local db_location = DataStorage:getSettingsDir() .. "/statistics.sqlite3"
local Reader = {}
local OVERLAP_SECONDS = 24 * 60 * 60
function Reader.bookData()
local conn = SQ3.open(db_location)
local sql = "SELECT id,title,authors,notes,last_open,highlights,pages,series,language,md5,total_read_time,total_read_pages FROM book"
local result, rows = conn:exec(sql)
local books = {}
for i = 1, rows do
table.insert(books, {
id = tonumber(result[1][i]), title = result[2][i], authors = result[3][i],
notes = tonumber(result[4][i]), last_open = tonumber(result[5][i]), highlights = tonumber(result[6][i]),
pages = tonumber(result[7][i]), series = result[8][i], language = result[9][i], md5 = result[10][i],
total_read_time = tonumber(result[11][i]), total_read_pages = tonumber(result[12][i]),
})
end
conn:close()
return books
end
local function md5_by_id(books)
local map = {}
for _, b in ipairs(books) do map[b.id] = b.md5 end
return map
end
local function flush_statistics()
local ok, ReaderUI = pcall(require, "apps/reader/readerui")
if ok and ReaderUI and ReaderUI.instance and ReaderUI.instance.statistics and ReaderUI.instance.statistics.is_doc then
pcall(function() ReaderUI.instance.statistics:insertDB() end)
end
end
-- The first sync sends the complete statistics history. Successful syncs store a
-- high-water mark on the reader; later syncs only re-send a one-day overlap.
-- kovi de-duplicates identical page_stat_data events, so the overlap protects
-- against clock/order edge cases without double-counting reading time.
function Reader.progressData(sync_cursor, books)
flush_statistics()
books = books or Reader.bookData()
local map = md5_by_id(books)
local cursor = math.max(0, tonumber(sync_cursor) or 0)
local threshold = math.max(0, cursor - OVERLAP_SECONDS)
local conn = SQ3.open(db_location)
local sql = "SELECT id_book,page,start_time,duration,total_pages FROM page_stat_data"
if threshold > 0 then sql = sql .. " WHERE start_time >= " .. tostring(math.floor(threshold)) end
local result, rows = conn:exec(sql)
local stats = {}
local device_id = G_reader_settings:readSetting("device_id")
for i = 1, rows do
local md5 = map[tonumber(result[1][i])]
if md5 then
table.insert(stats, {
book_md5 = md5, page = tonumber(result[2][i]), start_time = tonumber(result[3][i]),
duration = tonumber(result[4][i]), total_pages = tonumber(result[5][i]), device_id = device_id,
})
else
logger.warn("[kovi] statistic row references unknown book")
end
end
conn:close()
return stats
end
return Reader