Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d97c8c1c4 | |||
| 030f5fbdfe | |||
| ee2db3e9b7 | |||
| 4554648ba7 |
@@ -15,7 +15,9 @@ defmodule Pinchflat.Settings.Setting do
|
|||||||
:video_codec_preference,
|
:video_codec_preference,
|
||||||
:audio_codec_preference,
|
:audio_codec_preference,
|
||||||
:youtube_api_key,
|
:youtube_api_key,
|
||||||
:extractor_sleep_interval_seconds
|
:extractor_sleep_interval_seconds,
|
||||||
|
:download_throughput_limit,
|
||||||
|
:restrict_filenames
|
||||||
]
|
]
|
||||||
|
|
||||||
@required_fields [
|
@required_fields [
|
||||||
@@ -35,6 +37,9 @@ defmodule Pinchflat.Settings.Setting do
|
|||||||
field :youtube_api_key, :string
|
field :youtube_api_key, :string
|
||||||
field :route_token, :string
|
field :route_token, :string
|
||||||
field :extractor_sleep_interval_seconds, :integer, default: 0
|
field :extractor_sleep_interval_seconds, :integer, default: 0
|
||||||
|
# This is a string because it accepts values like "100K" or "4.2M"
|
||||||
|
field :download_throughput_limit, :string
|
||||||
|
field :restrict_filenames, :boolean, default: false
|
||||||
|
|
||||||
field :video_codec_preference, :string
|
field :video_codec_preference, :string
|
||||||
field :audio_codec_preference, :string
|
field :audio_codec_preference, :string
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
|||||||
|
|
||||||
output_filepath = generate_output_filepath(addl_opts)
|
output_filepath = generate_output_filepath(addl_opts)
|
||||||
print_to_file_opts = [{:print_to_file, output_template}, output_filepath]
|
print_to_file_opts = [{:print_to_file, output_template}, output_filepath]
|
||||||
user_configured_opts = cookie_file_options(addl_opts) ++ sleep_interval_opts(addl_opts)
|
user_configured_opts = cookie_file_options(addl_opts) ++ rate_limit_options(addl_opts) ++ misc_options()
|
||||||
# These must stay in exactly this order, hence why I'm giving it its own variable.
|
# These must stay in exactly this order, hence why I'm giving it its own variable.
|
||||||
all_opts = command_opts ++ print_to_file_opts ++ user_configured_opts ++ global_options()
|
all_opts = command_opts ++ print_to_file_opts ++ user_configured_opts ++ global_options()
|
||||||
formatted_command_opts = [url] ++ CliUtils.parse_options(all_opts)
|
formatted_command_opts = [url] ++ CliUtils.parse_options(all_opts)
|
||||||
@@ -116,20 +116,6 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp sleep_interval_opts(addl_opts) do
|
|
||||||
sleep_interval = Settings.get!(:extractor_sleep_interval_seconds)
|
|
||||||
|
|
||||||
if sleep_interval <= 0 || Keyword.get(addl_opts, :skip_sleep_interval) do
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
[
|
|
||||||
sleep_requests: NumberUtils.add_jitter(sleep_interval),
|
|
||||||
sleep_interval: NumberUtils.add_jitter(sleep_interval),
|
|
||||||
sleep_subtitles: NumberUtils.add_jitter(sleep_interval)
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp add_cookie_file do
|
defp add_cookie_file do
|
||||||
base_dir = Application.get_env(:pinchflat, :extras_directory)
|
base_dir = Application.get_env(:pinchflat, :extras_directory)
|
||||||
filename_options_map = %{cookies: "cookies.txt"}
|
filename_options_map = %{cookies: "cookies.txt"}
|
||||||
@@ -145,6 +131,32 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp rate_limit_options(addl_opts) do
|
||||||
|
throughput_limit = Settings.get!(:download_throughput_limit)
|
||||||
|
sleep_interval_opts = sleep_interval_opts(addl_opts)
|
||||||
|
throughput_option = if throughput_limit, do: [limit_rate: throughput_limit], else: []
|
||||||
|
|
||||||
|
throughput_option ++ sleep_interval_opts
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sleep_interval_opts(addl_opts) do
|
||||||
|
sleep_interval = Settings.get!(:extractor_sleep_interval_seconds)
|
||||||
|
|
||||||
|
if sleep_interval <= 0 || Keyword.get(addl_opts, :skip_sleep_interval) do
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
[
|
||||||
|
sleep_requests: NumberUtils.add_jitter(sleep_interval),
|
||||||
|
sleep_interval: NumberUtils.add_jitter(sleep_interval),
|
||||||
|
sleep_subtitles: NumberUtils.add_jitter(sleep_interval)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp misc_options do
|
||||||
|
if Settings.get!(:restrict_filenames), do: [:restrict_filenames], else: []
|
||||||
|
end
|
||||||
|
|
||||||
defp backend_executable do
|
defp backend_executable do
|
||||||
Application.get_env(:pinchflat, :yt_dlp_executable)
|
Application.get_env(:pinchflat, :yt_dlp_executable)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -47,7 +47,21 @@
|
|||||||
placeholder="0"
|
placeholder="0"
|
||||||
type="number"
|
type="number"
|
||||||
label="Sleep Interval (seconds)"
|
label="Sleep Interval (seconds)"
|
||||||
help="Sleep interval in seconds between each extractor request. Must be a positive whole number (or set to 0 to disable)"
|
help="Sleep interval in seconds between each extractor request. Must be a positive whole number. Set to 0 to disable"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<.input
|
||||||
|
field={f[:download_throughput_limit]}
|
||||||
|
placeholder="4.2M"
|
||||||
|
label="Download Throughput"
|
||||||
|
help="Sets the max bytes-per-second throughput when downloading media. Examples: '50K' or '4.2M'. Leave blank to disable"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<.input
|
||||||
|
field={f[:restrict_filenames]}
|
||||||
|
type="toggle"
|
||||||
|
label="Restrict Filenames"
|
||||||
|
help="Restrict filenames to only ASCII characters and avoid ampersands/spaces in filenames"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ defmodule PinchflatWeb.Endpoint do
|
|||||||
cookie_key: "request_logger"
|
cookie_key: "request_logger"
|
||||||
|
|
||||||
plug Plug.RequestId
|
plug Plug.RequestId
|
||||||
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
|
|
||||||
|
plug Plug.Telemetry,
|
||||||
|
event_prefix: [:phoenix, :endpoint],
|
||||||
|
log: {__MODULE__, :log_level, []}
|
||||||
|
|
||||||
plug Plug.Parsers,
|
plug Plug.Parsers,
|
||||||
parsers: [:urlencoded, :multipart, :json],
|
parsers: [:urlencoded, :multipart, :json],
|
||||||
@@ -55,6 +58,10 @@ defmodule PinchflatWeb.Endpoint do
|
|||||||
|
|
||||||
plug PinchflatWeb.Router
|
plug PinchflatWeb.Router
|
||||||
|
|
||||||
|
# Disables logging in Plug.Telemetry for healthcheck requests
|
||||||
|
def log_level(%Plug.Conn{path_info: ["healthcheck"]}), do: false
|
||||||
|
def log_level(_), do: :info
|
||||||
|
|
||||||
# URLs need to be generated using the host of the current page being accessed
|
# URLs need to be generated using the host of the current page being accessed
|
||||||
# for things like Podcast RSS feeds to contain links to the right location.
|
# for things like Podcast RSS feeds to contain links to the right location.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ defmodule PinchflatWeb.Router do
|
|||||||
scope "/", PinchflatWeb do
|
scope "/", PinchflatWeb do
|
||||||
pipe_through :api
|
pipe_through :api
|
||||||
|
|
||||||
get "/healthcheck", HealthController, :check
|
get "/healthcheck", HealthController, :check, log: false
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/dev" do
|
scope "/dev" do
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ defmodule Pinchflat.MixProject do
|
|||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :pinchflat,
|
app: :pinchflat,
|
||||||
version: "2025.3.6",
|
version: "2025.3.17",
|
||||||
elixir: "~> 1.17",
|
elixir: "~> 1.17",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 506 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
defmodule Pinchflat.Repo.Migrations.AddRateLimitSpeedToSettings do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:settings) do
|
||||||
|
add :download_throughput_limit, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
defmodule Pinchflat.Repo.Migrations.AddRestrictFilenamesToSettings do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:settings) do
|
||||||
|
add :restrict_filenames, :boolean, default: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -96,7 +96,7 @@ defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "run/4 when testing sleep interval options" do
|
describe "run/4 when testing rate limit options" do
|
||||||
test "includes sleep interval options by default" do
|
test "includes sleep interval options by default" do
|
||||||
Settings.set(extractor_sleep_interval_seconds: 5)
|
Settings.set(extractor_sleep_interval_seconds: 5)
|
||||||
|
|
||||||
@@ -124,6 +124,22 @@ defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
|||||||
refute String.contains?(output, "--sleep-requests")
|
refute String.contains?(output, "--sleep-requests")
|
||||||
refute String.contains?(output, "--sleep-subtitles")
|
refute String.contains?(output, "--sleep-subtitles")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "includes limit_rate option when specified" do
|
||||||
|
Settings.set(download_throughput_limit: "100K")
|
||||||
|
|
||||||
|
assert {:ok, output} = Runner.run(@media_url, :foo, [], "")
|
||||||
|
|
||||||
|
assert String.contains?(output, "--limit-rate 100K")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't include limit_rate option when download_throughput_limit is nil" do
|
||||||
|
Settings.set(download_throughput_limit: nil)
|
||||||
|
|
||||||
|
assert {:ok, output} = Runner.run(@media_url, :foo, [], "")
|
||||||
|
|
||||||
|
refute String.contains?(output, "--limit-rate")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "run/4 when testing global options" do
|
describe "run/4 when testing global options" do
|
||||||
@@ -146,6 +162,24 @@ defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "run/4 when testing misc options" do
|
||||||
|
test "includes --restrict-filenames when enabled" do
|
||||||
|
Settings.set(restrict_filenames: true)
|
||||||
|
|
||||||
|
assert {:ok, output} = Runner.run(@media_url, :foo, [], "")
|
||||||
|
|
||||||
|
assert String.contains?(output, "--restrict-filenames")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't include --restrict-filenames when disabled" do
|
||||||
|
Settings.set(restrict_filenames: false)
|
||||||
|
|
||||||
|
assert {:ok, output} = Runner.run(@media_url, :foo, [], "")
|
||||||
|
|
||||||
|
refute String.contains?(output, "--restrict-filenames")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "version/0" do
|
describe "version/0" do
|
||||||
test "adds the version arg" do
|
test "adds the version arg" do
|
||||||
assert {:ok, output} = Runner.version()
|
assert {:ok, output} = Runner.version()
|
||||||
|
|||||||
Reference in New Issue
Block a user