diff --git a/lib/pinchflat/media_client/video_downloader.ex b/lib/pinchflat/media_client/video_downloader.ex index 4ab205f..d1028e9 100644 --- a/lib/pinchflat/media_client/video_downloader.ex +++ b/lib/pinchflat/media_client/video_downloader.ex @@ -11,7 +11,6 @@ defmodule Pinchflat.MediaClient.VideoDownloader do alias Pinchflat.Repo alias Pinchflat.Media alias Pinchflat.Media.MediaItem - alias Pinchflat.Profiles.MediaProfile alias Pinchflat.MediaClient.Backends.YtDlp.Video, as: YtDlpVideo alias Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilder, as: YtDlpDownloadOptionBuilder @@ -31,9 +30,8 @@ defmodule Pinchflat.MediaClient.VideoDownloader do """ def download_for_media_item(%MediaItem{} = media_item, backend \\ :yt_dlp) do item_with_preloads = Repo.preload(media_item, [:metadata, source: :media_profile]) - media_profile = item_with_preloads.source.media_profile - case download_for_media_profile(media_item.original_url, media_profile, backend) do + case download_with_options(media_item.original_url, item_with_preloads, backend) do {:ok, parsed_json} -> {parser, helpers} = metadata_parsers(backend) @@ -57,10 +55,10 @@ defmodule Pinchflat.MediaClient.VideoDownloader do end end - defp download_for_media_profile(url, %MediaProfile{} = media_profile, backend) do + defp download_with_options(url, item_with_preloads, backend) do option_builder = option_builder(backend) video_backend = video_backend(backend) - {:ok, options} = option_builder.build(media_profile) + {:ok, options} = option_builder.build(item_with_preloads) video_backend.download(url, options) end diff --git a/lib/pinchflat/media_source/source.ex b/lib/pinchflat/media_source/source.ex index ed77c7b..6282e28 100644 --- a/lib/pinchflat/media_source/source.ex +++ b/lib/pinchflat/media_source/source.ex @@ -15,7 +15,7 @@ defmodule Pinchflat.Sources.Source do collection_name collection_id collection_type - friendly_name + custom_name index_frequency_minutes download_media last_indexed_at @@ -27,7 +27,7 @@ defmodule Pinchflat.Sources.Source do collection_name collection_id collection_type - friendly_name + custom_name index_frequency_minutes download_media original_url @@ -35,7 +35,7 @@ defmodule Pinchflat.Sources.Source do )a schema "sources" do - field :friendly_name, :string + field :custom_name, :string field :collection_name, :string field :collection_id, :string field :collection_type, Ecto.Enum, values: [:channel, :playlist] @@ -58,7 +58,7 @@ defmodule Pinchflat.Sources.Source do def changeset(source, attrs) do source |> cast(attrs, @allowed_fields) - |> dynamic_default(:friendly_name, fn cs -> get_field(cs, :collection_name) end) + |> dynamic_default(:custom_name, fn cs -> get_field(cs, :collection_name) end) |> validate_required(@required_fields) |> unique_constraint([:collection_id, :media_profile_id]) end diff --git a/lib/pinchflat/profiles/media_profile.ex b/lib/pinchflat/profiles/media_profile.ex index 848eae6..db3f888 100644 --- a/lib/pinchflat/profiles/media_profile.ex +++ b/lib/pinchflat/profiles/media_profile.ex @@ -29,7 +29,8 @@ defmodule Pinchflat.Profiles.MediaProfile do schema "media_profiles" do field :name, :string - field :output_path_template, :string, default: "/{{ channel }}/{{ title }}/{{ title }} [{{ id }}].{{ ext }}" + field :output_path_template, :string, + default: "/{{ source_custom_name }}/{{ title }}/{{ title }} [{{ id }}].{{ ext }}" field :download_subs, :boolean, default: true field :download_auto_subs, :boolean, default: true diff --git a/lib/pinchflat/profiles/options/yt_dlp/download_option_builder.ex b/lib/pinchflat/profiles/options/yt_dlp/download_option_builder.ex index 02d735b..e9b37cf 100644 --- a/lib/pinchflat/profiles/options/yt_dlp/download_option_builder.ex +++ b/lib/pinchflat/profiles/options/yt_dlp/download_option_builder.ex @@ -5,21 +5,17 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilder do IDEA: consider making this a behaviour so I can add other backends later """ - alias Pinchflat.Profiles.MediaProfile + alias Pinchflat.Media.MediaItem alias Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder @doc """ - Builds the options for yt-dlp to download media based on the given media profile. + Builds the options for yt-dlp to download media based on the given media's profile. IDEA: consider adding the ability to pass in a second argument to override these options """ - def build(%MediaProfile{} = media_profile) do - # 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! - - # NOTE: Looks like you can put different media types in different directories. - # see: https://github.com/yt-dlp/yt-dlp#output-template + def build(%MediaItem{} = media_item_with_preloads) do + media_profile = media_item_with_preloads.source.media_profile built_options = default_options() ++ @@ -27,12 +23,11 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilder do thumbnail_options(media_profile) ++ metadata_options(media_profile) ++ quality_options(media_profile) ++ - output_options(media_profile) + output_options(media_item_with_preloads) {:ok, built_options} end - # This will be updated a lot as I add new options to profiles defp default_options do [:no_progress, :windows_filenames] end @@ -101,14 +96,25 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilder do end end - defp output_options(media_profile) do - {:ok, output_path} = OutputPathBuilder.build(media_profile.output_path_template) + defp output_options(media_item_with_preloads) do + media_profile = media_item_with_preloads.source.media_profile + additional_options_map = output_options_map(media_item_with_preloads) + {:ok, output_path} = OutputPathBuilder.build(media_profile.output_path_template, additional_options_map) [ output: Path.join(base_directory(), output_path) ] end + defp output_options_map(media_item_with_preloads) do + source = media_item_with_preloads.source + + %{ + "source_custom_name" => source.custom_name, + "source_collection_type" => source.collection_type + } + end + defp base_directory do Application.get_env(:pinchflat, :media_directory) end diff --git a/lib/pinchflat/profiles/options/yt_dlp/output_path_builder.ex b/lib/pinchflat/profiles/options/yt_dlp/output_path_builder.ex index dadb9fe..4f76eb2 100644 --- a/lib/pinchflat/profiles/options/yt_dlp/output_path_builder.ex +++ b/lib/pinchflat/profiles/options/yt_dlp/output_path_builder.ex @@ -8,13 +8,16 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder do alias Pinchflat.RenderedString.Parser, as: TemplateParser @doc """ - Builds the actual final filepath from a given template. + Builds the actual final filepath from a given template. Optionally, you can pass in + a map of additional options to be used in the template. Translates liquid-style templates into yt-dlp-style templates, leaving yt-dlp syntax intact. """ - def build(template_string) do - TemplateParser.parse(template_string, custom_yt_dlp_option_map(), &identifier_fn/2) + def build(template_string, additional_template_options \\ %{}) do + combined_options = Map.merge(custom_yt_dlp_option_map(), additional_template_options) + + TemplateParser.parse(template_string, combined_options, &identifier_fn/2) end # The `nil` case simply wraps the identifier in yt-dlp-style syntax. This assumes that @@ -30,6 +33,9 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder do end end + # This isn't the only source for custom options, since they can be passed in my the caller. + # `download_option_builder` is the most likely place for other custom options to be added, + # but if in doubt just search the codebase for `OutputPathBuilder.build`. defp custom_yt_dlp_option_map do %{ # Individual parts of the upload date diff --git a/lib/pinchflat/workers/media_indexing_worker.ex b/lib/pinchflat/workers/media_indexing_worker.ex index 6dc0c32..ae54ac1 100644 --- a/lib/pinchflat/workers/media_indexing_worker.ex +++ b/lib/pinchflat/workers/media_indexing_worker.ex @@ -15,8 +15,9 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do @doc """ The ID is that of a source _record_, not a YouTube channel/playlist ID. Indexes the provided source, kicks off downloads for each new MediaItem, and - reschedules the job to run again in the future (as determined by the - souce's `index_frequency_minutes` field). + reschedules the job to run again in the future. It will ALWAYS index a source + if it's never been indexed before, but rescheduling is determined by the + `index_frequency_minutes` field. README: Re-scheduling here works a little different than you may expect. The reschedule time is relative to the time the job has actually _completed_. @@ -39,18 +40,31 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do def perform(%Oban.Job{args: %{"id" => source_id}}) do source = Sources.get_source!(source_id) - if source.index_frequency_minutes <= 0 do - :ok - else - index_media_and_reschedule(source) + case {source.index_frequency_minutes, source.last_indexed_at} do + {index_freq, _} when index_freq > 0 -> + # If the indexing is on a schedule simply run indexing and reschedule + index_media(source) + reschedule_indexing(source) + + {_, nil} -> + # If the source has never been indexed, index it once + # even if it's not meant to reschedule + index_media(source) + + _ -> + # If the source HAS been indexed and is not meant to reschedule, + # perform a no-op + :ok end end - defp index_media_and_reschedule(source) do + defp index_media(source) do SourceTasks.index_media_items(source) # This method handles the case where a source is set to not download media SourceTasks.enqueue_pending_media_tasks(source) + end + defp reschedule_indexing(source) do source |> Map.take([:id]) |> MediaIndexingWorker.new(schedule_in: source.index_frequency_minutes * 60) diff --git a/lib/pinchflat_web/controllers/media_items/media_item_html/show.html.heex b/lib/pinchflat_web/controllers/media_items/media_item_html/show.html.heex index 95da677..8056359 100644 --- a/lib/pinchflat_web/controllers/media_items/media_item_html/show.html.heex +++ b/lib/pinchflat_web/controllers/media_items/media_item_html/show.html.heex @@ -17,7 +17,7 @@
Source: <.inline_link href={~p"/sources/#{@media_item.source_id}"}> - <%= @media_item.source.friendly_name %> + <%= @media_item.source.custom_name %>
diff --git a/lib/pinchflat_web/controllers/media_profiles/media_profile_html.ex b/lib/pinchflat_web/controllers/media_profiles/media_profile_html.ex index 41840f0..a313002 100644 --- a/lib/pinchflat_web/controllers/media_profiles/media_profile_html.ex +++ b/lib/pinchflat_web/controllers/media_profiles/media_profile_html.ex @@ -30,7 +30,13 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileHTML do end def custom_output_template_options do - ~w(upload_day upload_month upload_year)a + %{ + upload_day: nil, + upload_month: nil, + upload_year: nil, + source_custom_name: "the name of the sources that use this profile", + source_collection_type: "the collection type of the sources that use this profile. Either 'channel' or 'playlist'" + } end def common_output_template_options do diff --git a/lib/pinchflat_web/controllers/media_profiles/media_profile_html/output_template_help.html.heex b/lib/pinchflat_web/controllers/media_profiles/media_profile_html/output_template_help.html.heex index 7b11b1d..7dd6df2 100644 --- a/lib/pinchflat_web/controllers/media_profiles/media_profile_html/output_template_help.html.heex +++ b/lib/pinchflat_web/controllers/media_profiles/media_profile_html/output_template_help.html.heex @@ -46,8 +46,8 @@

Template Options

-
-

+

+

Any single-word yt-dlp option <.inline_link href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template"> @@ -58,8 +58,9 @@

Custom Aliases

Common Options

diff --git a/lib/pinchflat_web/controllers/media_profiles/media_profile_html/show.html.heex b/lib/pinchflat_web/controllers/media_profiles/media_profile_html/show.html.heex index 7cdb2df..a7e5b1f 100644 --- a/lib/pinchflat_web/controllers/media_profiles/media_profile_html/show.html.heex +++ b/lib/pinchflat_web/controllers/media_profiles/media_profile_html/show.html.heex @@ -50,7 +50,7 @@ <:tab title="Sources"> <.table rows={@media_profile.sources} table_class="text-black dark:text-white"> <:col :let={source} label="Name"> - <%= source.friendly_name || source.collection_name %> + <%= source.custom_name || source.collection_name %> <:col :let={source} label="Type"><%= source.collection_type %> <:col :let={source} label="Should Download?"> diff --git a/lib/pinchflat_web/controllers/sources/source_html/index.html.heex b/lib/pinchflat_web/controllers/sources/source_html/index.html.heex index 3dbfec2..0b3f743 100644 --- a/lib/pinchflat_web/controllers/sources/source_html/index.html.heex +++ b/lib/pinchflat_web/controllers/sources/source_html/index.html.heex @@ -14,7 +14,7 @@
<.table rows={@sources} table_class="text-black dark:text-white"> <:col :let={source} label="Name"> - <%= source.friendly_name || source.collection_name %> + <%= source.custom_name || source.collection_name %> <:col :let={source} label="Type"><%= source.collection_type %> <:col :let={source} label="Should Download?"> diff --git a/lib/pinchflat_web/controllers/sources/source_html/source_form.html.heex b/lib/pinchflat_web/controllers/sources/source_html/source_form.html.heex index c4f8f70..f7f5fe0 100644 --- a/lib/pinchflat_web/controllers/sources/source_html/source_form.html.heex +++ b/lib/pinchflat_web/controllers/sources/source_html/source_form.html.heex @@ -4,7 +4,7 @@ <.input - field={f[:friendly_name]} + field={f[:custom_name]} type="text" label="Custom Name" help="Something descriptive. Does not impact indexing or downloading" diff --git a/priv/repo/migrations/20240302194115_rename_friendly_name_to_custom_name.exs b/priv/repo/migrations/20240302194115_rename_friendly_name_to_custom_name.exs new file mode 100644 index 0000000..16bfba5 --- /dev/null +++ b/priv/repo/migrations/20240302194115_rename_friendly_name_to_custom_name.exs @@ -0,0 +1,7 @@ +defmodule Pinchflat.Repo.Migrations.RenameFriendlyNameToCustomName do + use Ecto.Migration + + def change do + rename table(:sources), :friendly_name, to: :custom_name + end +end diff --git a/test/pinchflat/media_client/backends/yt_dlp/output_path_builder_test.exs b/test/pinchflat/media_client/backends/yt_dlp/output_path_builder_test.exs index 8915639..f924981 100644 --- a/test/pinchflat/media_client/backends/yt_dlp/output_path_builder_test.exs +++ b/test/pinchflat/media_client/backends/yt_dlp/output_path_builder_test.exs @@ -3,7 +3,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.OutputPathBuilderTest do alias Pinchflat.Profiles.Options.YtDlp.OutputPathBuilder - describe "build/1" do + describe "build/2" do test "it expands 'standard' curly brace variables in the template" do assert {:ok, res} = OutputPathBuilder.build("/videos/{{ title }}.{{ ext }}") @@ -16,6 +16,12 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.OutputPathBuilderTest do assert res == "/videos/%(upload_date>%Y)S.%(ext)S" end + test "it respects additional options" do + assert {:ok, res} = OutputPathBuilder.build("/videos/{{ custom }}.{{ ext }}", %{"custom" => "test"}) + + assert res == "/videos/test.%(ext)S" + end + test "it leaves yt-dlp variables alone" do assert {:ok, res} = OutputPathBuilder.build("/videos/%(title)s.%(ext)s") diff --git a/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs b/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs index 69e7f12..38e1ea0 100644 --- a/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs +++ b/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs @@ -1,24 +1,40 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilderTest do - use ExUnit.Case, async: true + use Pinchflat.DataCase + import Pinchflat.MediaFixtures + import Pinchflat.ProfilesFixtures + import Pinchflat.SourcesFixtures - alias Pinchflat.Profiles.MediaProfile + alias Pinchflat.Profiles alias Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilder - @media_profile %MediaProfile{ - output_path_template: "{{ title }}.%(ext)s" - } + setup do + media_profile = media_profile_fixture(%{output_path_template: "{{ title }}.%(ext)s"}) + source = source_fixture(%{media_profile_id: media_profile.id, custom_name: "my source"}) + media_item = Repo.preload(media_item_fixture(source_id: source.id), source: :media_profile) - describe "build/1" do - test "it generates an expanded output path based on the given template" do - assert {:ok, res} = DownloadOptionBuilder.build(@media_profile) + {:ok, media_item: media_item} + end + + describe "build/1 when testing output options" do + test "it generates an expanded output path based on the given template", %{media_item: media_item} do + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert {:output, "/tmp/test/videos/%(title)S.%(ext)s"} in res end + + test "it respects custom output path options", %{media_item: media_item} do + media_item = + update_media_profile_attribute(media_item, %{output_path_template: "{{ source_custom_name }}.%(ext)s"}) + + assert {:ok, res} = DownloadOptionBuilder.build(media_item) + + assert {:output, "/tmp/test/videos/#{media_item.source.custom_name}.%(ext)s"} in res + end end describe "build/1 when testing default options" do - test "it includes default options" do - assert {:ok, res} = DownloadOptionBuilder.build(@media_profile) + test "it includes default options", %{media_item: media_item} do + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :no_progress in res assert :windows_filenames in res @@ -26,109 +42,93 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilderTest do end describe "build/1 when testing subtitle options" do - test "includes :write_subs option when specified" do - media_profile = %MediaProfile{@media_profile | download_subs: true} + test "includes :write_subs option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_subs: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :write_subs in res end - test "forces SRT format when download_subs is true" do - media_profile = %MediaProfile{@media_profile | download_subs: true} + test "forces SRT format when download_subs is true", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_subs: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert {:convert_subs, "srt"} in res end - test "includes :write_auto_subs option when specified" do - media_profile = %MediaProfile{@media_profile | download_subs: true, download_auto_subs: true} + test "includes :write_auto_subs option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_subs: true, download_auto_subs: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :write_auto_subs in res end - test "doesn't include :write_auto_subs option when download_subs is false" do - media_profile = %MediaProfile{@media_profile | download_subs: false, download_auto_subs: true} + test "doesn't include :write_auto_subs option when download_subs is false", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_subs: false, download_auto_subs: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) refute :write_auto_subs in res end - test "includes :embed_subs option when specified" do - media_profile = %MediaProfile{@media_profile | embed_subs: true} + test "includes :embed_subs option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{embed_subs: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :embed_subs in res end - test "includes sub_langs option when download_subs is true" do - media_profile = %MediaProfile{@media_profile | download_subs: true, sub_langs: "en"} + test "includes sub_langs option when download_subs is true", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_subs: true, sub_langs: "en"}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert {:sub_langs, "en"} in res end - test "includes sub_langs option when embed_subs is true" do - media_profile = %MediaProfile{@media_profile | embed_subs: true, sub_langs: "en"} + test "includes sub_langs option when embed_subs is true", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{embed_subs: true, sub_langs: "en"}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert {:sub_langs, "en"} in res end - test "doesn't include sub_langs option when neither downloading nor embedding" do - media_profile = %MediaProfile{ - @media_profile - | embed_subs: false, - download_subs: false, - sub_langs: "en" - } + test "doesn't include sub_langs option when neither downloading nor embedding", %{media_item: media_item} do + media_item = + update_media_profile_attribute(media_item, %{embed_subs: false, download_subs: false, sub_langs: "en"}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) refute {:sub_langs, "en"} in res end - - test "other struct attributes are ignored" do - media_profile = %MediaProfile{@media_profile | id: -1} - - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) - - refute {:id, -1} in res - end end describe "build/1 when testing thumbnail options" do - test "includes :write_thumbnail option when specified" do - media_profile = %MediaProfile{@media_profile | download_thumbnail: true} + test "includes :write_thumbnail option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_thumbnail: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :write_thumbnail in res end - test "includes :embed_thumbnail option when specified" do - media_profile = %MediaProfile{@media_profile | embed_thumbnail: true} + test "includes :embed_thumbnail option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{embed_thumbnail: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :embed_thumbnail in res end - test "doesn't include these options when not specified" do - media_profile = %MediaProfile{ - @media_profile - | embed_thumbnail: false, - download_thumbnail: false - } + test "doesn't include these options when not specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{embed_thumbnail: false, download_thumbnail: false}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) refute :write_thumbnail in res refute :embed_thumbnail in res @@ -136,31 +136,27 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilderTest do end describe "build/1 when testing metadata options" do - test "includes :write_info_json option when specified" do - media_profile = %MediaProfile{@media_profile | download_metadata: true} + test "includes :write_info_json option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{download_metadata: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :write_info_json in res assert :clean_info_json in res end - test "includes :embed_metadata option when specified" do - media_profile = %MediaProfile{@media_profile | embed_metadata: true} + test "includes :embed_metadata option when specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{embed_metadata: true}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert :embed_metadata in res end - test "doesn't include these options when not specified" do - media_profile = %MediaProfile{ - @media_profile - | embed_metadata: false, - download_metadata: false - } + test "doesn't include these options when not specified", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{embed_metadata: false, download_metadata: false}) - assert {:ok, res} = DownloadOptionBuilder.build(media_profile) + assert {:ok, res} = DownloadOptionBuilder.build(media_item) refute :write_info_json in res refute :clean_info_json in res @@ -169,10 +165,22 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilderTest do end describe "build/1 when testing quality options" do - test "it includes quality options" do - assert {:ok, res} = DownloadOptionBuilder.build(@media_profile) + test "it includes quality options", %{media_item: media_item} do + media_item = update_media_profile_attribute(media_item, %{preferred_resolution: :"1080p"}) + + assert {:ok, res} = DownloadOptionBuilder.build(media_item) assert {:format_sort, "res:1080,+codec:avc:m4a"} in res end end + + defp update_media_profile_attribute(media_item_with_preloads, attrs) do + media_item_with_preloads.source.media_profile + |> Profiles.change_media_profile(attrs) + |> Repo.update!() + + media_item_with_preloads + |> Repo.reload() + |> Repo.preload(source: :media_profile) + end end diff --git a/test/pinchflat/sources_test.exs b/test/pinchflat/sources_test.exs index 9fccb70..2426c52 100644 --- a/test/pinchflat/sources_test.exs +++ b/test/pinchflat/sources_test.exs @@ -66,18 +66,18 @@ defmodule Pinchflat.SourcesTest do assert String.starts_with?(source.collection_id, "some_playlist_id_") end - test "you can specify a custom friendly_name" do + test "you can specify a custom custom_name" do expect(YtDlpRunnerMock, :run, &channel_mock/3) valid_attrs = %{ media_profile_id: media_profile_fixture().id, original_url: "https://www.youtube.com/channel/abc123", - friendly_name: "some custom name" + custom_name: "some custom name" } assert {:ok, %Source{} = source} = Sources.create_source(valid_attrs) - assert source.friendly_name == "some custom name" + assert source.custom_name == "some custom name" end test "friendly name is pulled from collection_name if not specified" do @@ -90,7 +90,7 @@ defmodule Pinchflat.SourcesTest do assert {:ok, %Source{} = source} = Sources.create_source(valid_attrs) - assert source.friendly_name == "some channel name" + assert source.custom_name == "some channel name" end test "collection_type is inferred from source details" do diff --git a/test/pinchflat/workers/media_indexing_worker_test.exs b/test/pinchflat/workers/media_indexing_worker_test.exs index 176fa56..9fdc00a 100644 --- a/test/pinchflat/workers/media_indexing_worker_test.exs +++ b/test/pinchflat/workers/media_indexing_worker_test.exs @@ -12,23 +12,6 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do setup :verify_on_exit! describe "perform/1" do - test "it does not do any indexing if the source shouldn't be indexed" do - expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end) - - source = source_fixture(index_frequency_minutes: -1) - - perform_job(MediaIndexingWorker, %{id: source.id}) - end - - test "it does not reschedule if the source shouldn't be indexed" do - expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end) - - source = source_fixture(index_frequency_minutes: -1) - perform_job(MediaIndexingWorker, %{id: source.id}) - - refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id}) - end - test "it indexes the source if it should be indexed" do expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end) @@ -37,6 +20,31 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do perform_job(MediaIndexingWorker, %{id: source.id}) end + test "it indexes the source no matter what if the source has never been indexed before" do + expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end) + + source = source_fixture(index_frequency_minutes: 0, last_indexed_at: nil) + + perform_job(MediaIndexingWorker, %{id: source.id}) + end + + test "it does not do any indexing if the source has been indexed and shouldn't be rescheduled" do + expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end) + + source = source_fixture(index_frequency_minutes: -1, last_indexed_at: DateTime.utc_now()) + + perform_job(MediaIndexingWorker, %{id: source.id}) + end + + test "it does not reschedule if the source shouldn't be indexed" do + stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end) + + source = source_fixture(index_frequency_minutes: -1) + perform_job(MediaIndexingWorker, %{id: source.id}) + + refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id}) + end + test "it kicks off a download job for each pending media item" do expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, source_attributes_return_fixture()} end) diff --git a/test/support/fixtures/sources_fixtures.ex b/test/support/fixtures/sources_fixtures.ex index 2a8d8ad..5c25ffc 100644 --- a/test/support/fixtures/sources_fixtures.ex +++ b/test/support/fixtures/sources_fixtures.ex @@ -19,7 +19,7 @@ defmodule Pinchflat.SourcesFixtures do collection_name: "Source ##{:rand.uniform(1_000_000)}", collection_id: Base.encode16(:crypto.hash(:md5, "#{:rand.uniform(1_000_000)}")), collection_type: "channel", - friendly_name: "Cool and good internal name!", + custom_name: "Cool and good internal name!", original_url: "https://www.youtube.com/channel/#{Faker.String.base64(12)}", media_profile_id: ProfilesFixtures.media_profile_fixture().id, index_frequency_minutes: 60