hastily-0.1.0.4: app/Main.hs
{-# 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 [] = []::[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