You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koreader/plugins/opds.koplugin/opdspse.lua

204 lines
8.4 KiB
Lua

local http = require("socket.http")
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 ImageViewer = require("ui/widget/imageviewer")
local viewer = ImageViewer:new{
image = page_table,
fullscreen = true,
with_title_bar = false,
image_disposable = false, -- instead set page_table image_disposable to true
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_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