packages feed

hastily-0.1.0.6: 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 [] = ["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