@ -18,7 +18,9 @@ local Screen = require("device").screen
local Size = require ( " ui/size " )
local Size = require ( " ui/size " )
local TimeVal = require ( " ui/timeval " )
local TimeVal = require ( " ui/timeval " )
local UIManager = require ( " ui/uimanager " )
local UIManager = require ( " ui/uimanager " )
local lfs = require ( " libs/libkoreader-lfs " )
local logger = require ( " logger " )
local logger = require ( " logger " )
local rapidjson = require ( " rapidjson " )
local util = require ( " util " )
local util = require ( " util " )
local _ = require ( " gettext " )
local _ = require ( " gettext " )
local T = require ( " ffi/util " ) . template
local T = require ( " ffi/util " ) . template
@ -84,7 +86,7 @@ end
local function getBooksBySeries ( t , series )
local function getBooksBySeries ( t , series )
local result = { }
local result = { }
for _ , book in ipairs ( t ) do
for _ , book in ipairs ( t ) do
if book.series and type( book.series ) ~= " function " then
if book.series and book.series ~= rapidjson.null then
if book.series == series then
if book.series == series then
table.insert ( result , book )
table.insert ( result , book )
end
end
@ -112,7 +114,7 @@ end
local function searchBySeries ( t , query , case_insensitive )
local function searchBySeries ( t , query , case_insensitive )
local freq = { }
local freq = { }
for _ , book in ipairs ( t ) do
for _ , book in ipairs ( t ) do
if book.series and type( book.series ) ~= " function " then
if book.series and book.series ~= rapidjson.null then
if match ( book.series , query , case_insensitive ) then
if match ( book.series , query , case_insensitive ) then
freq [ book.series ] = ( freq [ book.series ] or 0 ) + 1
freq [ book.series ] = ( freq [ book.series ] or 0 ) + 1
end
end
@ -147,7 +149,7 @@ local function getBookInfo(book)
tags = _ ( " Tags: " ) .. " " .. tags
tags = _ ( " Tags: " ) .. " " .. tags
end
end
local series
local series
if book.series and type( book.series ) ~= " function " then
if book.series and book.series ~= rapidjson.null then
series = _ ( " Series: " ) .. " " .. book.series
series = _ ( " Series: " ) .. " " .. book.series
end
end
return string.format ( " %s \n %s \n %s%s%s " , title , authors ,
return string.format ( " %s \n %s \n %s%s%s " , title , authors ,
@ -168,12 +170,12 @@ local CalibreSearch = InputContainer:new{
" find_by_path " ,
" find_by_path " ,
} ,
} ,
cache_dir = DataStorage : getDataDir ( ) .. " /cache/calibre " ,
cache_libs = Persist : new {
cache_libs = Persist : new {
path = DataStorage : getDataDir ( ) .. " /cache/calibre - libraries.lua" ,
path = DataStorage : getDataDir ( ) .. " /cache/calibre / libraries.lua" ,
} ,
} ,
cache_books = Persist : new {
cache_books = Persist : new {
path = DataStorage : getDataDir ( ) .. " /cache/calibre - books.dat" ,
path = DataStorage : getDataDir ( ) .. " /cache/calibre / books.dat" ,
codec = " bitser " ,
codec = " bitser " ,
} ,
} ,
}
}
@ -494,6 +496,7 @@ function CalibreSearch:prompt(message)
paths = paths .. " \n " .. _ ( " SD card " ) .. " : " .. sd_paths
paths = paths .. " \n " .. _ ( " SD card " ) .. " : " .. sd_paths
end
end
lfs.mkdir ( self.cache_dir )
self.cache_libs : save ( self.libraries )
self.cache_libs : save ( self.libraries )
self : invalidateCache ( )
self : invalidateCache ( )
self.books = self : getMetadata ( )
self.books = self : getMetadata ( )
@ -561,11 +564,27 @@ function CalibreSearch:getMetadata()
-- try to load metadata from cache
-- try to load metadata from cache
if self.cache_metadata then
if self.cache_metadata then
local function cacheIsNewer ( timestamp )
local function cacheIsNewer ( timestamp )
local file_timestamp = self.cache_books : timestamp ( )
local cache_timestamp = self.cache_books : timestamp ( )
if not timestamp or not file_timestamp then return false end
-- stat returns a true Epoch (UTC)
if not timestamp or not cache_timestamp then return false end
local Y , M , D , h , m , s = timestamp : match ( " (%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+) " )
local Y , M , D , h , m , s = timestamp : match ( " (%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+) " )
local date = os.time ( { year = Y , month = M , day = D , hour = h , min = m , sec = s } )
-- calibre also stores this in UTC (c.f., calibre.utils.date.isoformat)...
return file_timestamp > date
-- But os.time uses mktime, which converts it to *local* time...
-- Meaning we'll have to jump through a lot of stupid hoops to make the two agree...
local meta_timestamp = os.time ( { year = Y , month = M , day = D , hour = h , min = m , sec = s } )
-- To that end, compute the local timezone's offset to UTC via strftime's %z token...
local tz = os.date ( " %z " ) -- +hhmm or -hhmm
-- We deal with a time_t, so, convert that to seconds...
local tz_sign , tz_hours , tz_minutes = tz : match ( " ([+-])(%d%d)(%d%d) " )
local utc_diff = ( tonumber ( tz_hours ) * 60 * 60 ) + ( tonumber ( tz_minutes ) * 60 )
if tz_sign == " - " then
utc_diff = - utc_diff
end
meta_timestamp = meta_timestamp + utc_diff
logger.dbg ( " CalibreSearch:getMetadata: Cache timestamp : " , cache_timestamp , os.date ( " !%FT%T.000000+00:00 " , cache_timestamp ) , os.date ( " (%F %T %z) " , cache_timestamp ) )
logger.dbg ( " CalibreSearch:getMetadata: Metadata timestamp: " , meta_timestamp , timestamp , os.date ( " (%F %T %z) " , meta_timestamp ) )
return cache_timestamp > meta_timestamp
end
end
local cache , err = self.cache_books : load ( )
local cache , err = self.cache_books : load ( )
@ -594,7 +613,7 @@ function CalibreSearch:getMetadata()
local serialized_table = { }
local serialized_table = { }
local function removeNull ( t )
local function removeNull ( t )
for _ , key in ipairs ( { " series " , " series_index " } ) do
for _ , key in ipairs ( { " series " , " series_index " } ) do
if t ype( t [ key ] ) == " function " then
if t [ key ] == rapidjson.null then
t [ key ] = nil
t [ key ] = nil
end
end
end
end
@ -603,7 +622,11 @@ function CalibreSearch:getMetadata()
for index , book in ipairs ( books ) do
for index , book in ipairs ( books ) do
table.insert ( serialized_table , index , removeNull ( book ) )
table.insert ( serialized_table , index , removeNull ( book ) )
end
end
self.cache_books : save ( serialized_table )
lfs.mkdir ( self.cache_dir )
local ok , err = self.cache_books : save ( serialized_table )
if not ok then
logger.info ( " Failed to serialize calibre metadata cache: " , err )
end
end
end
logger.info ( string.format ( template , # books , " calibre " , ( TimeVal : now ( ) - start ) : tomsecs ( ) ) )
logger.info ( string.format ( template , # books , " calibre " , ( TimeVal : now ( ) - start ) : tomsecs ( ) ) )
return books
return books