2020-01-04 00:18:51 +00:00
local BD = require ( " ui/bidi " )
2014-09-05 13:02:13 +00:00
local ButtonDialog = require ( " ui/widget/buttondialog " )
2017-02-25 17:01:37 +00:00
local ButtonDialogTitle = require ( " ui/widget/buttondialogtitle " )
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 " )
local Menu = require ( " ui/widget/menu " )
2017-04-02 14:17:49 +00:00
local MultiInputDialog = require ( " ui/widget/multiinputdialog " )
2020-02-07 12:27:04 +00:00
local InputDialog = require ( " ui/widget/inputdialog " )
2017-04-02 14:17:49 +00:00
local NetworkMgr = require ( " ui/network/manager " )
2021-02-06 18:09:31 +00:00
local OPDSParser = require ( " opdsparser " )
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 " ,
} ,
{
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
2014-09-05 13:02:13 +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 " ,
2014-10-03 14:22:37 +00:00
acquisition_rel = " ^http://opds%-spec%.org/acquisition " ,
2014-11-30 20:44:47 +00:00
image_rel = " http://opds-spec.org/image " ,
2014-09-05 13:02:13 +00:00
thumbnail_rel = " http://opds-spec.org/image/thumbnail " ,
width = Screen : getWidth ( ) ,
height = Screen : getHeight ( ) ,
no_title = false ,
parent = nil ,
}
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
2014-09-05 13:02:13 +00:00
Menu.init ( self ) -- call parent's init()
end
function OPDSBrowser : addServerFromInput ( fields )
2021-03-02 16:58:43 +00:00
logger.info ( " New OPDS catalog input: " , fields )
2020-02-07 12:27:04 +00:00
local new_server = {
2014-09-05 13:02:13 +00:00
title = fields [ 1 ] ,
2014-11-28 20:42:33 +00:00
url = ( fields [ 2 ] : match ( " ^%a+:// " ) and fields [ 2 ] or " http:// " .. fields [ 2 ] ) ,
2020-02-07 12:27:04 +00:00
searchable = ( fields [ 2 ] : match ( " %%s " ) and true or false ) ,
2021-03-02 16:58:43 +00:00
username = fields [ 3 ] ~= " " and fields [ 3 ] or nil ,
-- Allow empty passwords
2018-09-29 21:17:17 +00:00
password = fields [ 4 ] ,
2020-02-07 12:27:04 +00:00
}
2021-03-06 21:44:18 +00:00
table.insert ( self.opds_servers , new_server )
2014-09-05 13:02:13 +00:00
self : init ( )
end
2014-09-10 05:26:50 +00:00
function OPDSBrowser : editCalibreFromInput ( fields )
2021-03-02 16:58:43 +00:00
logger.dbg ( " Edit calibre server input: " , fields )
2014-09-10 05:26:50 +00:00
if fields [ 1 ] then
2021-03-06 21:44:18 +00:00
self.calibre_opds . host = fields [ 1 ]
2014-09-10 05:26:50 +00:00
end
if tonumber ( fields [ 2 ] ) then
2021-03-06 21:44:18 +00:00
self.calibre_opds . port = fields [ 2 ]
2014-09-10 05:26:50 +00:00
end
2021-03-02 16:58:43 +00:00
if fields [ 3 ] and fields [ 3 ] ~= " " then
2021-03-06 21:44:18 +00:00
self.calibre_opds . username = fields [ 3 ]
2021-03-02 16:58:43 +00:00
else
2021-03-06 21:44:18 +00:00
self.calibre_opds . username = nil
2018-09-29 21:17:17 +00:00
end
if fields [ 4 ] then
2021-03-06 21:44:18 +00:00
self.calibre_opds . password = fields [ 4 ]
2021-03-02 16:58:43 +00:00
else
2021-03-06 21:44:18 +00:00
self.calibre_opds . password = nil
2018-09-29 21:17:17 +00:00
end
2014-09-10 05:26:50 +00:00
self : init ( )
end
2014-09-05 13:02:13 +00:00
function OPDSBrowser : addNewCatalog ( )
self.add_server_dialog = MultiInputDialog : new {
2018-08-11 20:30:10 +00:00
title = _ ( " Add OPDS catalog " ) ,
2014-09-05 13:02:13 +00:00
fields = {
{
text = " " ,
2018-08-11 20:30:10 +00:00
hint = _ ( " Catalog name " ) ,
2014-09-05 13:02:13 +00:00
} ,
{
text = " " ,
2018-08-11 20:30:10 +00:00
hint = _ ( " Catalog URL " ) ,
2014-09-05 13:02:13 +00:00
} ,
2018-09-29 21:17:17 +00:00
{
text = " " ,
hint = _ ( " Username (optional) " ) ,
} ,
{
text = " " ,
hint = _ ( " Password (optional) " ) ,
text_type = " password " ,
} ,
2014-09-05 13:02:13 +00:00
} ,
buttons = {
{
{
2018-08-11 20:30:10 +00:00
text = _ ( " Cancel " ) ,
2014-09-05 13:02:13 +00:00
callback = function ( )
self.add_server_dialog : onClose ( )
UIManager : close ( self.add_server_dialog )
end
} ,
{
2018-08-11 20:30:10 +00:00
text = _ ( " Add " ) ,
2014-09-05 13:02:13 +00:00
callback = function ( )
self.add_server_dialog : onClose ( )
UIManager : close ( self.add_server_dialog )
self : addServerFromInput ( MultiInputDialog : getFields ( ) )
end
} ,
} ,
} ,
2020-06-12 23:56:36 +00:00
width = math.floor ( Screen : getWidth ( ) * 0.95 ) ,
height = math.floor ( Screen : getHeight ( ) * 0.2 ) ,
2014-09-05 13:02:13 +00:00
}
UIManager : show ( self.add_server_dialog )
2018-03-30 10:46:36 +00:00
self.add_server_dialog : onShowKeyboard ( )
2014-09-05 13:02:13 +00:00
end
2014-09-10 05:26:50 +00:00
function OPDSBrowser : editCalibreServer ( )
self.add_server_dialog = MultiInputDialog : new {
2018-08-11 20:30:10 +00:00
title = _ ( " Edit local calibre host and port " ) ,
2014-09-10 05:26:50 +00:00
fields = {
{
2019-08-23 17:53:53 +00:00
--- @todo get IP address of current device
2021-03-06 21:44:18 +00:00
text = self.calibre_opds . host or " 192.168.1.1 " ,
2018-08-11 20:30:10 +00:00
hint = _ ( " calibre host " ) ,
2014-09-10 05:26:50 +00:00
} ,
{
2021-03-06 21:44:18 +00:00
text = self.calibre_opds . port and tostring ( self.calibre_opds . port ) or " 8080 " ,
2018-08-11 20:30:10 +00:00
hint = _ ( " calibre port " ) ,
2014-09-10 05:26:50 +00:00
} ,
2018-09-29 21:17:17 +00:00
{
2021-03-06 21:44:18 +00:00
text = self.calibre_opds . username or " " ,
2018-09-29 21:17:17 +00:00
hint = _ ( " Username (optional) " ) ,
} ,
{
2021-03-06 21:44:18 +00:00
text = self.calibre_opds . password or " " ,
2018-09-29 21:17:17 +00:00
hint = _ ( " Password (optional) " ) ,
text_type = " password " ,
} ,
2014-09-10 05:26:50 +00:00
} ,
buttons = {
{
{
2018-08-11 20:30:10 +00:00
text = _ ( " Cancel " ) ,
2014-09-10 05:26:50 +00:00
callback = function ( )
self.add_server_dialog : onClose ( )
UIManager : close ( self.add_server_dialog )
end
} ,
{
2018-08-11 20:30:10 +00:00
text = _ ( " Apply " ) ,
2014-09-10 05:26:50 +00:00
callback = function ( )
self.add_server_dialog : onClose ( )
UIManager : close ( self.add_server_dialog )
self : editCalibreFromInput ( MultiInputDialog : getFields ( ) )
end
} ,
} ,
} ,
2020-06-12 23:56:36 +00:00
width = math.floor ( Screen : getWidth ( ) * 0.95 ) ,
height = math.floor ( Screen : getHeight ( ) * 0.2 ) ,
2014-09-10 05:26:50 +00:00
}
UIManager : show ( self.add_server_dialog )
2018-03-30 10:46:36 +00:00
self.add_server_dialog : onShowKeyboard ( )
2014-09-10 05:26:50 +00:00
end
2014-09-05 13:02:13 +00:00
function OPDSBrowser : genItemTableFromRoot ( )
local item_table = { }
2021-03-06 21:44:18 +00:00
for _ , server in ipairs ( self.opds_servers ) do
2014-09-05 13:02:13 +00:00
table.insert ( item_table , {
text = server.title ,
content = server.subtitle ,
url = server.url ,
2018-09-29 21:17:17 +00:00
username = server.username ,
password = server.password ,
2014-09-10 04:25:08 +00:00
deletable = true ,
editable = true ,
2020-02-07 12:27:04 +00:00
searchable = server.searchable ,
2014-09-05 13:02:13 +00:00
} )
end
2021-03-06 21:44:18 +00:00
if not self.calibre_opds . host or not self.calibre_opds . port then
2014-09-10 05:26:50 +00:00
table.insert ( item_table , {
text = self.calibre_name ,
callback = function ( )
self : editCalibreServer ( )
end ,
deletable = false ,
} )
else
table.insert ( item_table , {
text = self.calibre_name ,
url = string.format ( " http://%s:%d/opds " ,
2021-03-06 21:44:18 +00:00
self.calibre_opds . host , self.calibre_opds . port ) ,
username = self.calibre_opds . username ,
password = self.calibre_opds . password ,
2014-09-10 05:26:50 +00:00
editable = true ,
deletable = false ,
2020-02-07 12:27:04 +00:00
searchable = false ,
2014-09-10 05:26:50 +00:00
} )
end
2014-09-05 13:02:13 +00:00
table.insert ( item_table , {
2018-08-11 20:30:10 +00:00
text = _ ( " Add new OPDS catalog " ) ,
2014-09-05 13:02:13 +00:00
callback = function ( )
self : addNewCatalog ( )
end ,
} )
return item_table
end
2019-04-05 06:50:33 +00:00
function OPDSBrowser : fetchFeed ( item_url , username , password , method )
2021-03-02 16:58:43 +00:00
local sink = { }
2021-03-15 00:25:10 +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 ,
method = method and method or " GET " ,
-- 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 ) ,
2021-03-21 13:00:13 +00:00
user = username ,
2021-03-15 00:25:10 +00:00
password = password ,
2021-03-02 16:58:43 +00:00
}
logger.info ( " Request: " , request )
2021-03-15 00:25:10 +00:00
local code , headers = socket.skip ( 1 , http.request ( request ) )
socketutil : reset_timeout ( )
2014-09-05 13:02:13 +00:00
-- raise error message when network is unavailable
if headers == nil then
2014-09-10 04:25:08 +00:00
error ( code )
2014-09-05 13:02:13 +00:00
end
2018-09-29 21:17:17 +00:00
if code == 200 then
2019-04-05 06:50:33 +00:00
if method == " HEAD " then
if headers [ " last-modified " ] then
return headers [ " last-modified " ]
else
return
end
end
2015-09-14 16:59:00 +00:00
local xml = table.concat ( sink )
if xml ~= " " then
return xml
end
2019-11-07 19:37:02 +00:00
elseif method == " HEAD " then
-- Don't show error messages when we check headers only.
return
2019-08-22 13:37:17 +00:00
elseif code == 301 then
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " The catalog has been permanently moved. Please update catalog URL to '%1'. " ) , BD.url ( headers [ ' Location ' ] ) ) ,
2019-08-22 13:37:17 +00:00
} )
2020-12-24 14:37:19 +00:00
elseif code == 302 and item_url : match ( " ^https " ) and headers.location : match ( " ^http[^s] " ) then
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 ( item_url ) , BD.url ( headers.location ) ) ,
icon = " notice-warning " ,
} )
2019-08-22 13:37:17 +00:00
elseif code == 401 then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Authentication required for catalog. Please add a username and password. " ) ) ,
} )
elseif code == 403 then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Failed to authenticate. Please check your username and password. " ) ) ,
} )
elseif code == 404 then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Catalog not found. " ) ) ,
} )
2021-03-02 16:58:43 +00:00
elseif code == 406 then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Cannot get catalog. Server refuses to serve uncompressed content. " ) ) ,
} )
2018-09-29 21:17:17 +00:00
else
UIManager : show ( InfoMessage : new {
2019-08-22 13:37:17 +00:00
text = T ( _ ( " Cannot get catalog. Server response code %1. " ) , code ) ,
2018-09-29 21:17:17 +00:00
} )
2014-09-05 13:02:13 +00:00
end
end
2018-09-29 21:17:17 +00:00
function OPDSBrowser : parseFeed ( item_url , username , password )
2019-04-05 06:50:33 +00:00
local feed_last_modified = self : fetchFeed ( item_url , username , password , " HEAD " )
2018-09-29 21:17:17 +00:00
local hash = " opds|catalog| " .. item_url
2019-04-05 06:50:33 +00:00
if feed_last_modified then
2020-09-13 20:14:05 +00:00
hash = hash .. " | " .. feed_last_modified
2019-04-05 06:50:33 +00:00
end
2021-05-07 01:59:27 +00:00
local feed = CatalogCache : check ( hash )
if feed then
2020-09-13 20:14:05 +00:00
logger.dbg ( " Cache hit for " , hash )
2014-09-05 13:02:13 +00:00
else
2020-09-13 20:14:05 +00:00
logger.dbg ( " Cache miss for " , hash )
2018-09-29 21:17:17 +00:00
feed = self : fetchFeed ( item_url , username , password )
2014-09-05 13:02:13 +00:00
if feed then
2020-09-13 20:14:05 +00:00
logger.dbg ( " Caching " , hash )
2021-05-07 01:59:27 +00:00
CatalogCache : insert ( hash , feed )
2014-09-05 13:02:13 +00:00
end
end
if feed then
return OPDSParser : parse ( feed )
end
end
2018-09-29 21:17:17 +00:00
function OPDSBrowser : getCatalog ( item_url , username , password )
local ok , catalog = pcall ( self.parseFeed , self , item_url , username , password )
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
if not ok and catalog then
2021-03-02 16:58:43 +00:00
logger.info ( " Cannot get catalog info from " , item_url or " nil " , catalog )
2014-09-10 04:25:08 +00:00
UIManager : show ( InfoMessage : new {
2020-11-22 03:51:32 +00:00
text = T ( _ ( " Cannot get catalog info from %1 " ) , ( item_url and BD.url ( item_url ) or " nil " ) ) ,
2014-09-10 04:25:08 +00:00
} )
2014-09-05 13:02:13 +00:00
return
end
if ok and catalog then
return catalog
end
end
2018-09-29 21:17:17 +00:00
function OPDSBrowser : genItemTableFromURL ( item_url , username , password )
local catalog = self : getCatalog ( item_url , username , password )
return self : genItemTableFromCatalog ( catalog , item_url , username , password )
2014-11-30 18:06:27 +00:00
end
2021-03-06 18:29:15 +00:00
function OPDSBrowser : getSearchTemplate ( osd_url , username , password )
-- parse search descriptor
local search_descriptor = self : parseFeed ( osd_url , username , password )
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
2018-09-29 21:17:17 +00:00
function OPDSBrowser : genItemTableFromCatalog ( catalog , item_url , username , password )
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
for _ , link in ipairs ( feed.link ) do
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
2021-03-06 23:05:41 +00:00
local stpl = self : getSearchTemplate ( build_href ( link.href ) , username , password )
2021-04-09 17:57:06 +00:00
-- The OpenSearchDescription/Url template field might *also* be a relative path...
stpl = build_href ( stpl )
2021-03-06 18:29:15 +00:00
-- insert the search item
local item = { }
item.acquisitions = { }
item.text = " Search "
item.callback = function ( )
self : browseSearchable ( stpl , username , password )
end
table.insert ( item_table , item )
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
2018-09-29 21:17:17 +00:00
if username then
item_table.username = username
end
if password then
item_table.password = password
end
2016-12-27 10:00:13 +00:00
if not feed.entry then
2019-11-07 19:37:02 +00:00
if # hrefs == 0 then
UIManager : show ( InfoMessage : new {
2021-03-02 16:58:43 +00:00
text = _ ( " Failed to parse the catalog. " ) ,
2019-11-07 19:37:02 +00:00
} )
end
2016-12-27 10:00:13 +00:00
return item_table
end
for _ , entry in ipairs ( feed.entry ) do
local item = { }
item.acquisitions = { }
if entry.link then
for _ , link in ipairs ( entry.link ) do
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
item.url = build_href ( link.href )
2014-09-05 13:02:13 +00:00
end
2016-12-27 10:00:13 +00:00
if link.rel then
if link.rel : match ( self.acquisition_rel ) then
table.insert ( item.acquisitions , {
type = link.type ,
href = build_href ( link.href ) ,
} )
elseif link.rel == self.thumbnail_rel then
item.thumbnail = build_href ( link.href )
elseif link.rel == self.image_rel then
item.image = build_href ( link.href )
2014-09-05 13:02:13 +00:00
end
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
item.id = entry.id
item.content = entry.content
item.updated = entry.updated
2018-09-29 21:17:17 +00:00
if username then
item.username = username
end
if password then
item.password = password
end
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
2018-09-29 21:17:17 +00:00
function OPDSBrowser : updateCatalog ( item_url , username , password )
local menu_table = self : genItemTableFromURL ( item_url , username , password )
2014-09-05 13:02:13 +00:00
if # menu_table > 0 then
2020-12-23 13:35:54 +00:00
self : switchItemTable ( self.catalog_title , menu_table )
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
return true
end
end
2018-09-29 21:17:17 +00:00
function OPDSBrowser : appendCatalog ( item_url , username , password )
local new_table = self : genItemTableFromURL ( item_url , username , password )
2017-03-18 22:03:30 +00:00
if # new_table == 0 then return false end
2016-02-10 18:30:05 +00:00
for _ , item in ipairs ( new_table ) do
2014-09-05 13:02:13 +00:00
table.insert ( self.item_table , item )
end
self.item_table . hrefs = new_table.hrefs
2020-12-23 13:35:54 +00:00
self : switchItemTable ( self.catalog_title , self.item_table , - 1 )
2014-09-05 13:02:13 +00:00
return true
end
2017-02-25 17:01:37 +00:00
function OPDSBrowser . getCurrentDownloadDir ( )
local lastdir = G_reader_settings : readSetting ( " lastdir " )
return G_reader_settings : readSetting ( " download_dir " ) or lastdir
end
2021-01-17 08:22:48 +00:00
function OPDSBrowser : downloadFile ( item , filetype , remote_url )
2021-03-05 17:12:47 +00:00
-- Download to user selected folder or last opened folder.
2017-02-25 17:01:37 +00:00
local download_dir = self.getCurrentDownloadDir ( )
2021-03-06 18:29:36 +00:00
local filename = item.title .. " . " .. filetype
if item.author then
filename = item.author .. " - " .. filename
end
filename = util.getSafeFilename ( filename , download_dir )
2019-05-14 17:10:41 +00:00
local local_path = download_dir .. " / " .. filename
2017-04-02 14:17:49 +00:00
local_path = util.fixUtf8 ( local_path , " _ " )
2014-09-05 13:02:13 +00:00
2018-01-14 19:19:45 +00:00
local function download ( )
UIManager : scheduleIn ( 1 , function ( )
2021-03-02 16:58:43 +00:00
logger.dbg ( " Downloading file " , local_path , " from " , remote_url )
2018-01-14 19:19:45 +00:00
local parsed = url.parse ( remote_url )
2018-12-26 13:12:50 +00:00
2021-03-15 00:25:10 +00:00
local code , headers
if parsed.scheme == " http " or parsed.scheme == " https " then
socketutil : set_timeout ( socketutil.FILE_BLOCK_TIMEOUT , socketutil.FILE_TOTAL_TIMEOUT )
code , headers = socket.skip ( 1 , http.request {
2018-12-26 13:12:50 +00:00
url = remote_url ,
2021-03-15 00:25:10 +00:00
headers = {
[ " Accept-Encoding " ] = " identity " ,
} ,
2018-12-26 13:12:50 +00:00
sink = ltn12.sink . file ( io.open ( local_path , " w " ) ) ,
2021-03-02 16:58:43 +00:00
user = item.username ,
2021-03-15 00:25:10 +00:00
password = item.password ,
} )
socketutil : reset_timeout ( )
2018-12-26 13:12:50 +00:00
else
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Invalid protocol: \n %1 " ) , parsed.scheme ) ,
timeout = 3 ,
} )
end
2020-12-24 14:37:19 +00:00
if code == 200 then
2021-03-02 16:58:43 +00:00
logger.dbg ( " File downloaded to " , local_path )
2018-01-14 19:19:45 +00:00
if self.file_downloaded_callback then
self.file_downloaded_callback ( local_path )
end
2020-12-24 14:37:19 +00:00
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 " ,
} )
2018-01-14 19:19:45 +00:00
else
2020-12-24 14:37:19 +00:00
util.removeFile ( local_path )
2018-01-14 19:19:45 +00:00
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = _ ( " Could not save file to: \n " ) .. BD.filepath ( local_path ) ,
2018-01-14 19:19:45 +00:00
timeout = 3 ,
} )
end
end )
2014-09-10 04:25:08 +00:00
UIManager : show ( InfoMessage : new {
2018-08-11 20:30:10 +00:00
text = _ ( " Downloading may take several minutes… " ) ,
2018-01-14 19:19:45 +00:00
timeout = 1 ,
} )
end
if lfs.attributes ( local_path , " mode " ) == " file " then
UIManager : show ( ConfirmBox : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " The file %1 already exists. Do you want to overwrite it? " ) , BD.filepath ( local_path ) ) ,
2018-08-11 20:30:10 +00:00
ok_text = _ ( " Overwrite " ) ,
2018-01-14 19:19:45 +00:00
ok_callback = function ( )
download ( )
end ,
2014-09-10 04:25:08 +00:00
} )
2018-01-14 19:19:45 +00:00
else
download ( )
2014-09-05 13:02:13 +00:00
end
end
2017-02-25 17:01:37 +00:00
function OPDSBrowser : createNewDownloadDialog ( path , buttons )
self.download_dialog = ButtonDialogTitle : new {
2021-03-05 16:49:33 +00:00
title = T ( _ ( " Download folder: \n %1 \n \n Download file type: " ) , BD.dirpath ( path ) ) ,
2021-05-31 20:19:24 +00:00
use_info_style = true ,
2017-02-25 17:01:37 +00:00
buttons = buttons
}
end
2014-09-05 13:02:13 +00:00
function OPDSBrowser : showDownloads ( item )
local acquisitions = item.acquisitions
local downloadsperline = 2
local lines = math.ceil ( # acquisitions / downloadsperline )
local buttons = { }
for i = 1 , lines do
local line = { }
for j = 1 , downloadsperline do
local button = { }
local index = ( i - 1 ) * downloadsperline + j
local acquisition = acquisitions [ index ]
if acquisition then
2021-08-23 10:44:07 +00:00
local filetype = util.getFileNameSuffix ( acquisition.href )
if not DocumentRegistry : hasProvider ( " dummy. " .. filetype ) then
filetype = nil
end
if not filetype and DocumentRegistry : hasProvider ( nil , acquisition.type ) then
2021-01-17 08:22:48 +00:00
filetype = DocumentRegistry : mimeToExt ( acquisition.type )
end
2021-08-23 10:44:07 +00:00
2021-01-17 08:22:48 +00:00
if filetype then
2021-08-23 10:44:07 +00:00
filetype = string.lower ( filetype )
2017-02-25 17:01:37 +00:00
-- append DOWNWARDS BLACK ARROW ⬇ U+2B07 to format
2021-01-17 08:22:48 +00:00
button.text = string.upper ( filetype ) .. " \xE2 \xAC \x87 "
2014-09-05 13:02:13 +00:00
button.callback = function ( )
2021-01-17 08:22:48 +00:00
self : downloadFile ( item , filetype , acquisition.href )
2014-09-05 13:02:13 +00:00
UIManager : close ( self.download_dialog )
end
table.insert ( line , button )
end
2017-02-25 17:01:37 +00:00
elseif # acquisitions > downloadsperline then
table.insert ( line , { text = " " } )
2014-09-05 13:02:13 +00:00
end
end
table.insert ( buttons , line )
end
2017-02-25 17:01:37 +00:00
table.insert ( buttons , { } )
2021-05-31 20:19:24 +00:00
-- Set download folder and book info buttons.
2014-09-10 04:25:08 +00:00
table.insert ( buttons , {
{
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 )
2021-03-05 17:12:47 +00:00
logger.info ( " Download folder set to " , path )
2014-09-10 04:25:08 +00:00
G_reader_settings : saveSetting ( " download_dir " , path )
2017-02-25 17:01:37 +00:00
UIManager : nextTick ( function ( )
UIManager : close ( self.download_dialog )
self : createNewDownloadDialog ( path , buttons )
UIManager : show ( self.download_dialog )
end )
2014-09-10 04:25:08 +00:00
end ,
2014-10-28 09:14:06 +00:00
} : chooseDir ( )
2014-09-10 04:25:08 +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 ,
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
2017-02-25 17:01:37 +00:00
self : createNewDownloadDialog ( self.getCurrentDownloadDir ( ) , buttons )
2014-09-05 13:02:13 +00:00
UIManager : show ( self.download_dialog )
end
2020-02-07 12:27:04 +00:00
function OPDSBrowser : browse ( browse_url , username , password )
2021-03-02 16:58:43 +00:00
logger.dbg ( " Browse OPDS url " , browse_url or " nil " )
2020-02-07 12:27:04 +00:00
table.insert ( self.paths , {
url = browse_url ,
username = username ,
password = password ,
2020-12-23 13:35:54 +00:00
title = self.catalog_title ,
2020-02-07 12:27:04 +00:00
} )
if not self : updateCatalog ( browse_url , username , password ) then
table.remove ( self.paths )
end
end
function OPDSBrowser : browseSearchable ( browse_url , username , password )
self.search_server_dialog = InputDialog : new {
title = _ ( " Search OPDS catalog " ) ,
input = " " ,
2020-02-12 16:39:08 +00:00
-- @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 " ) ,
2020-02-07 12:27:04 +00:00
input_type = " string " ,
description = _ ( " %s in url will be replaced by your input " ) ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
callback = function ( )
UIManager : close ( self.search_server_dialog )
end ,
} ,
{
text = _ ( " Search " ) ,
is_enter_default = true ,
callback = function ( )
UIManager : close ( self.search_server_dialog )
local search = self.search_server_dialog : getInputText ( ) : gsub ( " " , " + " )
local searched_url = browse_url : gsub ( " %%s " , search )
self : browse ( searched_url , username , password )
end ,
} ,
}
} ,
}
UIManager : show ( self.search_server_dialog )
self.search_server_dialog : onShowKeyboard ( )
end
2014-09-05 13:02:13 +00:00
function OPDSBrowser : onMenuSelect ( item )
2021-03-27 18:06:35 +00:00
self.catalog_title = self.catalog_title or _ ( " OPDS Catalog " )
2014-09-05 13:02:13 +00:00
-- add catalog
if item.callback then
item.callback ( )
-- acquisition
elseif item.acquisitions and # item.acquisitions > 0 then
2021-03-02 16:58:43 +00:00
logger.dbg ( " Downloads available: " , item )
2014-09-05 13:02:13 +00:00
self : showDownloads ( item )
-- navigation
else
2021-03-27 18:06:35 +00:00
self.catalog_title = item.text or self.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
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 ( )
self : browseSearchable ( item.url , item.username , item.password )
end
2020-02-07 12:27:04 +00:00
else
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 ( )
self : browse ( item.url , item.username , item.password )
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
2014-09-10 04:25:08 +00:00
function OPDSBrowser : editServerFromInput ( item , fields )
2021-03-02 16:58:43 +00:00
logger.info ( " Edit OPDS catalog input: " , fields )
2021-03-06 21:44:18 +00:00
for _ , server in ipairs ( self.opds_servers ) do
2014-09-10 04:25:08 +00:00
if server.title == item.text or server.url == item.url then
server.title = fields [ 1 ]
2014-11-28 20:42:33 +00:00
server.url = ( fields [ 2 ] : match ( " ^%a+:// " ) and fields [ 2 ] or " http:// " .. fields [ 2 ] )
2020-02-07 12:27:04 +00:00
server.searchable = ( fields [ 2 ] : match ( " %%s " ) and true or false )
2021-03-02 16:58:43 +00:00
server.username = fields [ 3 ] ~= " " and fields [ 3 ] or nil
2018-09-29 21:17:17 +00:00
server.password = fields [ 4 ]
2014-09-10 04:25:08 +00:00
end
end
self : init ( )
end
function OPDSBrowser : editOPDSServer ( item )
2021-03-02 16:58:43 +00:00
logger.info ( " Edit OPDS Server: " , item )
2014-09-10 04:25:08 +00:00
self.edit_server_dialog = MultiInputDialog : new {
2018-08-11 20:30:10 +00:00
title = _ ( " Edit OPDS catalog " ) ,
2014-09-10 04:25:08 +00:00
fields = {
{
text = item.text or " " ,
2021-04-02 15:59:29 +00:00
hint = _ ( " Catalog name " ) ,
2014-09-10 04:25:08 +00:00
} ,
{
text = item.url or " " ,
2018-08-11 20:30:10 +00:00
hint = _ ( " Catalog URL " ) ,
2014-09-10 04:25:08 +00:00
} ,
2018-09-29 21:17:17 +00:00
{
text = item.username or " " ,
hint = _ ( " Username (optional) " ) ,
} ,
{
text = item.password or " " ,
hint = _ ( " Password (optional) " ) ,
text_type = " password " ,
} ,
2014-09-10 04:25:08 +00:00
} ,
buttons = {
{
{
2018-08-11 20:30:10 +00:00
text = _ ( " Cancel " ) ,
2014-09-10 04:25:08 +00:00
callback = function ( )
self.edit_server_dialog : onClose ( )
UIManager : close ( self.edit_server_dialog )
end
} ,
{
2018-08-11 20:30:10 +00:00
text = _ ( " Apply " ) ,
2014-09-10 04:25:08 +00:00
callback = function ( )
self.edit_server_dialog : onClose ( )
UIManager : close ( self.edit_server_dialog )
self : editServerFromInput ( item , MultiInputDialog : getFields ( ) )
end
} ,
} ,
} ,
2020-06-12 23:56:36 +00:00
width = math.floor ( Screen : getWidth ( ) * 0.95 ) ,
height = math.floor ( Screen : getHeight ( ) * 0.2 ) ,
2014-09-10 04:25:08 +00:00
}
UIManager : show ( self.edit_server_dialog )
2018-03-30 10:46:36 +00:00
self.edit_server_dialog : onShowKeyboard ( )
2014-09-10 04:25:08 +00:00
end
function OPDSBrowser : deleteOPDSServer ( item )
2021-03-02 16:58:43 +00:00
logger.info ( " Delete OPDS server: " , item )
2021-03-06 21:44:18 +00:00
for i = # self.opds_servers , 1 , - 1 do
local server = self.opds_servers [ i ]
if server.title == item.text and server.url == item.url then
table.remove ( self.opds_servers , i )
2014-09-10 04:25:08 +00:00
end
end
self : init ( )
end
function OPDSBrowser : onMenuHold ( item )
if item.deletable or item.editable then
self.opds_server_dialog = ButtonDialog : new {
buttons = {
{
{
2018-08-11 20:30:10 +00:00
text = _ ( " Edit " ) ,
2014-09-10 04:25:08 +00:00
enabled = item.editable ,
callback = function ( )
UIManager : close ( self.opds_server_dialog )
2014-09-10 05:26:50 +00:00
if item.text ~= self.calibre_name then
self : editOPDSServer ( item )
else
self : editCalibreServer ( item )
end
2014-09-10 04:25:08 +00:00
end
} ,
{
2018-08-11 20:30:10 +00:00
text = _ ( " Delete " ) ,
2014-09-10 04:25:08 +00:00
enabled = item.deletable ,
callback = function ( )
UIManager : close ( self.opds_server_dialog )
self : deleteOPDSServer ( item )
end
} ,
} ,
}
}
UIManager : show ( self.opds_server_dialog )
return true
end
end
2014-09-05 13:02:13 +00:00
function OPDSBrowser : onReturn ( )
if # self.paths > 0 then
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
2018-09-29 21:17:17 +00:00
self : updateCatalog ( path.url , path.username , path.password )
2014-09-05 13:02:13 +00:00
else
-- return to root path, we simply reinit opdsbrowser
self : init ( )
end
end
return true
end
2021-06-29 08:22:03 +00:00
function OPDSBrowser : onHoldReturn ( )
if # self.paths > 1 then
local path = self.paths [ 1 ]
if path then
for i = # self.paths , 2 , - 1 do
table.remove ( self.paths )
end
self.catalog_title = path.title
self : updateCatalog ( path.url , path.username , path.password )
end
end
return true
end
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
2018-09-29 21:17:17 +00:00
if not self : appendCatalog ( hrefs.next , self.item_table . username , self.item_table . password ) 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