Refactor and document output path (#28)

* Updated output path builder to better use yt-dlp variables

* Added UI explaining output template options

* Fixed outdated reference in dockerfile
This commit is contained in:
Kieran
2024-02-20 18:40:13 -08:00
committed by GitHub
parent af7e76c1d1
commit 1ec1a48235
12 changed files with 185 additions and 57 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ defmodule Pinchflat.Profiles.MediaProfile do
schema "media_profiles" do
field :name, :string
field :output_path_template, :string, default: "/{{ uploader }}/{{ title }}/{{ title }}-{{ id }}.{{ ext }}"
field :output_path_template, :string, default: "/{{ channel }}/{{ title }}/{{ title }} [{{ id }}].{{ ext }}"
field :download_subs, :boolean, default: true
field :download_auto_subs, :boolean, default: true
@@ -12,50 +12,22 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder do
Translates liquid-style templates into yt-dlp-style templates,
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
TemplateParser.parse(template_string, full_yt_dlp_options_map())
TemplateParser.parse(template_string, custom_yt_dlp_option_map(), &identifier_fn/2)
end
defp full_yt_dlp_options_map do
Map.merge(
standard_yt_dlp_option_map(),
custom_yt_dlp_option_map()
)
end
defp standard_yt_dlp_option_map do
%{
# Uppercase "S" means "safe" - ie: filepath-safe
"id" => "%(id)S",
"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"
}
# The `nil` case simply wraps the identifier in yt-dlp-style syntax. This assumes that
# the identifier is a valid yt-dlp option. The upside is that this gives the user
# access to ALL single-word yt-dlp options in the (imo) more friendly/forgiving liquid-style syntax.
#
# For all "custom" variables, we use the `Map.get/3` function to look up the value in the provided.
# See `custom_yt_dlp_option_map` for a list of those.
defp identifier_fn(identifier, variables) do
case Map.get(variables, identifier) do
nil -> "%(#{identifier})S"
value -> value
end
end
defp custom_yt_dlp_option_map do
+12 -5
View File
@@ -8,28 +8,35 @@ defmodule Pinchflat.RenderedString.Parser do
use Pinchflat.RenderedString.Base
@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.
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`
case do_parse(string) do
{:ok, parsed, _, _, _, _} ->
{:ok, build_string(parsed, variables)}
{:ok, build_string(parsed, variables, value_fetch_fn)}
{:error, message, _, _, _, _} ->
{:error, message}
end
end
defp build_string(parsed, variables) do
defp build_string(parsed, variables, value_fetch_fn) do
Enum.reduce(parsed, "", fn element, acc ->
case element do
{:text, text} -> acc <> text
{:interpolation, {:identifier, identifier}} -> acc <> to_string(variables[identifier])
{:interpolation, {:identifier, identifier}} -> acc <> value_fetch_fn.(identifier, variables)
end
end)
end
def default_fetcher(identifier, variables) do
Map.get(variables, identifier, "")
end
end