2020-01-04 00:18:51 +00:00
local BD = require ( " ui/bidi " )
2023-06-09 18:58:50 +00:00
local ButtonDialog = require ( " ui/widget/buttondialog " )
2017-04-02 14:17:49 +00:00
local Cache = require ( " cache " )
2018-01-14 19:19:45 +00:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2021-01-17 08:22:48 +00:00
local DocumentRegistry = require ( " document/documentregistry " )
2021-06-04 16:45:08 +00:00
local Font = require ( " ui/font " )
2014-09-05 13:02:13 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
2021-12-07 13:06:37 +00:00
local InputDialog = require ( " ui/widget/inputdialog " )
2014-09-05 13:02:13 +00:00
local Menu = require ( " ui/widget/menu " )
2017-04-02 14:17:49 +00:00
local MultiInputDialog = require ( " ui/widget/multiinputdialog " )
local NetworkMgr = require ( " ui/network/manager " )
2021-02-06 18:09:31 +00:00
local OPDSParser = require ( " opdsparser " )
2022-11-12 06:27:57 +00:00
local OPDSPSE = require ( " opdspse " )
2014-10-30 18:42:18 +00:00
local Screen = require ( " device " ) . screen
2017-04-02 14:17:49 +00:00
local UIManager = require ( " ui/uimanager " )
2021-03-15 00:25:10 +00:00
local http = require ( " socket.http " )
2018-01-14 19:19:45 +00:00
local lfs = require ( " libs/libkoreader-lfs " )
2017-04-02 14:17:49 +00:00
local logger = require ( " logger " )
2021-03-15 00:25:10 +00:00
local ltn12 = require ( " ltn12 " )
local socket = require ( " socket " )
local socketutil = require ( " socketutil " )
local url = require ( " socket.url " )
2017-04-02 14:17:49 +00:00
local util = require ( " util " )
2018-08-11 20:30:10 +00:00
local _ = require ( " gettext " )
2017-04-02 14:17:49 +00:00
local T = require ( " ffi/util " ) . template
2014-09-05 13:02:13 +00:00
-- cache catalog parsed from feed xml
local CatalogCache = Cache : new {
2021-05-07 01:59:27 +00:00
-- Make it 20 slots, with no storage space constraints
slots = 20 ,
2014-09-05 13:02:13 +00:00
}
local OPDSBrowser = Menu : extend {
2021-03-06 21:44:18 +00:00
opds_servers = G_reader_settings : readSetting ( " opds_servers " , {
{
title = " Project Gutenberg " ,
url = " https://m.gutenberg.org/ebooks.opds/?format=opds " ,
} ,
2022-01-04 20:24:21 +00:00
{
title = " Standard Ebooks " ,
2022-08-19 17:20:34 +00:00
url = " https://standardebooks.org/feeds/opds " ,
2022-01-04 20:24:21 +00:00
} ,
2021-03-06 21:44:18 +00:00
{
title = " Feedbooks " ,
url = " https://catalog.feedbooks.com/catalog/public_domain.atom " ,
} ,
{
title = " ManyBooks " ,
url = " http://manybooks.net/opds/index.php " ,
} ,
{
title = " Internet Archive " ,
url = " https://bookserver.archive.org/ " ,
} ,
{
title = " textos.info (Spanish) " ,
url = " https://www.textos.info/catalogo.atom " ,
} ,
{
title = " Gallica (French) " ,
url = " https://gallica.bnf.fr/opds " ,
} ,
} ) ,
2020-02-04 21:03:22 +00:00
calibre_name = _ ( " Local calibre library " ) ,
2021-03-06 21:44:18 +00:00
calibre_opds = G_reader_settings : readSetting ( " calibre_opds " , { } ) ,
2014-09-10 05:26:50 +00:00
2022-10-29 17:06:42 +00:00
catalog_type = " application/atom%+xml " ,
search_type = " application/opensearchdescription%+xml " ,
2021-03-06 18:29:15 +00:00
search_template_type = " application/atom%+xml " ,
2022-10-29 17:06:42 +00:00
acquisition_rel = " ^http://opds%-spec%.org/acquisition " ,
2023-06-09 18:58:50 +00:00
borrow_rel = " http://opds-spec.org/acquisition/borrow " ,
2022-10-29 17:06:42 +00:00
image_rel = " http://opds-spec.org/image " ,
2023-06-09 18:58:50 +00:00
image_rel_alt = " http://opds-spec.org/cover " , -- ManyBooks.net, not in spec
2022-10-29 17:06:42 +00:00
thumbnail_rel = " http://opds-spec.org/image/thumbnail " ,
2023-06-09 18:58:50 +00:00
thumbnail_rel_alt = " http://opds-spec.org/thumbnail " , -- ManyBooks.net, not in spec
2022-10-29 17:06:42 +00:00
stream_rel = " http://vaemendis.net/opds-pse/stream " ,
root_catalog_title = nil ,
root_catalog_username = nil ,
root_catalog_password = nil ,
2014-09-05 13:02:13 +00:00
width = Screen : getWidth ( ) ,
height = Screen : getHeight ( ) ,
2022-10-29 17:06:42 +00:00
title_shrink_font_to_fit = true ,
2014-09-05 13:02:13 +00:00
}
function OPDSBrowser : init ( )
2017-01-22 01:00:02 +00:00
self.item_table = self : genItemTableFromRoot ( )
2020-12-23 13:35:54 +00:00
self.catalog_title = nil
2022-01-12 16:54:55 +00:00
self.title_bar_left_icon = " plus "
self.onLeftButtonTap = function ( )
2022-10-29 17:06:42 +00:00
self : addEditCatalog ( )
2022-01-12 16:54:55 +00:00
end
2014-09-05 13:02:13 +00:00
Menu.init ( self ) -- call parent's init()
end
2022-10-29 17:06:42 +00:00
-- Builds the root list of catalogs
function OPDSBrowser : genItemTableFromRoot ( )
local item_table = {
{ -- calibre is the first and non-deletable item
text = self.calibre_name ,
2022-10-31 13:07:37 +00:00
url = self.calibre_opds . host and self.calibre_opds . port and
string.format ( " http://%s:%d/opds " , self.calibre_opds . host , self.calibre_opds . port ) ,
2022-10-29 17:06:42 +00:00
username = self.calibre_opds . username ,
password = self.calibre_opds . password ,
searchable = false ,
} ,
2020-02-07 12:27:04 +00:00
}
2022-10-29 17:06:42 +00:00
for _ , server in ipairs ( self.opds_servers ) do
table.insert ( item_table , {
text = server.title ,
url = server.url ,
username = server.username ,
password = server.password ,
searchable = server.url : match ( " %%s " ) and true or false ,
} )
end
return item_table
2014-09-05 13:02:13 +00:00
end
2022-10-29 17:06:42 +00:00
-- Shows dialog to edit properties of the new/existing catalog
function OPDSBrowser : addEditCatalog ( item , is_calibre )
local title
local fields = { { } , { } , { } , { } }
if is_calibre then
title = _ ( " Edit local calibre host and port " )
fields [ 1 ] . text = self.calibre_opds . host or " 192.168.1.1 "
fields [ 1 ] . hint = _ ( " calibre host " )
fields [ 2 ] . text = self.calibre_opds . port and tostring ( self.calibre_opds . port ) or " 8080 "
fields [ 2 ] . hint = _ ( " calibre port " )
fields [ 3 ] . text = self.calibre_opds . username
fields [ 4 ] . text = self.calibre_opds . password
2021-03-02 16:58:43 +00:00
else
2022-10-29 17:06:42 +00:00
fields [ 1 ] . hint = _ ( " Catalog name " )
fields [ 2 ] . hint = _ ( " Catalog URL " )
if item then
title = _ ( " Edit OPDS catalog " )
fields [ 1 ] . text = item.text
fields [ 2 ] . text = item.url
fields [ 3 ] . text = item.username
fields [ 4 ] . text = item.password
else
title = _ ( " Add OPDS catalog " )
end
2018-09-29 21:17:17 +00:00
end
2022-10-29 17:06:42 +00:00
fields [ 3 ] . hint = _ ( " Username (optional) " )
fields [ 4 ] . hint = _ ( " Password (optional) " )
fields [ 4 ] . text_type = " password "
2014-09-10 05:26:50 +00:00
2022-10-29 17:06:42 +00:00
local dialog
dialog = MultiInputDialog : new {
title = title ,
fields = fields ,
2014-09-05 13:02:13 +00:00
buttons = {
{
{
2018-08-11 20:30:10 +00:00
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2014-09-05 13:02:13 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
2014-09-05 13:02:13 +00:00
end
} ,
{
2022-10-29 17:06:42 +00:00
text = _ ( " Save " ) ,
2014-09-05 13:02:13 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
local dialog_fields = dialog : getFields ( )
if is_calibre then
self : editCalibreFromInput ( dialog_fields )
else
self : editCatalogFromInput ( dialog_fields , item )
end
UIManager : close ( dialog )
2014-09-05 13:02:13 +00:00
end
} ,
} ,
} ,
}
2022-10-29 17:06:42 +00:00
UIManager : show ( dialog )
dialog : onShowKeyboard ( )
2014-09-05 13:02:13 +00:00
end
2022-10-29 17:06:42 +00:00
-- Shows dialog to add a subcatalog to the root list
function OPDSBrowser : addSubCatalog ( item_url )
local dialog
dialog = InputDialog : new {
title = _ ( " Add OPDS catalog " ) ,
input = self.root_catalog_title .. " - " .. self.catalog_title ,
2014-09-10 05:26:50 +00:00
buttons = {
{
{
2018-08-11 20:30:10 +00:00
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2014-09-10 05:26:50 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
end ,
2014-09-10 05:26:50 +00:00
} ,
{
2022-10-29 17:06:42 +00:00
text = _ ( " Save " ) ,
is_enter_default = true ,
2014-09-10 05:26:50 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
local name = dialog : getInputText ( )
if name ~= " " then
UIManager : close ( dialog )
local fields = { name , item_url , self.root_catalog_username , self.root_catalog_password }
self : editCatalogFromInput ( fields , false , true ) -- no init, stay in the subcatalog
end
end ,
2014-09-10 05:26:50 +00:00
} ,
} ,
} ,
}
2022-10-29 17:06:42 +00:00
UIManager : show ( dialog )
dialog : onShowKeyboard ( )
2014-09-10 05:26:50 +00:00
end
2022-10-29 17:06:42 +00:00
-- Saves catalog properties from input dialog
function OPDSBrowser : editCatalogFromInput ( fields , item , no_init )
local new_server
if item then -- edit old
for _ , server in ipairs ( self.opds_servers ) do
if server.title == item.text and server.url == item.url then
new_server = server
break
end
end
else -- add new
new_server = { }
2014-09-05 13:02:13 +00:00
end
2022-10-29 17:06:42 +00:00
new_server.title = fields [ 1 ]
new_server.url = fields [ 2 ] : match ( " ^%a+:// " ) and fields [ 2 ] or " http:// " .. fields [ 2 ]
new_server.username = fields [ 3 ] ~= " " and fields [ 3 ] or nil
new_server.password = fields [ 4 ]
if not item then
table.insert ( self.opds_servers , new_server )
end
if not no_init then
self : init ( )
2014-09-10 05:26:50 +00:00
end
2014-09-05 13:02:13 +00:00
end
2022-10-29 17:06:42 +00:00
-- Saves calibre properties from input dialog
function OPDSBrowser : editCalibreFromInput ( fields )
self.calibre_opds . host = fields [ 1 ]
if tonumber ( fields [ 2 ] ) then
self.calibre_opds . port = fields [ 2 ]
end
self.calibre_opds . username = fields [ 3 ] ~= " " and fields [ 3 ] or nil
self.calibre_opds . password = fields [ 4 ]
self : init ( )
end
-- Deletes catalog from the root list
function OPDSBrowser : deleteCatalog ( item )
for i , server in ipairs ( self.opds_servers ) do
if server.title == item.text and server.url == item.url then
table.remove ( self.opds_servers , i )
break
end
end
self : init ( )
end
-- Fetches feed from server
function OPDSBrowser : fetchFeed ( item_url , headers_only )
2021-03-02 16:58:43 +00:00
local sink = { }
2022-10-29 17:06:42 +00:00
socketutil : set_timeout ( socketutil.LARGE_BLOCK_TIMEOUT , socketutil.LARGE_TOTAL_TIMEOUT )
2021-03-02 16:58:43 +00:00
local request = {
url = item_url ,
2022-10-29 17:06:42 +00:00
method = headers_only and " HEAD " or " GET " ,
2021-09-13 06:30:43 +00:00
-- Explicitly specify that we don't support compressed content.
-- Some servers will still break RFC2616 14.3 and send crap instead.
2021-03-15 00:25:10 +00:00
headers = {
[ " Accept-Encoding " ] = " identity " ,
} ,
2021-03-02 16:58:43 +00:00
sink = ltn12.sink . table ( sink ) ,
2022-10-29 17:06:42 +00:00
user = self.root_catalog_username ,
password = self.root_catalog_password ,
2021-03-02 16:58:43 +00:00
}
2022-02-17 09:53:15 +00:00
logger.dbg ( " Request: " , request )
2022-09-16 22:08:00 +00:00
local code , headers , status = socket.skip ( 1 , http.request ( request ) )
2021-03-15 00:25:10 +00:00
socketutil : reset_timeout ( )
2022-10-29 17:06:42 +00:00
if headers_only then
2022-11-25 13:48:54 +00:00
return headers and headers [ " last-modified " ]
2022-10-29 17:06:42 +00:00
end
2018-09-29 21:17:17 +00:00
if code == 200 then
2015-09-14 16:59:00 +00:00
local xml = table.concat ( sink )
2022-10-29 17:06:42 +00:00
return xml ~= " " and xml
end
local text , icon
2022-11-25 13:48:54 +00:00
if headers and code == 301 then
2022-10-29 17:06:42 +00:00
text = T ( _ ( " The catalog has been permanently moved. Please update catalog URL to '%1'. " ) , BD.url ( headers.location ) )
2022-11-25 13:48:54 +00:00
elseif headers and code == 302
2021-09-13 06:30:43 +00:00
and item_url : match ( " ^https " )
2022-10-29 17:06:42 +00:00
and headers.location : match ( " ^http[^s] " ) then
text = T ( _ ( " Insecure HTTPS → HTTP downgrade attempted by redirect from: \n \n '%1' \n \n to \n \n '%2'. \n \n Please inform the server administrator that many clients disallow this because it could be a downgrade attack. " ) ,
BD.url ( item_url ) , BD.url ( headers.location ) )
icon = " notice-warning "
2018-09-29 21:17:17 +00:00
else
2022-10-29 17:06:42 +00:00
local error_message = {
[ " 401 " ] = _ ( " Authentication required for catalog. Please add a username and password. " ) ,
[ " 403 " ] = _ ( " Failed to authenticate. Please check your username and password. " ) ,
[ " 404 " ] = _ ( " Catalog not found. " ) ,
[ " 406 " ] = _ ( " Cannot get catalog. Server refuses to serve uncompressed content. " ) ,
}
2022-11-25 13:48:54 +00:00
text = code and error_message [ tostring ( code ) ] or T ( _ ( " Cannot get catalog. Server response status: %1. " ) , status or code )
2014-09-05 13:02:13 +00:00
end
2022-10-29 17:06:42 +00:00
UIManager : show ( InfoMessage : new {
text = text ,
icon = icon ,
} )
2014-09-05 13:02:13 +00:00
end
2022-10-29 17:06:42 +00:00
-- Parses feed to catalog
function OPDSBrowser : parseFeed ( item_url )
local feed_last_modified = self : fetchFeed ( item_url , true ) -- headers only
2022-11-25 13:48:54 +00:00
local feed
2019-04-05 06:50:33 +00:00
if feed_last_modified then
2022-11-25 13:48:54 +00:00
local hash = " opds|catalog| " .. item_url .. " | " .. feed_last_modified
feed = CatalogCache : check ( hash )
2014-09-05 13:02:13 +00:00
if feed then
2022-11-25 13:48:54 +00:00
logger.dbg ( " Cache hit for " , hash )
else
logger.dbg ( " Cache miss for " , hash )
feed = self : fetchFeed ( item_url )
if feed then
logger.dbg ( " Caching " , hash )
CatalogCache : insert ( hash , feed )
end
2014-09-05 13:02:13 +00:00
end
2022-11-25 13:48:54 +00:00
else
feed = self : fetchFeed ( item_url )
2014-09-05 13:02:13 +00:00
end
if feed then
return OPDSParser : parse ( feed )
end
end
2022-10-29 17:06:42 +00:00
-- Generates link to search in catalog
function OPDSBrowser : getSearchTemplate ( osd_url )
2021-03-06 18:29:15 +00:00
-- parse search descriptor
2022-10-29 17:06:42 +00:00
local search_descriptor = self : parseFeed ( osd_url )
2021-03-06 18:29:15 +00:00
if search_descriptor and search_descriptor.OpenSearchDescription and search_descriptor.OpenSearchDescription . Url then
for _ , candidate in ipairs ( search_descriptor.OpenSearchDescription . Url ) do
if candidate.type and candidate.template and candidate.type : find ( self.search_template_type ) then
2021-03-06 23:05:41 +00:00
return candidate.template : gsub ( " {searchTerms} " , " %%s " )
2021-03-06 18:29:15 +00:00
end
end
end
end
2022-10-29 17:06:42 +00:00
-- Generates menu items from the fetched list of catalog entries
function OPDSBrowser : genItemTableFromURL ( item_url )
local ok , catalog = pcall ( self.parseFeed , self , item_url )
if not ok then
logger.info ( " Cannot get catalog info from " , item_url , catalog )
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Cannot get catalog info from %1 " ) , ( item_url and BD.url ( item_url ) or " nil " ) ) ,
} )
catalog = nil
end
return self : genItemTableFromCatalog ( catalog , item_url )
end
2021-03-06 18:29:15 +00:00
2022-10-29 17:06:42 +00:00
function OPDSBrowser : genItemTableFromCatalog ( catalog , item_url )
2014-11-30 18:06:27 +00:00
local item_table = { }
2016-12-27 10:00:13 +00:00
if not catalog then
return item_table
end
local feed = catalog.feed or catalog
local function build_href ( href )
return url.absolute ( item_url , href )
end
local hrefs = { }
if feed.link then
2022-10-29 17:06:42 +00:00
for __ , link in ipairs ( feed.link ) do
2016-12-27 10:00:13 +00:00
if link.type ~= nil then
2021-03-06 18:29:15 +00:00
if link.type : find ( self.catalog_type ) then
2016-12-27 10:00:13 +00:00
if link.rel and link.href then
hrefs [ link.rel ] = build_href ( link.href )
2014-09-05 13:02:13 +00:00
end
end
2021-03-06 18:29:15 +00:00
if link.type : find ( self.search_type ) then
if link.href then
2022-10-29 17:06:42 +00:00
table.insert ( item_table , { -- the first item in each subcatalog
text = " \u{f002} " .. _ ( " Search " ) , -- append SEARCH icon
url = build_href ( self : getSearchTemplate ( build_href ( link.href ) ) ) ,
searchable = true ,
} )
2021-03-06 18:29:15 +00:00
end
end
2014-09-05 13:02:13 +00:00
end
end
2016-12-27 10:00:13 +00:00
end
item_table.hrefs = hrefs
2023-06-09 18:58:50 +00:00
for _ , entry in ipairs ( feed.entry or { } ) do
2016-12-27 10:00:13 +00:00
local item = { }
item.acquisitions = { }
if entry.link then
2023-06-09 18:58:50 +00:00
for __ , link in ipairs ( entry.link ) do
2022-10-29 17:06:42 +00:00
local link_href = build_href ( link.href )
2021-08-21 13:54:31 +00:00
if link.type and link.type : find ( self.catalog_type )
2016-12-27 10:00:13 +00:00
and ( not link.rel
or link.rel == " subsection "
2019-01-26 15:01:54 +00:00
or link.rel == " http://opds-spec.org/subsection "
2016-12-27 10:00:13 +00:00
or link.rel == " http://opds-spec.org/sort/popular "
or link.rel == " http://opds-spec.org/sort/new " ) then
2022-10-29 17:06:42 +00:00
item.url = link_href
2014-09-05 13:02:13 +00:00
end
2021-09-13 06:30:43 +00:00
-- Some catalogs do not use the rel attribute to denote
-- a publication. Arxiv uses title. Specifically, it uses
-- a title attribute that contains pdf. (title="pdf")
if link.rel or link.title then
2023-06-09 18:58:50 +00:00
if link.rel == self.borrow_rel then
table.insert ( item.acquisitions , {
type = " borrow " ,
} )
elseif link.rel and link.rel : match ( self.acquisition_rel ) then
2016-12-27 10:00:13 +00:00
table.insert ( item.acquisitions , {
2022-10-29 17:06:42 +00:00
type = link.type ,
href = link_href ,
2021-11-14 06:52:53 +00:00
title = link.title ,
2016-12-27 10:00:13 +00:00
} )
2022-03-18 20:27:17 +00:00
elseif link.rel == self.stream_rel then
2022-10-29 17:06:42 +00:00
-- https://vaemendis.net/opds-pse/
-- «count» MUST provide the number of pages of the document
-- namespace may be not "pse"
local count
2022-10-20 14:45:05 +00:00
for k , v in pairs ( link ) do
2022-10-29 17:06:42 +00:00
if k : sub ( - 6 ) == " :count " then
count = tonumber ( v )
2022-10-20 14:45:05 +00:00
break
end
end
2022-10-29 17:06:42 +00:00
if count then
table.insert ( item.acquisitions , {
type = link.type ,
href = link_href ,
title = link.title ,
count = count ,
} )
end
2023-06-09 18:58:50 +00:00
elseif link.rel == self.thumbnail_rel or link.rel == self.thumbnail_rel_alt then
2022-10-29 17:06:42 +00:00
item.thumbnail = link_href
2023-06-09 18:58:50 +00:00
elseif link.rel == self.image_rel or link.rel == self.image_rel_alt then
2022-10-29 17:06:42 +00:00
item.image = link_href
2014-09-05 13:02:13 +00:00
end
2021-09-13 06:30:43 +00:00
-- This statement grabs the catalog items that are
-- indicated by title="pdf" or whose type is
-- "application/pdf"
if link.title == " pdf " or link.type == " application/pdf "
and link.rel ~= " subsection " then
-- Check for the presence of the pdf suffix and add it
-- if it's missing.
local href = link.href
2022-10-29 17:06:42 +00:00
if util.getFileNameSuffix ( href ) ~= " pdf " then
2021-09-13 06:30:43 +00:00
href = href .. " .pdf "
end
table.insert ( item.acquisitions , {
type = link.title ,
href = build_href ( href ) ,
} )
end
2014-09-05 13:02:13 +00:00
end
end
end
2016-12-27 10:00:13 +00:00
local title = " Unknown "
if type ( entry.title ) == " string " then
title = entry.title
elseif type ( entry.title ) == " table " then
if type ( entry.title . type ) == " string " and entry.title . div ~= " " then
title = entry.title . div
end
end
if title == " Unknown " then
2018-09-29 21:17:17 +00:00
logger.info ( " Cannot handle title " , entry.title )
2016-12-27 10:00:13 +00:00
end
2018-10-18 18:12:30 +00:00
item.text = title
2016-12-27 10:00:13 +00:00
local author = " Unknown Author "
if type ( entry.author ) == " table " and entry.author . name then
author = entry.author . name
2020-01-16 11:48:32 +00:00
if type ( author ) == " table " then
if # author > 0 then
author = table.concat ( author , " , " )
else
-- we may get an empty table on https://gallica.bnf.fr/opds
author = nil
end
end
if author then
2020-11-22 03:51:32 +00:00
item.text = title .. " - " .. author
2020-01-16 11:48:32 +00:00
end
2016-12-27 10:00:13 +00:00
end
item.title = title
item.author = author
2022-10-29 17:06:42 +00:00
item.content = entry.content or entry.summary
2016-12-27 10:00:13 +00:00
table.insert ( item_table , item )
2014-09-05 13:02:13 +00:00
end
return item_table
end
2022-10-29 17:06:42 +00:00
-- Requests and shows updated list of catalog entries
function OPDSBrowser : updateCatalog ( item_url , paths_updated )
local menu_table = self : genItemTableFromURL ( item_url )
2014-09-05 13:02:13 +00:00
if # menu_table > 0 then
2022-10-29 17:06:42 +00:00
if not paths_updated then
table.insert ( self.paths , {
url = item_url ,
title = self.catalog_title ,
} )
end
2020-12-23 13:35:54 +00:00
self : switchItemTable ( self.catalog_title , menu_table )
2022-01-12 16:54:55 +00:00
self.onLeftButtonTap = function ( )
2022-10-29 17:06:42 +00:00
self : addSubCatalog ( item_url )
2022-01-12 16:54:55 +00:00
end
2014-11-07 21:08:03 +00:00
if self.page_num <= 1 then
self : onNext ( )
end
2014-09-05 13:02:13 +00:00
end
end
2022-10-29 17:06:42 +00:00
-- Requests and adds more catalog entries to fill out the page
function OPDSBrowser : appendCatalog ( item_url )
local menu_table = self : genItemTableFromURL ( item_url )
if # menu_table > 0 then
for _ , item in ipairs ( menu_table ) do
table.insert ( self.item_table , item )
end
self.item_table . hrefs = menu_table.hrefs
self : switchItemTable ( self.catalog_title , self.item_table , - 1 )
return true
2014-09-05 13:02:13 +00:00
end
end
2022-10-29 17:06:42 +00:00
-- Shows dialog to search in catalog
function OPDSBrowser : searchCatalog ( item_url )
local dialog
dialog = InputDialog : new {
title = _ ( " Search OPDS catalog " ) ,
-- @translators: This is an input hint for something to search for in an OPDS catalog, namely a famous author everyone knows. It probably doesn't need to be localized, but this is just here in case another name or book title would be more appropriate outside of a European context.
input_hint = _ ( " Alexandre Dumas " ) ,
description = _ ( " %s in url will be replaced by your input " ) ,
2022-10-26 09:31:16 +00:00
buttons = {
{
{
text = _ ( " Cancel " ) ,
id = " close " ,
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
2022-10-26 09:31:16 +00:00
end ,
} ,
{
2022-10-29 17:06:42 +00:00
text = _ ( " Search " ) ,
2022-10-26 09:31:16 +00:00
is_enter_default = true ,
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
self.catalog_title = _ ( " Search results " )
local search_str = dialog : getInputText ( ) : gsub ( " " , " + " )
self : updateCatalog ( item_url : gsub ( " %%s " , search_str ) )
2022-10-26 09:31:16 +00:00
end ,
} ,
2022-10-29 17:06:42 +00:00
} ,
2022-10-26 09:31:16 +00:00
} ,
}
2022-10-29 17:06:42 +00:00
UIManager : show ( dialog )
dialog : onShowKeyboard ( )
2022-10-26 09:31:16 +00:00
end
2022-10-29 17:06:42 +00:00
-- Shows dialog to download / stream a book
2014-09-05 13:02:13 +00:00
function OPDSBrowser : showDownloads ( item )
local acquisitions = item.acquisitions
2021-12-07 13:06:37 +00:00
local filename = item.title
if item.author then
filename = item.author .. " - " .. filename
end
2022-02-17 09:53:15 +00:00
local filename_orig = filename
2023-06-09 18:58:50 +00:00
local function createTitle ( path , file ) -- title for ButtonDialog
2022-02-17 09:53:15 +00:00
return T ( _ ( " Download folder: \n %1 \n \n Download filename: \n %2 \n \n Download file type: " ) ,
BD.dirpath ( path ) , file )
end
2021-08-23 10:44:07 +00:00
2023-06-09 18:58:50 +00:00
local buttons = { } -- buttons for ButtonDialog
2022-10-29 17:06:42 +00:00
local stream_buttons -- page stream buttons
2022-10-26 09:31:16 +00:00
local download_buttons = { } -- file type download buttons
2022-10-29 17:06:42 +00:00
for i , acquisition in ipairs ( acquisitions ) do -- filter out unsupported file types
if acquisition.count then
stream_buttons = {
{
2022-12-11 09:08:46 +00:00
-- @translators "Stream" here refers to being able to read documents from an OPDS server without downloading them completely, on a page by page basis.
2022-10-29 17:06:42 +00:00
text = _ ( " Page stream " ) .. " \u{2B0C} " , -- append LEFT RIGHT BLACK ARROW
callback = function ( )
2022-11-12 06:27:57 +00:00
OPDSPSE : streamPages ( acquisition.href , acquisition.count , false , self.root_catalog_username , self.root_catalog_password )
2022-10-29 17:06:42 +00:00
UIManager : close ( self.download_dialog )
end ,
} ,
{
2022-12-11 09:08:46 +00:00
-- @translators "Stream" here refers to being able to read documents from an OPDS server without downloading them completely, on a page by page basis.
2022-10-29 17:06:42 +00:00
text = _ ( " Stream from page " ) .. " \u{2B0C} " , -- append LEFT RIGHT BLACK ARROW
callback = function ( )
2022-11-12 06:27:57 +00:00
OPDSPSE : streamPages ( acquisition.href , acquisition.count , true , self.root_catalog_username , self.root_catalog_password )
2022-10-29 17:06:42 +00:00
UIManager : close ( self.download_dialog )
end ,
} ,
}
2023-06-09 18:58:50 +00:00
elseif acquisition.type == " borrow " then
table.insert ( download_buttons , {
text = _ ( " Borrow " ) ,
enabled = false ,
} )
2022-03-18 20:27:17 +00:00
else
local filetype = util.getFileNameSuffix ( acquisition.href )
logger.dbg ( " Filetype for download is " , filetype )
if not DocumentRegistry : hasProvider ( " dummy. " .. filetype ) then
filetype = nil
end
if not filetype and DocumentRegistry : hasProvider ( nil , acquisition.type ) then
filetype = DocumentRegistry : mimeToExt ( acquisition.type )
end
if filetype then -- supported file type
2022-10-29 17:06:42 +00:00
local text = url.unescape ( acquisition.title or string.upper ( filetype ) )
2022-10-26 09:31:16 +00:00
table.insert ( download_buttons , {
2022-03-18 20:27:17 +00:00
text = text .. " \u{2B07} " , -- append DOWNWARDS BLACK ARROW
callback = function ( )
2022-10-29 17:06:42 +00:00
self : downloadFile ( filename .. " . " .. string.lower ( filetype ) , acquisition.href )
2022-03-18 20:27:17 +00:00
UIManager : close ( self.download_dialog )
end ,
} )
end
2014-09-05 13:02:13 +00:00
end
end
2022-10-29 17:06:42 +00:00
local buttons_nb = # download_buttons
if buttons_nb > 0 then
if buttons_nb == 1 then -- one wide button
2022-10-26 09:31:16 +00:00
table.insert ( buttons , download_buttons )
2022-10-29 17:06:42 +00:00
else
if buttons_nb % 2 == 1 then -- we need even number of buttons
table.insert ( download_buttons , { text = " " } )
end
for i = 1 , buttons_nb , 2 do -- two buttons in a row
table.insert ( buttons , { download_buttons [ i ] , download_buttons [ i + 1 ] } )
end
2022-10-26 09:31:16 +00:00
end
2022-10-29 17:06:42 +00:00
table.insert ( buttons , { } ) -- separator
2021-12-07 13:06:37 +00:00
end
2022-10-29 17:06:42 +00:00
if stream_buttons then
2022-10-26 09:31:16 +00:00
table.insert ( buttons , stream_buttons )
table.insert ( buttons , { } ) -- separator
2021-12-07 13:06:37 +00:00
end
table.insert ( buttons , { -- action buttons
2014-09-10 04:25:08 +00:00
{
2021-08-24 20:19:07 +00:00
text = _ ( " Choose folder " ) ,
2014-09-10 04:25:08 +00:00
callback = function ( )
2014-10-28 09:14:06 +00:00
require ( " ui/downloadmgr " ) : new {
2014-09-10 04:25:08 +00:00
onConfirm = function ( path )
2022-02-17 09:53:15 +00:00
logger.dbg ( " Download folder set to " , path )
2014-09-10 04:25:08 +00:00
G_reader_settings : saveSetting ( " download_dir " , path )
2022-02-17 09:53:15 +00:00
self.download_dialog : setTitle ( createTitle ( path , filename ) )
2014-09-10 04:25:08 +00:00
end ,
2022-02-17 09:53:15 +00:00
} : chooseDir ( self.getCurrentDownloadDir ( ) )
2014-09-10 04:25:08 +00:00
end ,
2021-05-31 20:19:24 +00:00
} ,
2021-12-07 13:06:37 +00:00
{
text = _ ( " Change filename " ) ,
callback = function ( )
2022-10-29 17:06:42 +00:00
local dialog
dialog = InputDialog : new {
2021-12-07 13:06:37 +00:00
title = _ ( " Enter filename " ) ,
input = filename ,
2022-02-17 09:53:15 +00:00
input_hint = filename_orig ,
2021-12-07 13:06:37 +00:00
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2021-12-07 13:06:37 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
2021-12-07 13:06:37 +00:00
end ,
} ,
{
text = _ ( " Set filename " ) ,
is_enter_default = true ,
callback = function ( )
2022-10-29 17:06:42 +00:00
filename = dialog : getInputValue ( )
2022-02-17 09:53:15 +00:00
if filename == " " then
filename = filename_orig
end
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
2022-02-17 09:53:15 +00:00
self.download_dialog : setTitle ( createTitle ( self.getCurrentDownloadDir ( ) , filename ) )
2021-12-07 13:06:37 +00:00
end ,
} ,
}
} ,
}
2022-10-29 17:06:42 +00:00
UIManager : show ( dialog )
dialog : onShowKeyboard ( )
2021-12-07 13:06:37 +00:00
end ,
} ,
} )
2023-06-09 18:58:50 +00:00
local cover_link = item.image or item.thumbnail
2021-12-07 13:06:37 +00:00
table.insert ( buttons , {
{
2023-06-09 18:58:50 +00:00
text = _ ( " Book cover " ) ,
enabled = cover_link and true or false ,
2021-12-07 13:06:37 +00:00
callback = function ( )
2023-06-09 18:58:50 +00:00
OPDSPSE : streamPages ( cover_link , 1 , false , self.root_catalog_username , self.root_catalog_password )
2021-12-07 13:06:37 +00:00
end ,
} ,
2021-05-31 20:19:24 +00:00
{
text = _ ( " Book information " ) ,
enabled = type ( item.content ) == " string " ,
callback = function ( )
local TextViewer = require ( " ui/widget/textviewer " )
UIManager : show ( TextViewer : new {
title = item.text ,
2022-01-24 11:37:08 +00:00
title_multilines = true ,
2021-05-31 20:19:24 +00:00
text = util.htmlToPlainTextIfHtml ( item.content ) ,
2021-06-04 16:45:08 +00:00
text_face = Font : getFace ( " x_smallinfofont " , G_reader_settings : readSetting ( " items_font_size " ) ) ,
2021-05-31 20:19:24 +00:00
} )
end ,
} ,
2014-09-10 04:25:08 +00:00
} )
2014-09-05 13:02:13 +00:00
2023-06-09 18:58:50 +00:00
self.download_dialog = ButtonDialog : new {
2022-02-17 09:53:15 +00:00
title = createTitle ( self.getCurrentDownloadDir ( ) , filename ) ,
buttons = buttons ,
}
2014-09-05 13:02:13 +00:00
UIManager : show ( self.download_dialog )
end
2022-10-29 17:06:42 +00:00
-- Returns user selected or last opened folder
function OPDSBrowser . getCurrentDownloadDir ( )
return G_reader_settings : readSetting ( " download_dir " ) or G_reader_settings : readSetting ( " lastdir " )
end
-- Downloads a book (with "File already exists" dialog)
function OPDSBrowser : downloadFile ( filename , remote_url )
local download_dir = self.getCurrentDownloadDir ( )
filename = util.getSafeFilename ( filename , download_dir )
local local_path = ( download_dir ~= " / " and download_dir or " " ) .. ' / ' .. filename
local_path = util.fixUtf8 ( local_path , " _ " )
local function download ( )
UIManager : scheduleIn ( 1 , function ( )
logger.dbg ( " Downloading file " , local_path , " from " , remote_url )
local parsed = url.parse ( remote_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 = remote_url ,
headers = {
[ " Accept-Encoding " ] = " identity " ,
} ,
sink = ltn12.sink . file ( io.open ( local_path , " w " ) ) ,
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
if code == 200 then
logger.dbg ( " File downloaded to " , local_path )
self.file_downloaded_callback ( local_path )
elseif code == 302 and remote_url : match ( " ^https " ) and headers.location : match ( " ^http[^s] " ) then
util.removeFile ( local_path )
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Insecure HTTPS → HTTP downgrade attempted by redirect from: \n \n '%1' \n \n to \n \n '%2'. \n \n Please inform the server administrator that many clients disallow this because it could be a downgrade attack. " ) , BD.url ( remote_url ) , BD.url ( headers.location ) ) ,
icon = " notice-warning " ,
} )
else
util.removeFile ( local_path )
logger.dbg ( " OPDSBrowser:downloadFile: Request failed: " , status or code )
logger.dbg ( " OPDSBrowser:downloadFile: Response headers: " , headers )
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Could not save file to: \n %1 \n %2 " ) ,
BD.filepath ( local_path ) ,
status or code or " network unreachable " ) ,
} )
end
end )
UIManager : show ( InfoMessage : new {
text = _ ( " Downloading may take several minutes… " ) ,
timeout = 1 ,
} )
end
if lfs.attributes ( local_path ) then
UIManager : show ( ConfirmBox : new {
text = T ( _ ( " The file %1 already exists. Do you want to overwrite it? " ) , BD.filepath ( local_path ) ) ,
ok_text = _ ( " Overwrite " ) ,
ok_callback = function ( )
download ( )
end ,
} )
else
download ( )
2020-02-07 12:27:04 +00:00
end
end
2022-10-29 17:06:42 +00:00
-- Menu action on item tap (Download a book / Show subcatalog / Search in catalog)
2014-09-05 13:02:13 +00:00
function OPDSBrowser : onMenuSelect ( item )
2022-10-29 17:06:42 +00:00
if item.acquisitions and item.acquisitions [ 1 ] then -- book
2021-03-02 16:58:43 +00:00
logger.dbg ( " Downloads available: " , item )
2014-09-05 13:02:13 +00:00
self : showDownloads ( item )
2022-10-29 17:06:42 +00:00
else -- catalog or Search item
if # self.paths == 0 then -- root list
self.root_catalog_title = item.text
self.root_catalog_username = item.username
self.root_catalog_password = item.password
end
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
local connect_callback
2020-02-07 12:27:04 +00:00
if item.searchable then
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
connect_callback = function ( )
2022-10-29 17:06:42 +00:00
self : searchCatalog ( item.url )
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
end
2020-02-07 12:27:04 +00:00
else
2022-10-29 17:06:42 +00:00
self.catalog_title = item.text or self.catalog_title or self.root_catalog_title
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
connect_callback = function ( )
2022-10-29 17:06:42 +00:00
self : updateCatalog ( item.url )
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
end
2014-09-05 13:02:13 +00:00
end
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
NetworkMgr : runWhenConnected ( connect_callback )
2014-09-05 13:02:13 +00:00
end
return true
end
2022-10-29 17:06:42 +00:00
-- Menu action on item long-press (dialog Edit / Delete catalog)
function OPDSBrowser : onMenuHold ( item )
if # self.paths > 0 then return end -- not root list
local is_calibre = item.text == self.calibre_name
local dialog
2023-06-09 18:58:50 +00:00
dialog = ButtonDialog : new {
2022-10-29 17:06:42 +00:00
title = item.text ,
title_align = " center " ,
2014-09-10 04:25:08 +00:00
buttons = {
{
{
2022-10-29 17:06:42 +00:00
text = _ ( " Edit " ) ,
2014-09-10 04:25:08 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : close ( dialog )
self : addEditCatalog ( item , is_calibre )
end ,
2014-09-10 04:25:08 +00:00
} ,
{
2022-10-29 17:06:42 +00:00
text = _ ( " Delete " ) ,
enabled = not is_calibre ,
2014-09-10 04:25:08 +00:00
callback = function ( )
2022-10-29 17:06:42 +00:00
UIManager : show ( ConfirmBox : new {
text = _ ( " Delete OPDS catalog? " ) ,
ok_text = _ ( " Delete " ) ,
ok_callback = function ( )
UIManager : close ( dialog )
self : deleteCatalog ( item )
end ,
} )
end ,
2014-09-10 04:25:08 +00:00
} ,
} ,
} ,
}
2022-10-29 17:06:42 +00:00
UIManager : show ( dialog )
return true
2014-09-10 04:25:08 +00:00
end
2022-10-29 17:06:42 +00:00
-- Menu action on return-arrow tap (go to one-level upper catalog)
2014-09-05 13:02:13 +00:00
function OPDSBrowser : onReturn ( )
2022-10-29 17:06:42 +00:00
if # self.paths > 0 then -- not root list
2014-09-05 13:02:13 +00:00
table.remove ( self.paths )
local path = self.paths [ # self.paths ]
if path then
-- return to last path
2020-12-23 13:35:54 +00:00
self.catalog_title = path.title
2022-10-29 17:06:42 +00:00
self : updateCatalog ( path.url , true )
2014-09-05 13:02:13 +00:00
else
-- return to root path, we simply reinit opdsbrowser
self : init ( )
end
end
return true
end
2022-10-29 17:06:42 +00:00
-- Menu action on return-arrow long-press (go to the catalog home page)
2021-06-29 08:22:03 +00:00
function OPDSBrowser : onHoldReturn ( )
2022-10-29 17:06:42 +00:00
if # self.paths > 1 then -- not catalog home page
2021-06-29 08:22:03 +00:00
local path = self.paths [ 1 ]
2022-10-29 17:06:42 +00:00
for i = # self.paths , 2 , - 1 do
table.remove ( self.paths )
2021-06-29 08:22:03 +00:00
end
2022-10-29 17:06:42 +00:00
self.catalog_title = path.title
self : updateCatalog ( path.url , true )
2021-06-29 08:22:03 +00:00
end
return true
end
2022-10-29 17:06:42 +00:00
-- Menu action on next-page chevron tap (request and show more catalog entries)
2014-09-05 13:02:13 +00:00
function OPDSBrowser : onNext ( )
2017-03-18 22:03:30 +00:00
-- self.page_num comes from menu.lua
2014-11-07 21:05:16 +00:00
local page_num = self.page_num
2017-03-18 22:03:30 +00:00
-- fetch more entries until we fill out one page or reach the end
while page_num == self.page_num do
local hrefs = self.item_table . hrefs
if hrefs and hrefs.next then
2022-10-29 17:06:42 +00:00
if not self : appendCatalog ( hrefs.next ) then
2017-03-18 22:03:30 +00:00
break -- reach end of paging
end
else
break
end
2014-09-05 13:02:13 +00:00
end
return true
end
return OPDSBrowser