59 lines
2 KiB
Haskell
59 lines
2 KiB
Haskell
module EpubParser where
|
|
|
|
import Codec.Archive.Zip (toArchive, findEntryByPath, fromEntry, Archive)
|
|
import qualified Data.ByteString.Lazy as BL
|
|
import qualified Data.ByteString.Lazy.Char8 as C8
|
|
import Text.HTML.TagSoup (parseTags, Tag)
|
|
import Text.HTML.Scalpel (scrape, text, tagSelector, attr)
|
|
import System.FilePath (takeDirectory)
|
|
import Control.Monad.Reader
|
|
import qualified Data.Text as T
|
|
import Data.Maybe (fromMaybe)
|
|
|
|
type EpubAction a = ReaderT EpubEnv IO a
|
|
|
|
data EpubEnv = EpubEnv
|
|
{ title :: T.Text
|
|
, author :: T.Text
|
|
, rootPrefix :: FilePath
|
|
} deriving (Show)
|
|
|
|
myEnv :: Archive -> [Tag String] -> EpubEnv
|
|
myEnv archive tags =
|
|
let
|
|
opfPath = fromMaybe "" (getOpfPath archive)
|
|
prefix = getRootPrefix opfPath
|
|
|
|
in
|
|
EpubEnv
|
|
{ title = getTagText "dc:title" tags
|
|
, author = getTagText "dc:creator" tags
|
|
, rootPrefix = prefix
|
|
}
|
|
|
|
openEpub :: FilePath -> IO (Either String Archive)
|
|
openEpub path = toArchive <$> BL.readFile path >>= \archive -> maybe (pure $ Left "Error reading file") (\entry -> if fromEntry entry == C8.pack "application/epub+zip" then pure (Right archive) else pure (Left "Wrong mimetype")) (findEntryByPath "mimetype" archive)
|
|
|
|
getOpfPath :: Archive -> Maybe FilePath
|
|
getOpfPath archive = do
|
|
entry <- findEntryByPath "META-INF/container.xml" archive
|
|
let tags = parseTags $ C8.unpack (fromEntry entry)
|
|
-- Logic: scrape (the_scraper) (the_tags)
|
|
scrape (attr "full-path" (tagSelector "rootfile")) tags
|
|
|
|
getOpfTags :: Archive -> FilePath -> [Tag String]
|
|
getOpfTags archive opfpath =
|
|
case findEntryByPath opfpath archive of
|
|
Just entry -> parseTags $ C8.unpack (fromEntry entry)
|
|
Nothing -> []
|
|
|
|
getRootPrefix :: FilePath -> FilePath
|
|
getRootPrefix path =
|
|
let dir = takeDirectory path
|
|
in if dir == "." then "" else dir ++ "/"
|
|
|
|
getTagText :: String -> [Tag String] -> T.Text
|
|
getTagText tagName tags =
|
|
let scraper = text (tagSelector tagName)
|
|
in T.pack $ fromMaybe "" (scrape scraper tags)
|
|
|