2
0
mirror of https://github.com/koreader/koreader synced 2024-11-13 19:11:25 +00:00
koreader/plugins/goodreads.koplugin/goodreadsapi.lua
NiLuJe 2f9db25969
Unify LuaSocket usage (#7405)
* Add a new socketutil module with a few helper functions that allow us to:
  * Always use a sane User-Agent (previously, only Wikipedia did so)
  * Set timeouts in an almost sane manner. Doing it explicitly prevents an interaction with KOSync that does crazy stuff I don't even want to try to understand.
* Unified said timeouts based on the request's intended usage (except for Wikipedia, which already had meaningful timeout values).
* Stopped using LuaSec directly, LuaSocket defers to LuaSec sanely on its own. Everything now transparently supports HTTPS without code duplication.
2021-03-15 01:25:10 +01:00

191 lines
6.0 KiB
Lua

local InputContainer = require("ui/widget/container/inputcontainer")
local GoodreadsBook = require("goodreadsbook")
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local http = require("socket.http")
local ltn12 = require("ltn12")
local socket = require("socket")
local socketutil = require("socketutil")
local _ = require("gettext")
local GoodreadsApi = InputContainer:new {
goodreads_key = "",
goodreads_secret = "",
total_result = 0,
}
function GoodreadsApi:init()
end
local function genSearchURL(text_search, userApi, search_type, npage)
if (text_search) then
text_search = string.gsub (text_search, "\n", "\r\n")
text_search = string.gsub (text_search, "([^%w %-%_%.%~])",
function (c) return string.format ("%%%02X", string.byte(c)) end)
text_search = string.gsub (text_search, " ", "+")
end
return (string.format(
"https://www.goodreads.com/search?q=%s&search[field]=%s&format=xml&key=%s&page=%s",
text_search,
search_type,
userApi,
npage
))
end
local function genIdUrl(id, userApi)
return (string.format(
"https://www.goodreads.com/book/show/%s?format=xml&key=%s",
id,
userApi
))
end
function GoodreadsApi:fetchXml(s_url)
local sink = {}
socketutil:set_timeout()
local request = {
url = s_url,
method = "GET",
sink = ltn12.sink.table(sink),
}
local headers = socket.skip(2, http.request(request))
socketutil:reset_timeout()
if headers == nil then
return nil
end
local xml = table.concat(sink)
if xml ~= "" then
return xml
end
end
function GoodreadsApi:showSearchTable(data)
local books = {}
if data == nil then
UIManager:show(InfoMessage:new{text =_("Network problem.\nCheck connection.")})
return books
end
self.total_result = data:match("<total[-]results>(.*)</total[-]results>")
for work in data:gmatch("<work>(.-)</work>") do
local book = work:match("<best_book[^>]+>(.*)</best_book>")
local id = book:match("<id[^>]+>([^<]+)</id>")
local title = book:match("<title>([^<]+)</title>"):gsub(" %(.*#%d+%)$", "")
local author = book:match("<name>([^<]+)</name>")
table.insert(books, {
author = author,
title = title,
id = id,
})
end
if #books == 0 then
UIManager:show(InfoMessage:new{text =_("Search not found!")})
end
return books
end
function GoodreadsApi:getTotalResults()
return self.total_result
end
local function showIdTable(data)
if data == nil then
UIManager:show(InfoMessage:new{text =_("Network problem.\nCheck connection.")})
return {}
end
local data1 = data:match("<book>(.*)</reviews_widget>")
local title_all = data1:match("<title>(.*)</title>"):gsub("<![[]CDATA[[]", ""):gsub("]]>$", "")
local title = title_all:gsub(" %(.*#%d+%)$", "")
local average_rating = data1:match("<average_rating>([^<]+)</average_rating>")
local series = title_all:match("%(.*#%d+%)$")
if series ~= nil then
series = series:match("[(](.*)[)]")
else
series = _("N/A")
end
local num_pages = data1:match("<num_pages>(.*)</num_pages>"):gsub("<![[]CDATA[[]", ""):gsub("]]>$", "")
if num_pages == nil or num_pages =="" then
num_pages = _("N/A")
end
local id = data1:match("<id>([^<]+)</id>"):gsub("<![[]CDATA[[]", ""):gsub("]]>$", "")
local author = data1:match("<name>([^<]+)</name>")
local description = data1:match("<description>(.*)</description>")
description = string.gsub(description, "<!%[CDATA%[(.*)%]%]>", "%1")
description = string.gsub(description, "%-%-", "%—")
--change format from medium to large
local image = data1:match("<image_url>([^<]+)</image_url>"):gsub("([0-9]+)m/", "%1l/")
local day = data1:match("<original_publication_day[^>]+>([^<]+)</original_publication_day>")
local month = data1:match("<original_publication_month[^>]+>([^<]+)</original_publication_month>")
local year = data1:match("<original_publication_year[^>]+>([^<]+)</original_publication_year>")
local release = {}
if (year) then
table.insert(release, year)
end
if (month) then
table.insert(release, string.format("%02d", month))
end
if (day) then
table.insert(release, string.format("%02d", day))
end
release = table.concat(release, "-")
if release == "" then
release = _("N/A")
end
local book_info = {
title = title,
author = author,
series = series,
rating = average_rating,
pages = num_pages,
release = release,
description = description,
image = image,
id = id,
}
if id == nil then
UIManager:show(InfoMessage:new{text = _("Search not found!")})
end
return book_info
end
-- search_type = all - search all
-- search_type = author - serch book by author
-- search_type = title - search book by title
function GoodreadsApi:showData(search_text, search_type, page, goodreads_key)
local stats = {}
local gen_url = genSearchURL(search_text, goodreads_key, search_type, page)
local gen_xml = self:fetchXml(gen_url)
local tbl = self:showSearchTable(gen_xml)
if #tbl == 0 then
return nil
end
for _, v in pairs(tbl) do
local author = v.author
local title = v.title
local id = v.id
table.insert(stats, { author,
title,
callback = function()
local dates = self:showIdData(id, goodreads_key)
if dates.id ~= nil then
UIManager:show(GoodreadsBook:new{
dates = dates,
})
end
end,
})
end
return stats
end
function GoodreadsApi:showIdData(id, goodreads_key)
local gen_url = genIdUrl(id, goodreads_key)
local gen_xml = self:fetchXml(gen_url)
local tbl = showIdTable(gen_xml)
return tbl
end
return GoodreadsApi