made download directory configurable
This commit is contained in:
parent
2ae1b60cae
commit
319bee7ee6
10
src/Config.hs
Normal file
10
src/Config.hs
Normal 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")
|
|
@ -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
|
||||||
|
|
24
src/Main.hs
24
src/Main.hs
|
@ -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
|
||||||
|
|
33
src/YTDL.hs
33
src/YTDL.hs
|
@ -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
5
todo
|
@ -1,2 +1,3 @@
|
||||||
configuration for scotty
|
better config options (file?)
|
||||||
cache results somehow
|
safer file deletion
|
||||||
|
queue system?
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user