made download directory configurable

master
Rachel Lambda Samuelsson 2021-07-06 16:25:55 +02:00
parent 2ae1b60cae
commit 319bee7ee6
6 changed files with 69 additions and 25 deletions

10
src/Config.hs 100644
View File

@ -0,0 +1,10 @@
module Config where
import System.Directory
data ViddlConfig = ViddlConfig
{ webPort :: Int
, dlDir :: FilePath }
defConfig :: IO ViddlConfig
defConfig = getTemporaryDirectory >>= pure . ViddlConfig 3000 . (<> "viddl")

View File

@ -3,8 +3,26 @@
module Helpers where module Helpers where
import YTDL import YTDL
import Config
import Text.Read (readMaybe)
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import Network.URI import Network.URI
import System.Exit
parseArgs :: ViddlConfig -> [String] -> IO ViddlConfig
parseArgs _ ("-h":_) = exitHelp
parseArgs _ ("--help":_) = exitHelp
parseArgs cfg@(ViddlConfig _ d) ("-p":p':xs) = case (readMaybe p' :: Maybe Int) of
(Just p) -> parseArgs (ViddlConfig p d) xs
Nothing -> parseArgs cfg (p':xs)
parseArgs (ViddlConfig p _) ("-d":d:xs) = parseArgs (ViddlConfig p d) xs
parseArgs cfg (_:xs) = parseArgs cfg xs
parseArgs cfg [] = pure cfg
exitHelp :: IO a
exitHelp = putStrLn "viddl [-p port] [-d dir]" >> exitSuccess
getRes :: TL.Text -> Maybe Resolution getRes :: TL.Text -> Maybe Resolution
getRes ("144p") = Just P144 getRes ("144p") = Just P144

View File

@ -6,22 +6,28 @@ import Templates.Index
import Templates.Error import Templates.Error
import YTDL import YTDL
import Helpers import Helpers
import Config
import Control.Monad.IO.Class import Control.Monad.Trans.Reader
import Control.Monad.Trans.Class
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import Web.Scotty import System.Environment
import Web.Scotty.Trans
safeDownloadAction :: ActionM () type Scotty = ScottyT TL.Text (ReaderT ViddlConfig IO)
type Action = ActionT TL.Text (ReaderT ViddlConfig IO)
safeDownloadAction :: Action ()
safeDownloadAction = downloadAction `rescue` (html . errorPage) safeDownloadAction = downloadAction `rescue` (html . errorPage)
downloadAction :: ActionM () downloadAction :: Action ()
downloadAction = do downloadAction = do
url <- param "url" url <- param "url"
res <- param "resolution" res <- param "resolution"
if (isURL url) && (isRes res) if (isURL url) && (isRes res)
then do then do
let (Just res') = getRes res -- safe cause we checked with isRes let (Just res') = getRes res -- safe cause we checked with isRes
ytdlRes <- liftIO $ ytdl (TL.unpack url) res' ytdlRes <- lift $ ytdl (TL.unpack url) res'
case ytdlRes of case ytdlRes of
(Right filePath) -> do (Right filePath) -> do
setHeader "content-type" "video/mp4" setHeader "content-type" "video/mp4"
@ -31,11 +37,15 @@ downloadAction = do
html $ errorPage "Invalid input!" html $ errorPage "Invalid input!"
-- todo ReaderT -- todo ReaderT
app :: ScottyM () app :: Scotty ()
app = do app = do
get "/" $ html indexPage get "/" $ html indexPage
get "/video.mp4" safeDownloadAction get "/video.mp4" safeDownloadAction
get "/audio.mp3" safeDownloadAction get "/audio.mp3" safeDownloadAction
main :: IO () main :: IO ()
main = scotty 3000 app main = do
args <- getArgs
defCfg <- defConfig
cfg <- parseArgs defCfg args
scottyT (webPort cfg) (\(ReaderT ma) -> ma cfg) app

View File

@ -1,12 +1,15 @@
module YTDL (Resolution(..), ytdl) where module YTDL (Resolution(..), ytdl) where
import Config
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Class
import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent (forkIO, threadDelay)
import Data.Digest.Pure.MD5 import Data.Digest.Pure.MD5
import qualified Data.ByteString.Lazy.UTF8 as BCU import qualified Data.ByteString.Lazy.UTF8 as BCU
import System.Directory import System.Directory
import System.Exit import System.Exit
import System.Process import System.Process
import System.IO
data Resolution data Resolution
= P144 = P144
@ -33,43 +36,41 @@ resToArgs (P1080) = wrapResString "1080"
resToArgs (PMAX) = ["-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4", "--merge-output-format", "mp4"] resToArgs (PMAX) = ["-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4", "--merge-output-format", "mp4"]
resToArgs (Audio) = ["-x", "--audio-format", "mp3"] resToArgs (Audio) = ["-x", "--audio-format", "mp3"]
ytdl :: String -> Resolution -> IO (Either String FilePath) ytdl :: String -> Resolution -> ReaderT ViddlConfig IO (Either String FilePath)
ytdl url res = do ytdl url res = do
cfg <- ask
let ext = case res of { Audio -> ".mp3"; _ -> ".mp4" } let ext = case res of { Audio -> ".mp3"; _ -> ".mp4" }
let ident = show . md5 . BCU.fromString $ url <> show res let ident = show . md5 . BCU.fromString $ url <> show res
-- todo: config for path let dir = concat [dlDir cfg, "/", ident]
tmpdir <- getTemporaryDirectory
let dir = concat [tmpdir, "/viddl/", ident]
let fileName = concat [dir, "/", ident, ext] let fileName = concat [dir, "/", ident, ext]
-- todo: implement file locking for deleting and uploading file processed <- lift $ doesFileExist fileName
processed <- doesFileExist fileName
if processed if processed
then do then do
putStrLn $ "Returning existing ident " <> ident lift $ putStrLn $ "Returning existing ident " <> ident
pure (Right fileName) pure (Right fileName)
else do else do
putStrLn $ "Processing new ident " <> ident lift $ putStrLn $ "Processing new ident " <> ident
createDirectoryIfMissing True dir lift $ createDirectoryIfMissing True dir
print (resToArgs res <> ["-o", fileName, url]) lift $ print (resToArgs res <> ["-o", fileName, url])
ytdlProc <- createProcess (proc "youtube-dl" (resToArgs res <> ["-o", fileName, url])) ytdlProc <- lift $ createProcess (proc "youtube-dl" (resToArgs res <> ["-o", fileName, url]))
case ytdlProc of case ytdlProc of
(_, _, _, ph) -> do (_, _, _, ph) -> do
exitCode <- waitForProcess ph exitCode <- lift $ waitForProcess ph
case exitCode of case exitCode of
ExitSuccess -> do ExitSuccess -> do
exists <- doesFileExist fileName exists <- lift $ doesFileExist fileName
if exists if exists
then do then do
-- wait 5 minutes and then delete the directory -- wait 5 minutes and then delete the directory
-- todo: implement file locking for deleting and uploading file _ <- lift $ forkIO $ threadDelay 300000000 >> removeDirectoryRecursive dir
_ <- forkIO $ threadDelay 300000000 >> removeDirectoryRecursive dir
pure (Right fileName) pure (Right fileName)
else do else do
-- removeDirectoryRecursive dir -- removeDirectoryRecursive dir

5
todo
View File

@ -1,2 +1,3 @@
configuration for scotty better config options (file?)
cache results somehow safer file deletion
queue system?

View File

@ -17,6 +17,7 @@ library
, Templates.Error , Templates.Error
, YTDL , YTDL
, Helpers , Helpers
, Config
ghc-options: -Wall -O2 -threaded ghc-options: -Wall -O2 -threaded
build-depends: base ^>=4.14.1.0 build-depends: base ^>=4.14.1.0
, scotty , scotty
@ -28,6 +29,7 @@ library
, bytestring , bytestring
, pureMD5 , pureMD5
, utf8-string , utf8-string
, transformers
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010
@ -37,6 +39,7 @@ executable viddl
, Templates.Error , Templates.Error
, Helpers , Helpers
, YTDL , YTDL
, Config
ghc-options: -Wall -O2 -threaded ghc-options: -Wall -O2 -threaded
build-depends: base ^>=4.14.1.0 build-depends: base ^>=4.14.1.0
, scotty , scotty
@ -48,5 +51,6 @@ executable viddl
, bytestring , bytestring
, pureMD5 , pureMD5
, utf8-string , utf8-string
, transformers
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010