Updated output path builder to better use yt-dlp variables
This commit is contained in:
parent
7d62f7c7df
commit
e5f8c9de4f
3 changed files with 33 additions and 46 deletions
|
|
@ -12,50 +12,22 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder do
|
||||||
|
|
||||||
Translates liquid-style templates into yt-dlp-style templates,
|
Translates liquid-style templates into yt-dlp-style templates,
|
||||||
leaving yt-dlp syntax intact.
|
leaving yt-dlp syntax intact.
|
||||||
|
|
||||||
IDEA: apart from any custom options I've defined, I can support any yt-dlp
|
|
||||||
option by assuming `{{ identifier }}` should transform to `%(identifier)S`.
|
|
||||||
It's not doing anything huge, but it's nicer to type and more approachable IMO.
|
|
||||||
IDEA: set a default for `MediaProfile`'s `output_path_template` field
|
|
||||||
"""
|
"""
|
||||||
def build(template_string) do
|
def build(template_string) do
|
||||||
TemplateParser.parse(template_string, full_yt_dlp_options_map())
|
TemplateParser.parse(template_string, custom_yt_dlp_option_map(), &identifier_fn/2)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp full_yt_dlp_options_map do
|
# The `nil` case simply wraps the identifier in yt-dlp-style syntax. This assumes that
|
||||||
Map.merge(
|
# the identifier is a valid yt-dlp option. The upside is that this gives the user
|
||||||
standard_yt_dlp_option_map(),
|
# access to ALL single-word yt-dlp options in the (imo) more friendly/forgiving liquid-style syntax.
|
||||||
custom_yt_dlp_option_map()
|
#
|
||||||
)
|
# For all "custom" variables, we use the `Map.get/3` function to look up the value in the provided.
|
||||||
end
|
# See `custom_yt_dlp_option_map` for a list of those.
|
||||||
|
defp identifier_fn(identifier, variables) do
|
||||||
defp standard_yt_dlp_option_map do
|
case Map.get(variables, identifier) do
|
||||||
%{
|
nil -> "%(#{identifier})S"
|
||||||
# Uppercase "S" means "safe" - ie: filepath-safe
|
value -> value
|
||||||
"id" => "%(id)S",
|
end
|
||||||
"ext" => "%(ext)S",
|
|
||||||
"title" => "%(title)S",
|
|
||||||
"fulltitle" => "%(fulltitle)S",
|
|
||||||
"uploader" => "%(uploader)S",
|
|
||||||
"creator" => "%(creator)S",
|
|
||||||
"upload_date" => "%(upload_date)S",
|
|
||||||
"release_date" => "%(release_date)S",
|
|
||||||
"duration" => "%(duration)S",
|
|
||||||
# For videos classified as an episode of a series:
|
|
||||||
"series" => "%(series)S",
|
|
||||||
"season" => "%(season)S",
|
|
||||||
"season_number" => "%(season_number)S",
|
|
||||||
"episode" => "%(episode)S",
|
|
||||||
"episode_number" => "%(episode_number)S",
|
|
||||||
"episode_id" => "%(episode_id)S",
|
|
||||||
# For videos classified as music:
|
|
||||||
"track" => "%(track)S",
|
|
||||||
"track_number" => "%(track_number)S",
|
|
||||||
"artist" => "%(artist)S",
|
|
||||||
"album" => "%(album)S",
|
|
||||||
"album_type" => "%(album_type)S",
|
|
||||||
"genre" => "%(genre)S"
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp custom_yt_dlp_option_map do
|
defp custom_yt_dlp_option_map do
|
||||||
|
|
|
||||||
|
|
@ -8,28 +8,35 @@ defmodule Pinchflat.RenderedString.Parser do
|
||||||
use Pinchflat.RenderedString.Base
|
use Pinchflat.RenderedString.Base
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Parses a string into a rendered string, using the provided variables.
|
Parses a string into a rendered string, using the provided variables. Optionally
|
||||||
|
takes a custom fetcher function for handling missing variables.
|
||||||
|
|
||||||
Variable identifiers are surrounded by {{ and }}. The variable keys MUST be strings.
|
Variable identifiers are surrounded by {{ and }}. The variable keys MUST be strings.
|
||||||
If an identifier is not found in the provided variables, it will be removed from the string.
|
If an identifier is not found in the provided variables, it will be removed from the string.
|
||||||
|
|
||||||
|
Returns `{:ok, binary()}` or `{:error, binary()}`.
|
||||||
"""
|
"""
|
||||||
def parse(string, variables) do
|
def parse(string, variables, value_fetch_fn \\ &default_fetcher/2) do
|
||||||
# `do_parse` comes from `RenderedString.Base`
|
# `do_parse` comes from `RenderedString.Base`
|
||||||
case do_parse(string) do
|
case do_parse(string) do
|
||||||
{:ok, parsed, _, _, _, _} ->
|
{:ok, parsed, _, _, _, _} ->
|
||||||
{:ok, build_string(parsed, variables)}
|
{:ok, build_string(parsed, variables, value_fetch_fn)}
|
||||||
|
|
||||||
{:error, message, _, _, _, _} ->
|
{:error, message, _, _, _, _} ->
|
||||||
{:error, message}
|
{:error, message}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_string(parsed, variables) do
|
defp build_string(parsed, variables, value_fetch_fn) do
|
||||||
Enum.reduce(parsed, "", fn element, acc ->
|
Enum.reduce(parsed, "", fn element, acc ->
|
||||||
case element do
|
case element do
|
||||||
{:text, text} -> acc <> text
|
{:text, text} -> acc <> text
|
||||||
{:interpolation, {:identifier, identifier}} -> acc <> to_string(variables[identifier])
|
{:interpolation, {:identifier, identifier}} -> acc <> value_fetch_fn.(identifier, variables)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def default_fetcher(identifier, variables) do
|
||||||
|
Map.get(variables, identifier, "")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ defmodule Pinchflat.RenderedString.ParserTest do
|
||||||
|
|
||||||
alias Pinchflat.RenderedString.Parser
|
alias Pinchflat.RenderedString.Parser
|
||||||
|
|
||||||
describe "parse/2" do
|
describe "parse/3" do
|
||||||
test "it returns the rendered string when the string is valid" do
|
test "it returns the rendered string when the string is valid" do
|
||||||
assert {:ok, "bar"} = Parser.parse("{{ foo }}", %{"foo" => "bar"})
|
assert {:ok, "bar"} = Parser.parse("{{ foo }}", %{"foo" => "bar"})
|
||||||
end
|
end
|
||||||
|
|
@ -36,5 +36,13 @@ defmodule Pinchflat.RenderedString.ParserTest do
|
||||||
test "it returns an error when the string is invalid" do
|
test "it returns an error when the string is invalid" do
|
||||||
assert {:error, "expected end of string"} = Parser.parse("{{ 1-1 }", %{})
|
assert {:error, "expected end of string"} = Parser.parse("{{ 1-1 }", %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it supports a custom fetcher function" do
|
||||||
|
custom_fetcher = fn _, _ ->
|
||||||
|
"quux"
|
||||||
|
end
|
||||||
|
|
||||||
|
assert {:ok, "quux"} = Parser.parse("{{ foo }}", %{}, custom_fetcher)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue