svitak/app/EpubParser.hs
2026-01-24 00:56:23 +00:00

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)