forked from marko/svitak
implement generic bookinfo and move logic to parser
This commit is contained in:
parent
55b04ecf44
commit
80be08965e
3 changed files with 49 additions and 13 deletions
|
|
@ -17,7 +17,7 @@ import Codec.Epub.Parse (getManifest, getMetadata, getSpine)
|
|||
import Control.Applicative ((<|>))
|
||||
import Control.Monad (guard)
|
||||
import Control.Monad.Except (runExceptT)
|
||||
import Control.Monad.Reader (MonadReader (ask), liftIO)
|
||||
import Control.Monad.Reader (MonadReader (ask), liftIO, runReaderT)
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Lazy as B
|
||||
import Data.List (find)
|
||||
|
|
@ -26,8 +26,35 @@ import qualified Data.Text as T
|
|||
import qualified Data.Text.Encoding as TE
|
||||
import System.FilePath (normalise, takeDirectory, (</>))
|
||||
import Text.HTML.Scalpel (Scraper, ScraperT, anySelector, attr, chroot, chroots, html, innerHTML, scrapeStringLike, text)
|
||||
import Types (Chapter (..), EpubAction, EpubEnv (..), InternalPath(..), ChapterIndex(..))
|
||||
import Types (Chapter (..), EpubAction, EpubEnv (..), InternalPath(..), ChapterIndex(..), BookInfo(..), EpubType(..))
|
||||
import Navigation (navFinder, lndmkScraper)
|
||||
import Codec.Epub.Data.Metadata (metaTitles, titleText, titleType, Creator (creatorText), Metadata (metaCreators))
|
||||
|
||||
instance BookInfo EpubEnv where
|
||||
getTitle env =
|
||||
let titles = metaTitles (eMetadata env)
|
||||
in case find (\t -> titleType t == Just "main") titles of
|
||||
Just t -> T.pack (titleText t)
|
||||
Nothing -> if null titles then "Unknown" else T.pack (titleText (head titles))
|
||||
|
||||
getAuthors env = map (T.pack . creatorText) (metaCreators (eMetadata env))
|
||||
|
||||
getLandmarks env =
|
||||
let xmlStr = opfXml env
|
||||
maybeNavPath = scrapeStringLike xmlStr navFinder
|
||||
in case maybeNavPath of
|
||||
Nothing -> []
|
||||
Just path ->
|
||||
let full = normalise (baseDir env </> path)
|
||||
in case findEntryByPath full (archive env) of
|
||||
Nothing -> []
|
||||
Just entry ->
|
||||
let raw = TE.decodeUtf8 . B.toStrict $ fromEntry entry
|
||||
in fromMaybe [] (scrapeStringLike raw (lndmkScraper [EpubType "toc", EpubType "landmarks", EpubType "bodymatter"]))
|
||||
|
||||
getBookPath = bookPath
|
||||
getSpine = runReaderT resolveSpine
|
||||
loadChapter env path idx = runReaderT (getChapter path idx) env
|
||||
|
||||
openEpub :: FilePath -> IO (Either String EpubEnv)
|
||||
openEpub path = do
|
||||
|
|
@ -79,7 +106,7 @@ resolveSpine = do
|
|||
env <- ask
|
||||
let xmlStr = opfXml env
|
||||
|
||||
spineResult <- liftIO $ runExceptT $ getSpine xmlStr
|
||||
spineResult <- liftIO $ runExceptT $ Codec.Epub.Parse.getSpine xmlStr
|
||||
let (DMan.Manifest items) = eManifest env
|
||||
|
||||
case spineResult of
|
||||
|
|
|
|||
10
app/Types.hs
10
app/Types.hs
|
|
@ -11,6 +11,7 @@ module Types
|
|||
ChapterIndex(..),
|
||||
InternalPath(..),
|
||||
EpubType(..),
|
||||
BookInfo(..)
|
||||
)
|
||||
where
|
||||
|
||||
|
|
@ -22,6 +23,15 @@ import Control.Concurrent.Async (Async)
|
|||
import Control.Monad.Reader (ReaderT)
|
||||
import Data.String (IsString)
|
||||
|
||||
-- Unified interface for EPub 2 and EPub 3 formats, allows generic programming of both without having to repeat boilerplate
|
||||
class BookInfo a where
|
||||
getTitle :: a -> T.Text
|
||||
getAuthors :: a -> [T.Text]
|
||||
getLandmarks :: a -> [NavItem]
|
||||
getBookPath :: a -> FilePath
|
||||
getSpine :: a -> IO [(InternalPath, T.Text)]
|
||||
loadChapter :: a -> (InternalPath, T.Text) -> ChapterIndex -> IO Chapter
|
||||
|
||||
newtype ChapterIndex = ChapterIndex Int
|
||||
deriving (Show, Eq, Ord, Num)
|
||||
|
||||
|
|
|
|||
19
app/UI.hs
19
app/UI.hs
|
|
@ -22,7 +22,7 @@ import qualified GI.WebKit as WebKit
|
|||
import Persistence (loadLastRead, saveLastRead)
|
||||
import State (initialState, prepareView)
|
||||
import Text.HTML.Scalpel (Scraper, attr, chroots, text)
|
||||
import Types (AppState (..), ChapterView (..), UserAction (..), ChapterIndex(..))
|
||||
import Types (AppState (..), ChapterView (..), UserAction (..), ChapterIndex(..), BookInfo (..))
|
||||
|
||||
updateUI :: Gtk.ApplicationWindow -> WebKit.WebView -> ChapterView -> ChapterIndex -> Int -> IO ()
|
||||
updateUI window webView view (ChapterIndex currentIdx) totalCount = do
|
||||
|
|
@ -62,23 +62,22 @@ updateZoom stateTVar webView level = do
|
|||
atomically $ modifyTVar stateTVar $ \s -> s {zoomlvl = level}
|
||||
WebKit.webViewSetZoomLevel webView level
|
||||
|
||||
initAppServices :: EpubEnv -> TVar AppState -> (UserAction -> IO ()) -> IO ()
|
||||
initAppServices :: (BookInfo a) => a -> TVar AppState -> (UserAction -> IO ()) -> IO ()
|
||||
initAppServices env stateTVar actionHandler = do
|
||||
_ <- async $ do
|
||||
ispine <- runReaderT resolveSpine env
|
||||
allChapters <- runReaderT (zipWithM getChapter ispine (map ChapterIndex [0 ..])) env
|
||||
ispine <- getSpine env
|
||||
|
||||
let titles = metaTitles (eMetadata env)
|
||||
let bookTitle = case find (\t -> titleType t == Just "main") titles of
|
||||
Just t -> titleText t
|
||||
Nothing -> if null titles then "Unknown" else titleText (head titles)
|
||||
allChapters <- zipWithM (loadChapter env) ispine (map ChapterIndex [0 ..])
|
||||
|
||||
atomically $ modifyTVar stateTVar $ \s -> s {cSpine = ispine, cAllChapters = allChapters, bTitle = T.pack bookTitle}
|
||||
let bookTitle = getTitle env
|
||||
|
||||
atomically $ modifyTVar stateTVar $ \s ->
|
||||
s {cSpine = ispine, cAllChapters = allChapters, bTitle = bookTitle}
|
||||
|
||||
_ <- GLib.idleAdd GLibConst.PRIORITY_DEFAULT $ do
|
||||
maybeSaved <- loadLastRead
|
||||
case maybeSaved of
|
||||
Just (path, idx) | path == bookPath env -> actionHandler (LoadSpecific idx)
|
||||
Just (path, idx) | path == getBookPath env -> actionHandler (LoadSpecific idx)
|
||||
_ -> actionHandler (LoadSpecific 0)
|
||||
pure False
|
||||
pure ()
|
||||
|
|
|
|||
Loading…
Reference in a new issue