Skip to content

Commit

Permalink
[WIP] Very-WIP first steps at parsing options and downloading
Browse files Browse the repository at this point in the history
  • Loading branch information
kieraneglin committed Jan 21, 2024
1 parent f6fd077 commit fad1e39
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 9 deletions.
4 changes: 4 additions & 0 deletions lib/pinchflat/downloader/backends/yt_dlp/command_runner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ defmodule Pinchflat.Downloader.Backends.YtDlp.CommandRunner do

@doc """
Runs a yt-dlp command and returns the string output
# TODO: deduplicate command opts, keeping the last one on conflict
although possibly not needed (and a LOT easier) if yt-dlp
just ignores duplicate options (ie: look into that)
"""
@impl BackendCommandRunner
def run(url, command_opts) do
Expand Down
13 changes: 4 additions & 9 deletions lib/pinchflat/downloader/backends/yt_dlp/video.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,17 @@ defmodule Pinchflat.Downloader.Backends.YtDlp.Video do
The video will be moved to its final destination... elsewhere
# TODO: update these docs when I figure out a module to move videos
# TODO: test
# NOTE: maybe instead of moving it to the tempdir, I can just download it
to the final destination by using the `output` option. The
parser could be updated to generate a value for the output option.
This way, advanced users can just the yt-dlp output syntax and
newer users can use the easier liquid-like syntax.
"""
def download(url, command_opts \\ []) do
default_output_path = Path.join([base_directory(), "%(id)s", "%(id)s.%(ext)s"])
default_opts = [output: default_output_path]
opts = Keyword.merge(default_opts, command_opts)

backend_runner().run(url, opts)
end

defp base_directory do
Application.get_env(:pinchflat, :media_directory)
# TODO: if this stays this simple, consider not abstracting it
# HOWEVER - this module does provide clarity of intent so maybe keep?
backend_runner().run(url, command_opts)
end

defp backend_runner do
Expand Down
39 changes: 39 additions & 0 deletions lib/pinchflat/downloader/video_downloader.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Pinchflat.Downloader.VideoDownloader do
@moduledoc """
This is the integration layer for actually downloading videos.
It takes into account the media profile's settings in order
to download the video with the desired options.
Technically hardcodes the yt-dlp backend for now, but should leave
it open-ish for future expansion (just in case).
"""

alias Pinchflat.Downloader.Backends.YtDlp.Video, as: YtDlpVideo
alias Pinchflat.Profiles.Options.YtDlp.OptionBuilder, as: YtDlpOptionBuilder

@doc """
Downloads a single video based on the settings in the given media profile.
# TODO: implement media profiles - so far this is a glorified mock
# TODO: test
"""
def download_for_media_profile(url, media_profile, backend \\ :yt_dlp) do
option_builder = option_builder(backend)
video_backend = video_backend(backend)
{:ok, options} = option_builder.build(media_profile)

video_backend.download(url, options)
end

def option_builder(backend) do
case backend do
:yt_dlp -> YtDlpOptionBuilder
end
end

def video_backend(backend) do
case backend do
:yt_dlp -> YtDlpVideo
end
end
end
43 changes: 43 additions & 0 deletions lib/pinchflat/profiles/options/yt_dlp/option_builder.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
defmodule Pinchflat.Profiles.Options.YtDlp.OptionBuilder do
@moduledoc """
Builds the options for yt-dlp based on the given media profile.
TODO: probably make this a behaviour so I can add other backends later
"""

alias Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder

@doc """
Builds the options for yt-dlp based on the given media profile.
TODO: add a guard to ensure argument is a media profile
TODO: consider adding the ability to pass in a second argument to override
these options
TODO: test
"""
def build(media_profile) do
{:ok, output_path} = OutputPathBuilder.build(media_profile.output_path_template)

# NOTE: I'll be hardcoding most things for now (esp. options to help me test) -
# add more configuration later as I build out the models. Walk before you can run!

{:ok,
[
:write_thumbnail,
:write_subs,
:embed_metadata,
:embed_thumbnail,
:embed_subs,
:write_info_json,
:write_auto_subs,
:no_progress,
convert_thumbnails: "jpg",
sub_langs: "en.*",
output: Path.join(base_directory(), output_path)
]}
end

defp base_directory do
Application.get_env(:pinchflat, :media_directory)
end
end
71 changes: 71 additions & 0 deletions lib/pinchflat/profiles/options/yt_dlp/output_path_builder.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
defmodule Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder do
@moduledoc """
Builds yt-dlp-friendly output paths for downloaded media
TODO: probably make this a behaviour so I can add other backends later
"""

alias Pinchflat.RenderedString.Parser, as: TemplateParser

@doc """
Builds the actual final filepath from a given template.
Translates liquid-style templates into yt-dlp-style templates,
leaving yt-dlp syntax intact.
TODO: test
"""
def build(template_string) do
TemplateParser.parse(template_string, full_yt_dlp_options_map())
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
%{
"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"
}
end

defp custom_yt_dlp_option_map do
%{
# Filepath-safe versions of some standard options
"safe_id" => "%(id)S",
"safe_title" => "%(title)S",
"safe_fulltitle" => "%(fulltitle)S",
"safe_uploader" => "%(uploader)S",
"safe_creator" => "%(creator)S",
# Individual parts of the upload date
"upload_year" => "%(upload_date>%Y)s",
"upload_month" => "%(upload_date>%m)s",
"upload_day" => "%(upload_date>%d)s"
}
end
end

0 comments on commit fad1e39

Please sign in to comment.