Added basic runner for yt-dlp commands
This commit is contained in:
parent
536f868ba0
commit
34b45d49be
4 changed files with 138 additions and 0 deletions
53
lib/pinchflat/downloader_backends/yt_dlp/command_runner.ex
Normal file
53
lib/pinchflat/downloader_backends/yt_dlp/command_runner.ex
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
defmodule Pinchflat.DownloaderBackends.YtDlp.CommandRunner do
|
||||||
|
@moduledoc """
|
||||||
|
Runs yt-dlp commands using the `System.cmd/3` function
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias Pinchflat.Utils.StringUtils
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Runs a yt-dlp command and returns the output and status
|
||||||
|
|
||||||
|
TODO: look into using a behavior for this (if I ever add other backends)
|
||||||
|
"""
|
||||||
|
def run(url, command_options, config_options \\ []) do
|
||||||
|
default_command = System.find_executable("yt-dlp")
|
||||||
|
base_command = Keyword.get(config_options, :base_command, default_command)
|
||||||
|
formatted_command_options = parse_options(command_options) ++ [url]
|
||||||
|
|
||||||
|
case System.cmd(base_command, formatted_command_options, stderr_to_stdout: true) do
|
||||||
|
{output, 0} -> {:ok, output}
|
||||||
|
{output, status} -> {:error, output, status}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# We want to satisfy the following behaviours:
|
||||||
|
#
|
||||||
|
# 1. If the key is an atom, convert it to a string and convert it to kebab case (for convenience)
|
||||||
|
# 2. If the key is a string, assume we want it as-is and don't convert it
|
||||||
|
# 3. If the key is accompanied by a value, append the value to the list
|
||||||
|
# 4. If the key is not accompanied by a value, assume it's a flag and PREpend it to the list
|
||||||
|
defp parse_options(command_options) do
|
||||||
|
Enum.reduce(command_options, [], &parse_option/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_option({k, v}, acc) when is_atom(k) do
|
||||||
|
stringified_key = StringUtils.to_kebab_case(Atom.to_string(k))
|
||||||
|
|
||||||
|
parse_option({"--#{stringified_key}", v}, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_option({k, v}, acc) when is_binary(k) do
|
||||||
|
acc ++ [k, to_string(v)]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_option(arg, acc) when is_atom(arg) do
|
||||||
|
stringified_arg = StringUtils.to_kebab_case(Atom.to_string(arg))
|
||||||
|
|
||||||
|
parse_option("--#{stringified_arg}", acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_option(arg, acc) when is_binary(arg) do
|
||||||
|
[arg | acc]
|
||||||
|
end
|
||||||
|
end
|
||||||
14
lib/utils/string_utils.ex
Normal file
14
lib/utils/string_utils.ex
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule Pinchflat.Utils.StringUtils do
|
||||||
|
@moduledoc """
|
||||||
|
Utility functions for working with strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Converts a string to kebab-case (ie: `hello world` -> `hello-world`)
|
||||||
|
"""
|
||||||
|
def to_kebab_case(string) do
|
||||||
|
string
|
||||||
|
|> String.replace(~r/[\s_]/, "-")
|
||||||
|
|> String.downcase()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
defmodule Pinchflat.DownloaderBackends.YtDlp.CommandRunnerTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
alias Pinchflat.DownloaderBackends.YtDlp.CommandRunner, as: Runner
|
||||||
|
|
||||||
|
@cmd "echo"
|
||||||
|
@video_url "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||||
|
|
||||||
|
describe "run/3" do
|
||||||
|
test "it returns the output and status when the command succeeds" do
|
||||||
|
assert {:ok, _output} = Runner.run(@video_url, [], base_command: @cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it converts symbol k-v arg keys to kebab case" do
|
||||||
|
assert {:ok, output} = Runner.run(@video_url, [buffer_size: 1024], base_command: @cmd)
|
||||||
|
|
||||||
|
assert String.contains?(output, "--buffer-size 1024")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it keeps string k-v arg keys untouched" do
|
||||||
|
assert {:ok, output} = Runner.run(@video_url, [{"--under_score", 1024}], base_command: @cmd)
|
||||||
|
|
||||||
|
assert String.contains?(output, "--under_score 1024")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it converts symbol arg keys to kebab case" do
|
||||||
|
assert {:ok, output} = Runner.run(@video_url, [:ignore_errors], base_command: @cmd)
|
||||||
|
|
||||||
|
assert String.contains?(output, "--ignore-errors")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it keeps string arg keys untouched" do
|
||||||
|
assert {:ok, output} = Runner.run(@video_url, ["-v"], base_command: @cmd)
|
||||||
|
|
||||||
|
assert String.contains?(output, "-v")
|
||||||
|
refute String.contains?(output, "--v")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it places arg keys (flags) at the beginning of the command" do
|
||||||
|
assert {:ok, output} =
|
||||||
|
Runner.run(@video_url, [{"--under_score", 1024}, :ignore_errors], base_command: @cmd)
|
||||||
|
|
||||||
|
assert String.contains?(output, "--ignore-errors --under_score 1024")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it includes the video url as the last argument" do
|
||||||
|
assert {:ok, output} = Runner.run(@video_url, [:ignore_errors], base_command: @cmd)
|
||||||
|
|
||||||
|
assert String.contains?(output, "--ignore-errors #{@video_url}\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns the output and status when the command fails" do
|
||||||
|
assert {:error, "", 1} = Runner.run(@video_url, [], base_command: "/bin/false")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
15
test/pinchflat/utils/string_utils_test.exs
Normal file
15
test/pinchflat/utils/string_utils_test.exs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pinchflat.Utils.StringUtilsTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
alias Pinchflat.Utils.StringUtils, as: StringUtils
|
||||||
|
|
||||||
|
describe "to_kebab_case/1" do
|
||||||
|
test "converts a space-delimited string to kebab-case" do
|
||||||
|
assert StringUtils.to_kebab_case("hello world") == "hello-world"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "converts an underscore-delimited string to kebab-case" do
|
||||||
|
assert StringUtils.to_kebab_case("hello_world") == "hello-world"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue