cut-the-crap-2.2.0: src/Cut/Download.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
-- | Deal with downloading and the cli options involved
module Cut.Download
( downloadIfNeccisary
, downloadCutifNeccisary
)
where
import Control.Lens
import Control.Monad
import Control.Monad.Catch
import Control.Monad.IO.Class
import Cut.Options
import Cut.Shell
import Data.Foldable (traverse_)
import Data.Word
import Network.URI (URI)
import Options.Applicative
import Shelly hiding (FilePath, shelly)
import System.Random
-- | Downloads a URI to the filepath returned
runYoutubeDL :: FileIO a -> URI -> IO FilePath
runYoutubeDL opts x = do
-- we need to make our own because youtube-dl doesn't write to existing files
-- System.IO.Temp would've worked neatly here.
inputNumbers :: Word32 <- randomIO
let inputChars :: String
-- youtube-dl doesn't do .mkv (not supported)
inputChars = show inputNumbers
filePath :: String
filePath = maybe inputChars (flip (</>) inputChars) $ opts ^. work_dir
resultPath = filePath <> ".mkv"
shelly $ do
void $ youtube_dl x filePath
-- (#52): fix bug where youtube-dl doesn't always create .mkv file output
out <- test_f filePath
when out $ mv filePath resultPath
pure resultPath -- because 'youtube_dl' sets merge-output-format
aquireFilePath :: FileIO InputSource -> IO (FileIO FilePath)
aquireFilePath x = do
result <- sequence $ (runYoutube <|> alreadyLocal)
case result of
Nothing -> error $ "Couldn't find " <> show x
Just y -> pure $ x & in_file .~ y
where
runYoutube :: Maybe (IO FilePath)
runYoutube = x ^? in_file . input_src_remote . to (runYoutubeDL x)
alreadyLocal :: Maybe (IO FilePath)
alreadyLocal = preview (in_file . input_src_local_file . to pure) x
removeDownloaded :: FileIO InputSource -> FileIO FilePath -> IO ()
removeDownloaded cliInput aquiredPath =
traverse_ (const $ -- only do this if we got it from remote (we did the download)
traverse_ (shelly . rm) $ aquiredPath ^? in_file
) $ cliInput ^? in_file . input_src_remote
-- | 'FileIO' can have a remote, if so we download that and pass the downloaded
-- path to the continuation, if not we simply pass the input path to
-- the continuation.
-- We need a continuation to clean up the downloaded file.
downloadIfNeccisary :: MonadMask m => MonadIO m => FileIO InputSource -> (FileIO FilePath -> m a) -> m a
downloadIfNeccisary x = bracket (liftIO $ aquireFilePath x) (liftIO . removeDownloaded x)
-- | this does the same as 'downloadIfNeccisary' but makes it work for
-- 'ListenCutOptionsT' with some type weaving
downloadCutifNeccisary :: MonadMask m => MonadIO m => ListenCutOptionsT InputSource -> ((ListenCutOptionsT FilePath) -> m a) -> m a
downloadCutifNeccisary cut fun =
downloadIfNeccisary (cut ^. lc_fileio) $
fun . (\y -> lc_fileio .~ y $ cut)