(OPDS) Progess Sync Support for Kavita & Various Refactor Fixes (#9750)

reviewable/pr9777/r1
Dylan Calvin 2 years ago committed by GitHub
parent 678d0c911c
commit c5cd87f09a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,14 +4,13 @@ local Cache = require("cache")
local ConfirmBox = require("ui/widget/confirmbox")
local DocumentRegistry = require("document/documentregistry")
local Font = require("ui/font")
local ImageViewer = require("ui/widget/imageviewer")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
local MultiInputDialog = require("ui/widget/multiinputdialog")
local NetworkMgr = require("ui/network/manager")
local OPDSParser = require("opdsparser")
local RenderImage = require("ui/renderimage")
local OPDSPSE = require("opdspse")
local Screen = require("device").screen
local UIManager = require("ui/uimanager")
local http = require("socket.http")
@ -600,14 +599,14 @@ function OPDSBrowser:showDownloads(item)
{
text = _("Page stream") .. "\u{2B0C}", -- append LEFT RIGHT BLACK ARROW
callback = function()
self:streamPages(item, acquisition.href, acquisition.count)
OPDSPSE:streamPages(acquisition.href, acquisition.count, false, self.root_catalog_username, self.root_catalog_password)
UIManager:close(self.download_dialog)
end,
},
{
text = _("Stream from page") .. "\u{2B0C}", -- append LEFT RIGHT BLACK ARROW
callback = function()
self:streamPages(item, acquisition.href, acquisition.count, true)
OPDSPSE:streamPages(acquisition.href, acquisition.count, true, self.root_catalog_username, self.root_catalog_password)
UIManager:close(self.download_dialog)
end,
},
@ -808,102 +807,6 @@ function OPDSBrowser:downloadFile(filename, remote_url)
end
end
-- Streams a book (OPDS-PSE Page Streaming Extension)
function OPDSBrowser:streamPages(remote_url, count, continue)
local page_table = {image_disposable = true}
setmetatable(page_table, {__index = function (_, key)
if type(key) ~= "number" then
local error_bb = RenderImage:renderImageFile("resources/koreader.png", false)
return error_bb
else
local index = key - 1
local page_url = remote_url:gsub("{pageNumber}", tostring(index))
page_url = page_url:gsub("{maxWidth}", tostring(Screen:getWidth()))
local page_data = {}
logger.dbg("Streaming page from", page_url)
local parsed = url.parse(page_url)
local code, headers, status
if parsed.scheme == "http" or parsed.scheme == "https" then
socketutil:set_timeout(socketutil.FILE_BLOCK_TIMEOUT, socketutil.FILE_TOTAL_TIMEOUT)
code, headers, status = socket.skip(1, http.request {
url = page_url,
headers = {
["Accept-Encoding"] = "identity",
},
sink = ltn12.sink.table(page_data),
user = self.root_catalog_username,
password = self.root_catalog_password,
})
socketutil:reset_timeout()
else
UIManager:show(InfoMessage:new {
text = T(_("Invalid protocol:\n%1"), parsed.scheme),
})
end
local page_bb
if code == 200 then
local data = table.concat(page_data)
page_bb = RenderImage:renderImageData(data, #data, false)
or RenderImage:renderImageFile("resources/koreader.png", false)
else
logger.dbg("OPDSBrowser:streamPages: Request failed:", status or code)
logger.dbg("OPDSBrowser:streamPages: Response headers:", headers)
page_bb = RenderImage:renderImageFile("resources/koreader.png", false)
end
return page_bb
end
end})
local viewer = ImageViewer:new{
image = page_table,
fullscreen = true,
with_title_bar = false,
image_disposable = false, -- instead set page_table image_disposable to true
}
-- in Lua 5.2 we could override __len, but this works too
viewer._images_list_nb = count
UIManager:show(viewer)
if continue then
self:jumpToPage(viewer)
end
end
-- Shows a page number dialog for page streaming
function OPDSBrowser:jumpToPage(viewer)
local dialog
dialog = InputDialog:new{
title = _("Enter page number"),
input_type = "number",
input_hint = "(1 - " .. viewer._images_list_nb .. ")",
buttons = {
{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(dialog)
end,
},
{
text = _("Stream"),
is_enter_default = true,
callback = function()
local page_num = dialog:getInputValue()
if page_num then
UIManager:close(dialog)
viewer:switchToImageNum(math.min(math.max(1, page_num), viewer._images_list_nb))
end
end,
},
},
},
}
UIManager:show(dialog)
dialog:onShowKeyboard()
end
-- Menu action on item tap (Download a book / Show subcatalog / Search in catalog)
function OPDSBrowser:onMenuSelect(item)
if item.acquisitions and item.acquisitions[1] then -- book

@ -0,0 +1,212 @@
local http = require("socket.http")
local ImageViewer = require("ui/widget/imageviewer")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local logger = require("logger")
local ltn12 = require("ltn12")
local RenderImage = require("ui/renderimage")
local Screen = require("device").screen
local socket = require("socket")
local socketutil = require("socketutil")
local UIManager = require("ui/uimanager")
local url = require("socket.url")
local _ = require("gettext")
local T = require("ffi/util").template
local OPDSPSE = {}
-- This function attempts to pull chapter progress from Kavita.
function OPDSPSE:getLastPage(remote_url, username, password)
local last_page = 0
-- create URL's and reference vars
local chapter = string.match(remote_url, "chapterId=(%w+)")
local api_key = string.match(remote_url, "opds/(.+)/image")
local progress_url = string.match(remote_url, "(.+)/api").."/api/Reader/get-progress?chapterId="..chapter
local auth_url = string.match(remote_url, "(.+)/api").."/api/Plugin/authenticate?apiKey="..api_key.."&pluginName=KOReader-OPDS"
-- Do an HTTP POST to get the Bearer Token for authentication of the /api/Reader/get-progress endpoint
local auth_parsed = url.parse(auth_url)
local auth_data = {}
local auth_code, auth_headers, auth_status
if auth_parsed.scheme == "http" or auth_parsed.scheme == "https" then
socketutil:set_timeout(socketutil.FILE_BLOCK_TIMEOUT, socketutil.FILE_TOTAL_TIMEOUT)
auth_code, auth_headers, auth_status = socket.skip(1, http.request {
method = "POST",
url = auth_url,
headers = {
["Accept-Encoding"] = "identity",
["Authentication"] = api_key,
},
sink = ltn12.sink.table(auth_data),
user = username,
password = password,
})
socketutil:reset_timeout()
else
UIManager:show(InfoMessage:new {
text = T(_("Invalid protocol:\n%1"), auth_parsed.scheme),
})
end
if auth_code == 200 then
-- if http request for bearer token was successful, pull bearer token from response and
-- attempt to pull progress for chapterId in remote_url
local bearer_token = auth_data[1]:match("\"token\":\"(.+)\",\"refresh")
-- Do HTTP GET request for chapter progress
local progress_parsed = url.parse(progress_url)
local progress_data = {}
local progress_code, progress_headers, progress_status
if progress_parsed.scheme == "http" or progress_parsed.scheme == "https" then
socketutil:set_timeout(socketutil.FILE_BLOCK_TIMEOUT, socketutil.FILE_TOTAL_TIMEOUT)
progress_code, progress_headers, progress_status = socket.skip(1, http.request {
url = progress_url,
headers = {
["Accept-Encoding"] = "identity",
["Authorization"] = "Bearer "..bearer_token,
},
sink = ltn12.sink.table(progress_data),
user = username,
password = password,
})
socketutil:reset_timeout()
else
UIManager:show(InfoMessage:new {
text = T(_("Invalid protocol:\n%1"), progress_parsed.scheme),
})
end
if progress_code == 200 then
-- if HTTP GET was successful, pull page number from response
last_page = progress_data[1]:match("\"pageNum\":(.+),\"seriesId")
else
logger.dbg("OPDSPSE:getLastPage: Progress Request failed:", progress_status or progress_code)
logger.dbg("OPDSPSE:getLastPage: Progress Response headers:", progress_headers)
end
else
logger.dbg("OPDSPSE:getLastPage: Authentication Request failed:", auth_status or auth_code)
logger.dbg("OPDSPSE:getLastPage: Authentication Response headers:", auth_headers)
end
-- returns page number. If the HTTP Requests were unsuccessful, defaults to 0.
return last_page;
end
function OPDSPSE:streamPages(remote_url, count, continue, username, password)
-- attempt to pull chapter progress from Kavita if user pressed
-- "Page Stream" button.
-- We have to pull the progress here, otherwise the creation of the page_table
-- will overwrite the book progress before we pull it, making it always 0.
local ok, last_page = pcall(function() return self:getLastPage(remote_url, username, password) end)
if not ok then
logger.warn("Couldn't pull progress, defaulting to Page 0.")
last_page = 0
end
local page_table = {image_disposable = true}
setmetatable(page_table, {__index = function (_, key)
if type(key) ~= "number" then
local error_bb = RenderImage:renderImageFile("resources/koreader.png", false)
return error_bb
else
local index = key - 1
local page_url = remote_url:gsub("{pageNumber}", tostring(index))
page_url = page_url:gsub("{maxWidth}", tostring(Screen:getWidth()))
local page_data = {}
logger.dbg("Streaming page from", page_url)
local parsed = url.parse(page_url)
local code, headers, status
if parsed.scheme == "http" or parsed.scheme == "https" then
socketutil:set_timeout(socketutil.FILE_BLOCK_TIMEOUT, socketutil.FILE_TOTAL_TIMEOUT)
code, headers, status = socket.skip(1, http.request {
url = page_url,
headers = {
["Accept-Encoding"] = "identity",
},
sink = ltn12.sink.table(page_data),
user = username,
password = password,
})
socketutil:reset_timeout()
else
UIManager:show(InfoMessage:new {
text = T(_("Invalid protocol:\n%1"), parsed.scheme),
})
end
local data = table.concat(page_data)
if code == 200 then
local page_bb = RenderImage:renderImageData(data, #data, false)
or RenderImage:renderImageFile("resources/koreader.png", false)
return page_bb
else
logger.dbg("OPDSBrowser:streamPages: Request failed:", status or code)
logger.dbg("OPDSBrowser:streamPages: Response headers:", headers)
local error_bb = RenderImage:renderImageFile("resources/koreader.png", false)
return error_bb
end
end
end})
local viewer = ImageViewer:new{
image = page_table,
fullscreen = true,
with_title_bar = false,
image_disposable = false, -- instead set page_table image_disposable to true
}
-- in Lua 5.2 we could override __len, but this works too
viewer._images_list_nb = count
UIManager:show(viewer)
if continue then
self:jumpToPage(viewer, count)
else
-- add 1 since Kavita's Page count is zero based
-- and ImageViewer is not.
viewer:switchToImageNum(last_page+1)
end
end
-- Shows a page number dialog for page streaming.
function OPDSPSE:jumpToPage(viewer, count)
local input_dialog
input_dialog = InputDialog:new{
title = _("Enter page number"),
input = "",
input_type = "number",
input_hint = "(" .. "1 - " .. count .. ")",
buttons = {
{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(input_dialog)
end,
},
{
text = _("Stream"),
is_enter_default = true,
callback = function()
local page_num = input_dialog:getInputValue()
if page_num then
UIManager:close(input_dialog)
viewer:switchToImageNum(math.min(math.max(1, page_num), count))
end
end,
},
}
},
}
UIManager:show(input_dialog)
input_dialog:onShowKeyboard()
end
return OPDSPSE
Loading…
Cancel
Save