module Text.Hastily.MovieSubtitleSources.OpenSubtitles.OpenSubtitles
(
getSubtitles
) where
import Control.Exception
import Control.Monad
import qualified Data.ByteString as BS
import Data.ByteString.Char8 as C (split)
import Data.String.Conversions
import Data.Text hiding
(filter,
length,
zip,
zip3,
zipWith)
import Network.HTTP.Client
import System.Directory
import System.FilePath
import Text.Printf
import Text.XML.HXT.Core
import qualified Text.Hastily.MovieSubtitleSources.OpenSubtitles.Languages as Ops
import Text.Hastily.Network
import Text.Hastily.SubtitleFileTypes.Srt.Srt
import Text.Hastily.Types
import Text.Hastily.Unpack.Zip.ZipExtractor
zip4 a b c d = zipWith doZip (zip3 a b c) d
where
doZip (a, b, c) d = (a, b, c, d)
getLang :: Text -> Text
getLang lang = case lookup lang Ops.languages of
Just lan -> lan
_ -> error $ cs ( "List of available languages \n" ++ (cs $ intercalate "\n" $ fmap (cs.fst) Ops.languages) ++ "\n\ncannot find language '" ++ (cs lang::String) ++ "'.\n" ::String)
getSubtitles :: MovieInfo -> Text -> Text -> Maybe Text -> FilePath -> IO [FilePath]
getSubtitles movie_info lang cd_count maybe_prefer_slug dst_folder = do
let language = getLang lang
links <- getSubtitleLinks movie_info language 0 []
try $ createDirectory dstSubFolder :: IO (Either SomeException ())
zipWithM_ (\a b -> extractFromEitherPath $ downloadLink dstSubFolder a b) (filter filterLink links) [1..]
getSrtFilesInDir dstSubFolder
where
filterLink :: (String, String, String, String) -> Bool
filterLink (link, sub_language, sub_release_name, sub_cd_count) =
case maybe_prefer_slug of
Nothing -> cd_count == (cs sub_cd_count)
Just slug -> let slug_lc = toLower $ slug
release_name_lc = toLower $ (cs sub_release_name::Text)
in cd_count == (cs sub_cd_count) && (isInfixOf slug_lc release_name_lc)
dstSubFolder = dst_folder </> "opensubtitles.org"
extractFromEitherPath :: IO (Either SomeException FilePath) -> IO (Either SomeException ())
extractFromEitherPath io_either_path = do
either_path <- io_either_path
case either_path of
Left err -> return $ Left err
Right file_path -> extractFiles file_path
getSubtitleLinks
:: MovieInfo
-> Text
-> Integer
-> [(String, String, String, String)]
-> IO [(String, String, String, String)]
getSubtitleLinks movie_info language offset result = do
(total, item_count, links) <- getResults movie_info language offset
let new_result = result ++ links
let link_count = offset + item_count
if item_count > link_count
then getSubtitleLinks movie_info language (link_count) new_result
else return new_result
getResults
:: MovieInfo
-> Text
-> Integer
-> IO (Integer, Integer, [(String, String, String, String)])
getResults movie_info language offset = do
result_xml <- getResultXml movie_info language offset
tc <- itemCount result_xml
ic <- responseItemCount result_xml
sub_details <- runX $ result_xml >>> getSubtitleDetails
let cd_count = []
let language = []
let movie_release_names = []
let subtitle_links = []
return (tc, ic, sub_details)
where
getSubtitleDetails = deep (isElem >>>hasName "subtitle") >>>
proc x -> do
subtitle_link <- getAttrValue "LinkDownload" <<< deep (hasName "IDSubtitle") -< x
release_name <- getText <<< getChildren <<< deep (hasName "MovieReleaseName") -< x
cd_count <- getText <<< getChildren <<< deep (hasName "SubSumCD") -< x
language <- getText <<< getChildren <<< deep (hasName "LanguageName") -< x
returnA -< (subtitle_link, language, release_name, cd_count)
getResultXml movie_info language offset = do
response <- (getXmlResponse movie_info language offset)
return $ response >>> (deepest (hasName "search")) >>> getChildren >>> (hasName "results")
itemCount result_xml = do
item_count_result <- runX ( result_xml >>> getAttrValue "itemsfound")
if (length item_count_result) == 0
then error "opensubtitles.org: No subtitles found!"
else return $ (read $ item_count_result!!0::Integer)
responseItemCount result_xml = do
item_count_result <- runX ( result_xml >>> getAttrValue "items")
if (length item_count_result) == 0
then error "opensubtitles.org: No subtitles found!"
else return $ (read $ item_count_result!!0::Integer)
getXmlResponse movie_info language offset = do
either_error_response_text <- getFrom (makeUrl (imdb movie_info) language offset) []
case either_error_response_text of
Left err -> error "Quering opensubtitles.org failed!"
Right response_text -> return $ readString [ withValidate no , withRemoveWS yes] $ cs response_text
where
makeUrl :: Text -> Text -> Integer -> String
makeUrl imdb language offset = printf "http://www.opensubtitles.org/en/search/sublanguageid-%s/imdbid-%s/offset-%s/xml" (cs language::String) (cs imdb::String) (show offset)
downloadLink
:: Show a =>
FilePath
-> (String, t, t1, t2) -> a -> IO (Either SomeException FilePath)
downloadLink dir (link, _, _, _) index = do
putStrLn $ printf "Downloading %s" link
request <- parseUrl link
full_path <- fullPath dir request
either_err <- getFromUrlAndDo link [] (saveResponse full_path (show index))
case either_err of
Left err -> do
return $ Left err
Right a -> return $ Right a
where
saveResponse path index response = do
putStrLn $ printf "Saving to %s" path
response_content <- brConsume $ responseBody response
BS.writeFile path (BS.intercalate "" response_content)
return path
fullPath dir request = do
let f_name = fileName request
created <- try $ createDirectory (dir </> f_name) :: IO (Either SomeException ())
case created of
Left err -> error $ "ERROR: Cannot create directory " ++ (dir </> f_name)
Right () -> return $ dir </> f_name </> f_name
where
fileName :: Request -> FilePath
fileName request = cs $ Prelude.last $ C.split '/' (path request)