{-# LANGUAGE OverloadedStrings #-} module Main where import Control.Exception import Control.Monad import Data.Maybe import Data.String import Data.String.Conversions import Data.Text (Text, intercalate) import System.Console.GetOpt import System.Directory import System.Environment (getArgs) import Text.Printf import qualified Text.Hastily.MovieInfoSources.Omdb.Omdb as Omdb import qualified Text.Hastily.MovieSubtitleSources.OpenSubtitles.OpenSubtitles as OpenSubtitles import qualified Text.Hastily.Report as Report import qualified Text.Hastily.SubtitleFileTypes.Srt.Srt as Srt import Text.Hastily.Types data Flag = MovieNameSlug String|Language String|DiscCount String|ReleaseNameSlug String| SkipWord String|TargetDir String|SkipNetwork deriving (Show) main :: IO () main = do (movie_name, language, disc_count, release_name, skip_words, target, skip_network) <- parseOptions (movie, movie_files) <- if not skip_network then do either_err_movie_info <- Omdb.getMovieInfo movie_name case either_err_movie_info of Left err -> error $ show err Right (Omdb.OMDBSearchResult movie_matches) -> do right_movie <- getUserSelection movie_matches try $ createDirectory (cs target):: IO (Either SomeException ()) movie_files <- downloadSubtitleFromSources right_movie language disc_count release_name (cs target) [OpenSubtitles.getSubtitles] return (right_movie, movie_files) else do movie_files <- Srt.getSrtFilesInDir $ cs target return (MovieInfo "dummy" "" "" "", movie_files) subtitles <- mapM (Srt.parseFile movie) movie_files Report.generate skip_words subtitles where downloadSubtitleFromSources :: MovieInfo -> Text -> Text -> Maybe Text -> FilePath -> [(MovieInfo -> Text -> Text -> Maybe Text -> FilePath -> IO [FilePath])] -> IO [FilePath] downloadSubtitleFromSources mi lang discs release target xs = do either_list <- mapM applyArgs xs gatherFiles either_list where applyArgs :: (MovieInfo -> Text -> Text -> Maybe Text -> FilePath -> IO [FilePath]) -> IO (Either SomeException [FilePath]) applyArgs f = try $ f mi lang discs release target gatherFiles :: [(Either SomeException [FilePath])] -> IO [FilePath] gatherFiles either_files_list = do path_lists <- mapM getFiles either_files_list return $ concat path_lists getFiles :: Either SomeException [FilePath] -> IO [FilePath] getFiles (Right files) = return files getFiles (Left exception) = do putStrLn $ show $ exception return [] options :: [OptDescr Flag] options = [ Option ['n'] ["name"] (ReqArg MovieNameSlug " moviename") "Full or partial movie name.", Option ['l'] ["language"] (ReqArg Language " subtitlelanguage") "Required subtitle language", Option ['d'] ["discs"] (ReqArg DiscCount " noofdiscs") "No of discs/files", Option ['s'] ["skipword"] (ReqArg SkipWord " skipword") "Skip dialogues with these words when searching for candidate dialogue.", Option ['t'] ["target-dir"] (ReqArg TargetDir " targetdirectory") "Target directory to store subtitles.", Option ['r'] ["release-name"] (ReqArg ReleaseNameSlug " releasename") "A partial string of a release name to prefer eg BRRip, 720p", Option ['i'] ["skip-network"] (NoArg SkipNetwork) "Do not fetch movie details or subtitles. Instead just print report using subtitles in the target directory." ] parseOptions :: IO (Text, Text, Text, Maybe Text, [Text], Text, Bool) parseOptions = do argv <- getArgs case getOpt Permute options argv of ([], _, errors) -> printError errors options (_, item, errors@(x:xs)) -> printError errors options (options, [], []) -> do return $ makeOptionsFrom options _ -> error "Bad parameters!" where printError errors options = ioError $ userError $ concat errors ++ usageInfo "Usage:" options makeOptionsFrom :: [Flag] -> (Text, Text, Text, Maybe Text, [Text], Text, Bool) makeOptionsFrom options = (getMovieName options, getLanguage options, getDiscs options, getReleaseName options, getSkipWords options, getTargetDir options, getSkipNetwork options) getMovieName [] = error "A partial movie name required!"::Text getMovieName (o:os) = case o of MovieNameSlug m -> cs m; _ -> getMovieName os getLanguage [] = "english"::Text getLanguage (o:os) = case o of Language m -> cs m _ -> getLanguage os getDiscs [] = "1"::Text getDiscs (o:os) = case o of DiscCount m -> cs m; _ -> getDiscs os getReleaseName [] = Nothing::Maybe Text getReleaseName (o:os) = case o of ReleaseNameSlug m -> Just (cs m); _ -> getReleaseName os getSkipWords [] = ["advertise"]::[Text] getSkipWords (o:os) = case o of SkipWord x -> [cs x] ++ (getSkipWords os); _ -> getSkipWords os getTargetDir [] = "subtitles"::Text getTargetDir (o:os) = case o of TargetDir x -> (cs x); _ -> getTargetDir os getSkipNetwork [] = False getSkipNetwork (o:os) = case o of SkipNetwork -> True; _ -> getSkipNetwork os getUserSelection :: [MovieInfo] -> IO MovieInfo getUserSelection movie_matches = do showMatches movie_matches getSelection movie_matches getSelection :: [MovieInfo] -> IO MovieInfo getSelection movie_matches = do selected_index <- getNumberBetween 1 movie_count return $ movie_matches !! (selected_index - 1) where movie_count = length movie_matches showMatches :: [MovieInfo] -> IO () showMatches ms = zipWithM_ printMovieInfo [(1::Integer)..] ms where printMovieInfo index movie_info = do putStrLn $ printf "%d. %s" index $ show movie_info getNumberBetween :: Int -> Int -> IO Int getNumberBetween start end = do putStrLn $ printf "Enter a number between 1 and %d, or q to quit." end input <- getLine if input == "q" then error "User abort!" else case reads input :: [(Int, String)] of [] -> getNumberBetween start end [(index, "")] -> if index >= start && index <= end then return index else getNumberBetween start end _ -> getNumberBetween start end