packages feed

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)