Fast indexing (#58)
* Made method to getting singular media details; Renamed other related method * Takes a fun and flirty digression to remove abstractions around yt-dlp since I'm 100% committed to using it exclusively * Removed commented test code * Lays the groundwork for fast indexing * Added module for working with youtube RSS feed * Added methods to kick off indexing workers from RSS response * Improve short detection (#59) * Made media attribute-related yt-dlp calls return a struct * Added shorts attribute to media items * Added ability to discern a short from yt-dlp response * Updated search to use new shorts attribute * Fast index UI (#63) * Added fast_index field and adds it to source form * Added fast indexing to source changeset operations * Added fast indexing worker and updated other modules to start using it * Handled fast index worker on source update * Add support modals (#65) * Added fast indexing upgrade modal * Improved modal on smaller screens * Updated links to work again * Added donation modal * Reverted source fast index to 15 minutes * Removed unneeded HTML attributes from old alpine approach
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
defmodule Pinchflat.YtDlp.Backend.CommandRunnerTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Pinchflat.YtDlp.Backend.CommandRunner, as: Runner
|
||||
|
||||
@original_executable Application.compile_env(:pinchflat, :yt_dlp_executable)
|
||||
@media_url "https://www.youtube.com/watch?v=-LHXuyzpex0"
|
||||
|
||||
setup do
|
||||
on_exit(&reset_executable/0)
|
||||
end
|
||||
|
||||
describe "run/4" do
|
||||
test "it returns the output and status when the command succeeds" do
|
||||
assert {:ok, _output} = Runner.run(@media_url, [], "")
|
||||
end
|
||||
|
||||
test "it converts symbol k-v arg keys to kebab case" do
|
||||
assert {:ok, output} = Runner.run(@media_url, [buffer_size: 1024], "")
|
||||
|
||||
assert String.contains?(output, "--buffer-size 1024")
|
||||
end
|
||||
|
||||
test "it keeps string k-v arg keys untouched" do
|
||||
assert {:ok, output} = Runner.run(@media_url, [{"--under_score", 1024}], "")
|
||||
|
||||
assert String.contains?(output, "--under_score 1024")
|
||||
end
|
||||
|
||||
test "it converts symbol arg keys to kebab case" do
|
||||
assert {:ok, output} = Runner.run(@media_url, [:ignore_errors], "")
|
||||
|
||||
assert String.contains?(output, "--ignore-errors")
|
||||
end
|
||||
|
||||
test "it keeps string arg keys untouched" do
|
||||
assert {:ok, output} = Runner.run(@media_url, ["-v"], "")
|
||||
|
||||
assert String.contains?(output, "-v")
|
||||
refute String.contains?(output, "--v")
|
||||
end
|
||||
|
||||
test "it includes the media url as the first argument" do
|
||||
assert {:ok, output} = Runner.run(@media_url, [:ignore_errors], "")
|
||||
|
||||
assert String.contains?(output, "#{@media_url} --ignore-errors")
|
||||
end
|
||||
|
||||
test "it automatically includes the --print-to-file flag" do
|
||||
assert {:ok, output} = Runner.run(@media_url, [], "%(id)s")
|
||||
|
||||
assert String.contains?(output, "--print-to-file %(id)s /tmp/")
|
||||
end
|
||||
|
||||
test "it returns the output and status when the command fails" do
|
||||
wrap_executable("/bin/false", fn ->
|
||||
assert {:error, "", 1} = Runner.run(@media_url, [], "")
|
||||
end)
|
||||
end
|
||||
|
||||
test "optionally lets you specify an output_filepath" do
|
||||
assert {:ok, output} = Runner.run(@media_url, [], "%(id)s", output_filepath: "/tmp/yt-dlp-output.json")
|
||||
|
||||
assert String.contains?(output, "--print-to-file %(id)s /tmp/yt-dlp-output.json")
|
||||
end
|
||||
end
|
||||
|
||||
defp wrap_executable(new_executable, fun) do
|
||||
Application.put_env(:pinchflat, :yt_dlp_executable, new_executable)
|
||||
fun.()
|
||||
reset_executable()
|
||||
end
|
||||
|
||||
def reset_executable do
|
||||
Application.put_env(:pinchflat, :yt_dlp_executable, @original_executable)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,111 @@
|
||||
defmodule Pinchflat.YtDlp.Backend.MediaCollectionTest do
|
||||
use Pinchflat.DataCase
|
||||
import Mox
|
||||
import Pinchflat.SourcesFixtures
|
||||
|
||||
alias Pinchflat.YtDlp.Backend.Media
|
||||
alias Pinchflat.YtDlp.Backend.MediaCollection
|
||||
|
||||
@channel_url "https://www.youtube.com/c/TheUselessTrials"
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
describe "get_media_attributes_for_collection/2" do
|
||||
test "returns a list of video attributes with no blank elements" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts ->
|
||||
{:ok, source_attributes_return_fixture() <> "\n\n"}
|
||||
end)
|
||||
|
||||
assert {:ok, [%Media{media_id: "video1"}, %Media{media_id: "video2"}, %Media{media_id: "video3"}]} =
|
||||
MediaCollection.get_media_attributes_for_collection(@channel_url)
|
||||
end
|
||||
|
||||
test "it passes the expected default args" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, opts, ot, _addl_opts ->
|
||||
assert opts == [:simulate, :skip_download]
|
||||
assert ot == Media.indexing_output_template()
|
||||
|
||||
{:ok, ""}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = MediaCollection.get_media_attributes_for_collection(@channel_url)
|
||||
end
|
||||
|
||||
test "returns the error straight through when the command fails" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:error, "Big issue", 1} end)
|
||||
|
||||
assert {:error, "Big issue", 1} = MediaCollection.get_media_attributes_for_collection(@channel_url)
|
||||
end
|
||||
|
||||
test "passes the explict tmpfile path to runner" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl_opts ->
|
||||
assert [{:output_filepath, filepath}] = addl_opts
|
||||
assert String.ends_with?(filepath, ".json")
|
||||
|
||||
{:ok, ""}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = MediaCollection.get_media_attributes_for_collection(@channel_url)
|
||||
end
|
||||
|
||||
test "supports an optional file_listener_handler that gets passed a filename" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:ok, ""} end)
|
||||
current_self = self()
|
||||
|
||||
handler = fn filename ->
|
||||
send(current_self, {:handler, filename})
|
||||
end
|
||||
|
||||
assert {:ok, _} =
|
||||
MediaCollection.get_media_attributes_for_collection(@channel_url, file_listener_handler: handler)
|
||||
|
||||
assert_receive {:handler, filename}
|
||||
assert String.ends_with?(filename, ".json")
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_source_details/1" do
|
||||
test "it returns a map with data on success" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||
Phoenix.json_library().encode(%{
|
||||
channel: "TheUselessTrials",
|
||||
channel_id: "UCQH2",
|
||||
playlist_id: "PLQH2",
|
||||
playlist_title: "TheUselessTrials - Videos"
|
||||
})
|
||||
end)
|
||||
|
||||
assert {:ok, res} = MediaCollection.get_source_details(@channel_url)
|
||||
|
||||
assert %{
|
||||
channel_id: "UCQH2",
|
||||
channel_name: "TheUselessTrials",
|
||||
playlist_id: "PLQH2",
|
||||
playlist_name: "TheUselessTrials - Videos"
|
||||
} = res
|
||||
end
|
||||
|
||||
test "it passes the expected args to the backend runner" do
|
||||
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
|
||||
assert opts == [:simulate, :skip_download, playlist_end: 1]
|
||||
assert ot == "%(.{channel,channel_id,playlist_id,playlist_title})j"
|
||||
|
||||
{:ok, "{}"}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = MediaCollection.get_source_details(@channel_url)
|
||||
end
|
||||
|
||||
test "it returns an error if the runner returns an error" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
||||
|
||||
assert {:error, "Big issue", 1} = MediaCollection.get_source_details(@channel_url)
|
||||
end
|
||||
|
||||
test "it returns an error if the output is not JSON" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end)
|
||||
|
||||
assert {:error, %Jason.DecodeError{}} = MediaCollection.get_source_details(@channel_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,139 @@
|
||||
defmodule Pinchflat.YtDlp.Backend.MediaTest do
|
||||
use Pinchflat.DataCase
|
||||
import Mox
|
||||
import Pinchflat.MediaFixtures
|
||||
|
||||
alias Pinchflat.YtDlp.Backend.Media
|
||||
|
||||
@media_url "https://www.youtube.com/watch?v=TiZPUDkDYbk"
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
describe "download/2" do
|
||||
test "it calls the backend runner with the expected arguments" do
|
||||
expect(YtDlpRunnerMock, :run, fn @media_url, opts, ot ->
|
||||
assert [:no_simulate] = opts
|
||||
assert "after_move:%()j" = ot
|
||||
|
||||
{:ok, render_metadata(:media_metadata)}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = Media.download(@media_url)
|
||||
end
|
||||
|
||||
test "it passes along additional options" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot ->
|
||||
assert [:no_simulate, :custom_arg] = opts
|
||||
|
||||
{:ok, "{}"}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = Media.download(@media_url, [:custom_arg])
|
||||
end
|
||||
|
||||
test "it parses and returns the generated file as JSON" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||
{:ok, render_metadata(:media_metadata)}
|
||||
end)
|
||||
|
||||
assert {:ok, %{"title" => "Trying to Wheelie Without the Rear Brake"}} =
|
||||
Media.download(@media_url)
|
||||
end
|
||||
|
||||
test "it returns errors" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opt, _ot ->
|
||||
{:error, "something"}
|
||||
end)
|
||||
|
||||
assert {:error, "something"} = Media.download(@media_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_media_attributes/1" do
|
||||
test "returns a list of video attributes" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||
{:ok, media_attributes_return_fixture()}
|
||||
end)
|
||||
|
||||
assert {:ok, %{description: _, media_id: _, original_url: _, title: _, livestream: _}} =
|
||||
Media.get_media_attributes(@media_url)
|
||||
end
|
||||
|
||||
test "it passes the expected default args" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, opts, ot ->
|
||||
assert opts == [:simulate, :skip_download]
|
||||
assert ot == Media.indexing_output_template()
|
||||
|
||||
{:ok, media_attributes_return_fixture()}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = Media.get_media_attributes(@media_url)
|
||||
end
|
||||
|
||||
test "returns the error straight through when the command fails" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
||||
|
||||
assert {:error, "Big issue", 1} = Media.get_media_attributes(@media_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe "indexing_output_template/0" do
|
||||
test "contains all the greatest hits" do
|
||||
assert "%(.{id,title,was_live,webpage_url,description,aspect_ratio,duration})j" ==
|
||||
Media.indexing_output_template()
|
||||
end
|
||||
end
|
||||
|
||||
describe "response_to_struct/1" do
|
||||
test "transforms a response into a struct" do
|
||||
response = %{
|
||||
"id" => "TiZPUDkDYbk",
|
||||
"title" => "Trying to Wheelie Without the Rear Brake",
|
||||
"description" => "I'm not sure what I expected.",
|
||||
"webpage_url" => "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||
"was_live" => false,
|
||||
"aspect_ratio" => 1.0,
|
||||
"duration" => 60
|
||||
}
|
||||
|
||||
assert %Media{
|
||||
media_id: "TiZPUDkDYbk",
|
||||
title: "Trying to Wheelie Without the Rear Brake",
|
||||
description: "I'm not sure what I expected.",
|
||||
original_url: "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||
livestream: false,
|
||||
short_form_content: false
|
||||
} = Media.response_to_struct(response)
|
||||
end
|
||||
|
||||
test "sets short_form_content to true if the URL contains /shorts/" do
|
||||
response = %{
|
||||
"webpage_url" => "https://www.youtube.com/shorts/TiZPUDkDYbk",
|
||||
"aspect_ratio" => 1.0,
|
||||
"duration" => 61
|
||||
}
|
||||
|
||||
assert %Media{short_form_content: true} = Media.response_to_struct(response)
|
||||
end
|
||||
|
||||
test "sets short_form_content to true if the aspect ratio are duration are right" do
|
||||
response = %{
|
||||
"webpage_url" => "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||
"aspect_ratio" => 0.5,
|
||||
"duration" => 59
|
||||
}
|
||||
|
||||
assert %Media{short_form_content: true} = Media.response_to_struct(response)
|
||||
end
|
||||
|
||||
test "sets short_form_content to false otherwise" do
|
||||
response = %{
|
||||
"webpage_url" => "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||
"aspect_ratio" => 1.0,
|
||||
"duration" => 61
|
||||
}
|
||||
|
||||
assert %Media{short_form_content: false} = Media.response_to_struct(response)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,219 @@
|
||||
defmodule Pinchflat.YtDlp.DownloadOptionBuilderTest do
|
||||
use Pinchflat.DataCase
|
||||
import Pinchflat.MediaFixtures
|
||||
import Pinchflat.ProfilesFixtures
|
||||
import Pinchflat.SourcesFixtures
|
||||
|
||||
alias Pinchflat.Profiles
|
||||
alias Pinchflat.YtDlp.DownloadOptionBuilder
|
||||
|
||||
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)
|
||||
|
||||
{: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/media/%(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/media/#{media_item.source.custom_name}.%(ext)s"} in res
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/1 when testing default options" do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/1 when testing subtitle options" do
|
||||
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_item)
|
||||
|
||||
assert :write_subs in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert {:convert_subs, "srt"} in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert :write_auto_subs in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
refute :write_auto_subs in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert :embed_subs in res
|
||||
end
|
||||
|
||||
test "doesn't include :embed_subs option when preferred_resolution is :audio", %{media_item: media_item} do
|
||||
media_item = update_media_profile_attribute(media_item, %{embed_subs: true, preferred_resolution: :audio})
|
||||
|
||||
assert {:ok, res} = DownloadOptionBuilder.build(media_item)
|
||||
|
||||
refute :embed_subs in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert {:sub_langs, "en"} in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert {:sub_langs, "en"} in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
refute {:sub_langs, "en"} in res
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/1 when testing thumbnail options" do
|
||||
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_item)
|
||||
|
||||
assert :write_thumbnail in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert :embed_thumbnail in res
|
||||
end
|
||||
|
||||
test "doesn't include :embed_thumbnail option when preferred_resolution is :audio", %{media_item: media_item} do
|
||||
media_item = update_media_profile_attribute(media_item, %{embed_thumbnail: true, preferred_resolution: :audio})
|
||||
|
||||
assert {:ok, res} = DownloadOptionBuilder.build(media_item)
|
||||
|
||||
refute :embed_thumbnail in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
refute :write_thumbnail in res
|
||||
refute :embed_thumbnail in res
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/1 when testing metadata options" do
|
||||
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_item)
|
||||
|
||||
assert :write_info_json in res
|
||||
assert :clean_info_json in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
assert :embed_metadata in res
|
||||
end
|
||||
|
||||
test "doesn't include :embed_metadata option when preferred_resolution is :audio", %{media_item: media_item} do
|
||||
media_item = update_media_profile_attribute(media_item, %{embed_metadata: true, preferred_resolution: :audio})
|
||||
|
||||
assert {:ok, res} = DownloadOptionBuilder.build(media_item)
|
||||
|
||||
refute :embed_metadata in res
|
||||
end
|
||||
|
||||
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_item)
|
||||
|
||||
refute :write_info_json in res
|
||||
refute :clean_info_json in res
|
||||
refute :embed_metadata in res
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/1 when testing quality options" do
|
||||
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
|
||||
|
||||
test "it includes quality options for audio only", %{media_item: media_item} do
|
||||
media_item = update_media_profile_attribute(media_item, %{preferred_resolution: :audio})
|
||||
|
||||
assert {:ok, res} = DownloadOptionBuilder.build(media_item)
|
||||
|
||||
assert {:format, "bestaudio"} in res
|
||||
assert {:format_sort, "ext"} 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
|
||||
@@ -0,0 +1,106 @@
|
||||
defmodule Pinchflat.MediaClient.MediaDownloaderTest do
|
||||
use Pinchflat.DataCase
|
||||
import Mox
|
||||
import Pinchflat.MediaFixtures
|
||||
|
||||
alias Pinchflat.MediaClient.MediaDownloader
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
setup do
|
||||
media_item =
|
||||
Repo.preload(
|
||||
media_item_fixture(%{title: "Something", media_filepath: nil}),
|
||||
[:metadata, source: :media_profile]
|
||||
)
|
||||
|
||||
stub(HTTPClientMock, :get, fn _url, _headers, _opts ->
|
||||
{:ok, ""}
|
||||
end)
|
||||
|
||||
{:ok, %{media_item: media_item}}
|
||||
end
|
||||
|
||||
describe "download_for_media_item/3" do
|
||||
test "it calls the backend runner", %{media_item: media_item} do
|
||||
expect(YtDlpRunnerMock, :run, fn url, _opts, ot ->
|
||||
assert url == media_item.original_url
|
||||
assert ot == "after_move:%()j"
|
||||
|
||||
{:ok, render_metadata(:media_metadata)}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
||||
end
|
||||
|
||||
test "it saves the metadata filepatha to the database", %{media_item: media_item} do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||
{:ok, render_metadata(:media_metadata)}
|
||||
end)
|
||||
|
||||
assert is_nil(media_item.metadata)
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
|
||||
assert updated_media_item.metadata.metadata_filepath =~ "media_items/#{media_item.id}/metadata.json.gz"
|
||||
assert updated_media_item.metadata.thumbnail_filepath =~ "media_items/#{media_item.id}/maxresdefault.jpg"
|
||||
end
|
||||
|
||||
test "errors are passed through", %{media_item: media_item} do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||
{:error, :some_error}
|
||||
end)
|
||||
|
||||
assert {:error, :some_error} = MediaDownloader.download_for_media_item(media_item)
|
||||
end
|
||||
end
|
||||
|
||||
describe "download_for_media_item/3 when testing media_item attributes" do
|
||||
setup do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||
{:ok, render_metadata(:media_metadata)}
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it sets the media_downloaded_at", %{media_item: media_item} do
|
||||
assert media_item.media_downloaded_at == nil
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert DateTime.diff(DateTime.utc_now(), updated_media_item.media_downloaded_at) < 2
|
||||
end
|
||||
|
||||
test "it extracts the title", %{media_item: media_item} do
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert updated_media_item.title == "Trying to Wheelie Without the Rear Brake"
|
||||
end
|
||||
|
||||
test "it extracts the description", %{media_item: media_item} do
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert is_binary(updated_media_item.description)
|
||||
end
|
||||
|
||||
test "it extracts the media_filepath", %{media_item: media_item} do
|
||||
assert media_item.media_filepath == nil
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert String.ends_with?(updated_media_item.media_filepath, ".mkv")
|
||||
end
|
||||
|
||||
test "it extracts the subtitle_filepaths", %{media_item: media_item} do
|
||||
assert media_item.subtitle_filepaths == []
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert [["de", _], ["en", _] | _rest] = updated_media_item.subtitle_filepaths
|
||||
end
|
||||
|
||||
test "it extracts the thumbnail_filepath", %{media_item: media_item} do
|
||||
assert media_item.thumbnail_filepath == nil
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert String.ends_with?(updated_media_item.thumbnail_filepath, ".webp")
|
||||
end
|
||||
|
||||
test "it extracts the metadata_filepath", %{media_item: media_item} do
|
||||
assert media_item.metadata_filepath == nil
|
||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||
assert String.ends_with?(updated_media_item.metadata_filepath, ".info.json")
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user