diff --git a/README.md b/README.md index d99b196..47da353 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,7 @@ If you change this setting and it works well for you, please leave a comment on | `TZ_DATA_DIR` | No | `/etc/elixir_tzdata_data` | The container path where the timezone database is stored | | `BASE_ROUTE_PATH` | No | `/` | The base path for route generation. Useful when running behind certain reverse proxies - prefixes must be stripped. | | `YT_DLP_WORKER_CONCURRENCY` | No | `2` | The number of concurrent workers that use `yt-dlp` _per queue_. Set to 1 if you're getting IP limited, otherwise don't touch it | +| `YT_DLP_VERSION` | No | `stable` | Controls yt-dlp updates: `stable`, `nightly`, `master`, `pinned`/`none` (disable), or a specific version like `2025.12.08` | | `ENABLE_PROMETHEUS` | No | `false` | Setting to _any_ non-blank value will enable Prometheus. See [docs](https://github.com/kieraneglin/pinchflat/wiki/Prometheus-and-Grafana) | ### Reverse Proxies diff --git a/config/runtime.exs b/config/runtime.exs index 5624bfe..e77252f 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -43,6 +43,18 @@ config :pinchflat, Pinchflat.Repo, # Some users may want to increase the number of workers that use yt-dlp to improve speeds # Others may want to decrease the number of these workers to lessen the chance of an IP ban {yt_dlp_worker_count, _} = Integer.parse(System.get_env("YT_DLP_WORKER_CONCURRENCY", "2")) + +# Controls yt-dlp version management. Supported values: +# - "stable" (default) - updates to latest stable release daily +# - "nightly" - updates to latest nightly build daily +# - "master" - updates to latest master build daily +# - "pinned" or "none" - disables automatic updates entirely +# - A specific version like "2025.12.08" - pins to that exact version +yt_dlp_version_channel = System.get_env("YT_DLP_VERSION", "stable") + +config :pinchflat, + yt_dlp_version_channel: yt_dlp_version_channel + # Used to set the cron for the yt-dlp update worker. The reason for this is # to avoid all instances of PF updating yt-dlp at the same time, which 1) # could result in rate limiting and 2) gives me time to react if an update diff --git a/lib/pinchflat/yt_dlp/command_runner.ex b/lib/pinchflat/yt_dlp/command_runner.ex index f574d30..224f671 100644 --- a/lib/pinchflat/yt_dlp/command_runner.ex +++ b/lib/pinchflat/yt_dlp/command_runner.ex @@ -77,15 +77,22 @@ defmodule Pinchflat.YtDlp.CommandRunner do end @doc """ - Updates yt-dlp to the latest version + Updates yt-dlp to the specified version channel or specific version. + + The version_target can be: + - "stable" - updates to latest stable release (default) + - "nightly" - updates to latest nightly build + - "master" - updates to latest master build + - A specific version like "2025.12.08" - pins to that exact version Returns {:ok, binary()} | {:error, binary()} """ @impl YtDlpCommandRunner - def update do + def update(version_target \\ "stable") do command = backend_executable() + args = build_update_args(version_target) - case CliUtils.wrap_cmd(command, ["--update"]) do + case CliUtils.wrap_cmd(command, args) do {output, 0} -> {:ok, String.trim(output)} @@ -94,6 +101,11 @@ defmodule Pinchflat.YtDlp.CommandRunner do end end + defp build_update_args("stable"), do: ["--update"] + defp build_update_args("nightly"), do: ["--update-to", "nightly"] + defp build_update_args("master"), do: ["--update-to", "master"] + defp build_update_args(specific_version), do: ["--update-to", "yt-dlp/yt-dlp@#{specific_version}"] + defp generate_output_filepath(addl_opts) do case Keyword.get(addl_opts, :output_filepath) do nil -> FSUtils.generate_metadata_tmpfile(:json) diff --git a/lib/pinchflat/yt_dlp/update_worker.ex b/lib/pinchflat/yt_dlp/update_worker.ex index 2d9b43f..abdb81e 100644 --- a/lib/pinchflat/yt_dlp/update_worker.ex +++ b/lib/pinchflat/yt_dlp/update_worker.ex @@ -1,5 +1,14 @@ defmodule Pinchflat.YtDlp.UpdateWorker do - @moduledoc false + @moduledoc """ + Handles automatic yt-dlp updates based on the YT_DLP_VERSION environment variable. + + Supported values for YT_DLP_VERSION: + - "stable" (default) - updates to latest stable release daily + - "nightly" - updates to latest nightly build daily + - "master" - updates to latest master build daily + - "pinned" or "none" - disables automatic updates entirely + - A specific version like "2025.12.08" - pins to that exact version + """ use Oban.Worker, queue: :local_data, @@ -23,14 +32,22 @@ defmodule Pinchflat.YtDlp.UpdateWorker do Updates yt-dlp and saves the version to the settings. This worker is scheduled to run via the Oban Cron plugin as well as on app boot. + The update behavior is controlled by the YT_DLP_VERSION environment variable. Returns :ok """ @impl Oban.Worker def perform(%Oban.Job{}) do - Logger.info("Updating yt-dlp") + version_setting = get_version_setting() - yt_dlp_runner().update() + case version_setting do + setting when setting in ["pinned", "none"] -> + Logger.info("yt-dlp auto-update disabled (YT_DLP_VERSION=#{setting})") + + version_target -> + Logger.info("Updating yt-dlp to #{version_target}") + yt_dlp_runner().update(version_target) + end {:ok, yt_dlp_version} = yt_dlp_runner().version() Settings.set(yt_dlp_version: yt_dlp_version) @@ -38,6 +55,13 @@ defmodule Pinchflat.YtDlp.UpdateWorker do :ok end + @doc """ + Returns the configured yt-dlp version setting from environment. + """ + def get_version_setting do + Application.get_env(:pinchflat, :yt_dlp_version_channel, "stable") + end + defp yt_dlp_runner do Application.get_env(:pinchflat, :yt_dlp_runner) end diff --git a/lib/pinchflat/yt_dlp/yt_dlp_command_runner.ex b/lib/pinchflat/yt_dlp/yt_dlp_command_runner.ex index e5c770e..91f1e9d 100644 --- a/lib/pinchflat/yt_dlp/yt_dlp_command_runner.ex +++ b/lib/pinchflat/yt_dlp/yt_dlp_command_runner.ex @@ -10,4 +10,5 @@ defmodule Pinchflat.YtDlp.YtDlpCommandRunner do @callback run(binary(), atom(), keyword(), binary(), keyword()) :: {:ok, binary()} | {:error, binary(), integer()} @callback version() :: {:ok, binary()} | {:error, binary()} @callback update() :: {:ok, binary()} | {:error, binary()} + @callback update(binary()) :: {:ok, binary()} | {:error, binary()} end