Improve episode-level compatability with media center apps (#86)
* Add media profile presets (#85) * Added presets for output templates * Added presets for the entire media profile form * Append `-thumb` to thumbnails when downloading (#87) * Appended -thumb to thumbnails when downloading * Added code to compensate for yt-dlp bug * Squash all the commits from the other branch bc I broke things (#88)
This commit is contained in:
@@ -125,7 +125,15 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilderTest do
|
||||
assert :write_thumbnail in res
|
||||
end
|
||||
|
||||
test "convertes thumbnail to jpg when download_thumbnail is true", %{media_item: media_item} do
|
||||
test "appends -thumb to the thumbnail name when download_thumbnail is true", %{media_item: media_item} do
|
||||
media_item = update_media_profile_attribute(media_item, %{download_thumbnail: true})
|
||||
|
||||
assert {:ok, res} = DownloadOptionBuilder.build(media_item)
|
||||
|
||||
assert {:output, "thumbnail:/tmp/test/media/%(title)S-thumb.%(ext)s"} in res
|
||||
end
|
||||
|
||||
test "converts thumbnail to jpg when download_thumbnail is true", %{media_item: media_item} do
|
||||
media_item = update_media_profile_attribute(media_item, %{download_thumbnail: true})
|
||||
|
||||
assert {:ok, res} = DownloadOptionBuilder.build(media_item)
|
||||
|
||||
@@ -94,6 +94,15 @@ defmodule Pinchflat.Metadata.MetadataParserTest do
|
||||
assert String.ends_with?(result.thumbnail_filepath, ".webp")
|
||||
end
|
||||
|
||||
# NOTE: this can be removed once this bug is fixed
|
||||
# https://github.com/yt-dlp/yt-dlp/issues/9445
|
||||
# and the associated conditional in the parser is removed
|
||||
test "automatically appends `-thumb` to the thumbnail filename", %{metadata: metadata} do
|
||||
result = Parser.parse_for_media_item(metadata)
|
||||
|
||||
assert String.contains?(result.thumbnail_filepath, "-thumb.webp")
|
||||
end
|
||||
|
||||
test "doesn't freak out if the media has no thumbnails", %{metadata: metadata} do
|
||||
metadata = Map.put(metadata, "thumbnails", %{})
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
||||
use Pinchflat.DataCase
|
||||
import Mox
|
||||
import Pinchflat.SourcesFixtures
|
||||
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
describe "kickoff_with_task/1" do
|
||||
test "enqueues a new worker for the source" do
|
||||
source = source_fixture()
|
||||
|
||||
assert {:ok, _} = SourceMetadataStorageWorker.kickoff_with_task(source)
|
||||
|
||||
assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
|
||||
end
|
||||
|
||||
test "creates a new task for the source" do
|
||||
source = source_fixture()
|
||||
|
||||
assert {:ok, task} = SourceMetadataStorageWorker.kickoff_with_task(source)
|
||||
|
||||
assert task.source_id == source.id
|
||||
end
|
||||
end
|
||||
|
||||
describe "perform/1" do
|
||||
test "sets metadata location for source" do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "{}"} end)
|
||||
source = Repo.preload(source_fixture(), :metadata)
|
||||
|
||||
refute source.metadata
|
||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||
source = Repo.preload(Repo.reload(source), :metadata)
|
||||
|
||||
assert source.metadata.metadata_filepath
|
||||
|
||||
File.rm!(source.metadata.metadata_filepath)
|
||||
end
|
||||
|
||||
test "fetches and stores returned metadata for source" do
|
||||
source = source_fixture()
|
||||
file_contents = Phoenix.json_library().encode!(%{"title" => "test"})
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, file_contents} end)
|
||||
|
||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||
source = Repo.preload(Repo.reload(source), :metadata)
|
||||
{:ok, metadata} = MetadataFileHelpers.read_compressed_metadata(source.metadata.metadata_filepath)
|
||||
|
||||
assert metadata == %{"title" => "test"}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -26,11 +26,11 @@ defmodule Pinchflat.ProfilesTest do
|
||||
|
||||
describe "create_media_profile/1" do
|
||||
test "creation with valid data creates a media_profile" do
|
||||
valid_attrs = %{name: "some name", output_path_template: "some output_path_template"}
|
||||
valid_attrs = %{name: "some name", output_path_template: "output_template.{{ ext }}"}
|
||||
|
||||
assert {:ok, %MediaProfile{} = media_profile} = Profiles.create_media_profile(valid_attrs)
|
||||
assert media_profile.name == "some name"
|
||||
assert media_profile.output_path_template == "some output_path_template"
|
||||
assert media_profile.output_path_template == "output_template.{{ ext }}"
|
||||
end
|
||||
|
||||
test "creation with invalid data returns error changeset" do
|
||||
@@ -44,14 +44,14 @@ defmodule Pinchflat.ProfilesTest do
|
||||
|
||||
update_attrs = %{
|
||||
name: "some updated name",
|
||||
output_path_template: "some updated output_path_template"
|
||||
output_path_template: "new_output_template.{{ ext }}"
|
||||
}
|
||||
|
||||
assert {:ok, %MediaProfile{} = media_profile} =
|
||||
Profiles.update_media_profile(media_profile, update_attrs)
|
||||
|
||||
assert media_profile.name == "some updated name"
|
||||
assert media_profile.output_path_template == "some updated output_path_template"
|
||||
assert media_profile.output_path_template == "new_output_template.{{ ext }}"
|
||||
end
|
||||
|
||||
test "updating with invalid data returns error changeset" do
|
||||
@@ -132,5 +132,41 @@ defmodule Pinchflat.ProfilesTest do
|
||||
media_profile = media_profile_fixture()
|
||||
assert %Ecto.Changeset{} = Profiles.change_media_profile(media_profile)
|
||||
end
|
||||
|
||||
test "it ensures the media profile's output template ends with an extension" do
|
||||
valid_templates = [
|
||||
"output_template.{{ ext }}",
|
||||
"output_template.{{ext}}",
|
||||
"output_template.%(ext)s",
|
||||
"output_template.%(ext)S",
|
||||
"output_template.%( ext )s",
|
||||
"output_template.%( ext )S"
|
||||
]
|
||||
|
||||
for template <- valid_templates do
|
||||
cs = Profiles.change_media_profile(%MediaProfile{}, %{name: "a", output_path_template: template})
|
||||
|
||||
assert cs.valid?
|
||||
end
|
||||
end
|
||||
|
||||
test "it does not allow invalid output templates" do
|
||||
invalid_templates = [
|
||||
"output_template.{{ ext }}.something",
|
||||
"output_template.{{ ext }}",
|
||||
"output_template{{ ext }}",
|
||||
"output_template.%(ext)s.something",
|
||||
"output_template.txt",
|
||||
"output_template%(ext)s",
|
||||
"output_template.%(nope)s",
|
||||
"output_template"
|
||||
]
|
||||
|
||||
for template <- invalid_templates do
|
||||
cs = Profiles.change_media_profile(%MediaProfile{}, %{name: "a", output_path_template: template})
|
||||
|
||||
refute cs.valid?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -101,7 +101,10 @@ defmodule Pinchflat.SlowIndexing.MediaCollectionIndexingWorkerTest do
|
||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:ok, ""} end)
|
||||
|
||||
source = source_fixture(index_frequency_minutes: 10)
|
||||
task_count_fetcher = fn -> Enum.count(Tasks.list_tasks()) end
|
||||
|
||||
task_count_fetcher = fn ->
|
||||
Enum.count(Tasks.list_tasks_for(:source_id, source.id, "MediaCollectionIndexingWorker"))
|
||||
end
|
||||
|
||||
assert_changed([from: 0, to: 1], task_count_fetcher, fn ->
|
||||
perform_job(MediaCollectionIndexingWorker, %{id: source.id})
|
||||
|
||||
@@ -8,16 +8,32 @@ defmodule Pinchflat.SourcesTest do
|
||||
|
||||
alias Pinchflat.Sources
|
||||
alias Pinchflat.Sources.Source
|
||||
alias Pinchflat.Metadata.MetadataFileHelpers
|
||||
alias Pinchflat.Downloading.DownloadingHelpers
|
||||
alias Pinchflat.FastIndexing.FastIndexingWorker
|
||||
alias Pinchflat.Downloading.MediaDownloadWorker
|
||||
alias Pinchflat.FastIndexing.MediaIndexingWorker
|
||||
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
||||
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
||||
|
||||
@invalid_source_attrs %{name: nil, collection_id: nil}
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
describe "schema" do
|
||||
test "source_metadata is deleted when the source is deleted" do
|
||||
source =
|
||||
source_fixture(%{metadata: %{metadata_filepath: "/metadata.json.gz"}})
|
||||
|
||||
metadata = source.metadata
|
||||
assert {:ok, %Source{}} = Sources.delete_source(source)
|
||||
|
||||
assert_raise Ecto.NoResultsError, fn ->
|
||||
Repo.reload!(metadata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "list_sources/0" do
|
||||
test "it returns all sources" do
|
||||
source = source_fixture()
|
||||
@@ -220,6 +236,21 @@ defmodule Pinchflat.SourcesTest do
|
||||
|
||||
assert source.index_frequency_minutes == 0
|
||||
end
|
||||
|
||||
test "creating will kickoff a metadata storage worker" do
|
||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
||||
|
||||
valid_attrs = %{
|
||||
media_profile_id: media_profile_fixture().id,
|
||||
original_url: "https://www.youtube.com/channel/abc123",
|
||||
fast_index: false,
|
||||
index_frequency_minutes: 0
|
||||
}
|
||||
|
||||
assert {:ok, %Source{} = source} = Sources.create_source(valid_attrs)
|
||||
|
||||
assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_source/2" do
|
||||
@@ -384,6 +415,15 @@ defmodule Pinchflat.SourcesTest do
|
||||
|
||||
assert source.index_frequency_minutes == 0
|
||||
end
|
||||
|
||||
test "updating will kickoff a metadata storage worker" do
|
||||
source = source_fixture()
|
||||
update_attrs = %{name: "some updated name"}
|
||||
|
||||
assert {:ok, %Source{} = source} = Sources.update_source(source, update_attrs)
|
||||
|
||||
assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_source/2" do
|
||||
@@ -421,6 +461,22 @@ defmodule Pinchflat.SourcesTest do
|
||||
assert {:ok, %Source{}} = Sources.delete_source(source)
|
||||
assert File.exists?(media_item.media_filepath)
|
||||
end
|
||||
|
||||
test "deletes the source's metadata files" do
|
||||
stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
|
||||
source = Repo.preload(source_fixture(), :metadata)
|
||||
|
||||
update_attrs = %{
|
||||
metadata: %{
|
||||
metadata_filepath: MetadataFileHelpers.compress_and_store_metadata_for(source, %{})
|
||||
}
|
||||
}
|
||||
|
||||
{:ok, updated_source} = Sources.update_source(source, update_attrs)
|
||||
|
||||
assert {:ok, _} = Sources.delete_source(updated_source, delete_files: true)
|
||||
refute File.exists?(updated_source.metadata.metadata_filepath)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_source/2 when deleting files" do
|
||||
@@ -452,18 +508,18 @@ defmodule Pinchflat.SourcesTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "change_source_from_url/2" do
|
||||
describe "maybe_change_source_from_url/2" do
|
||||
test "it returns a changeset" do
|
||||
stub(YtDlpRunnerMock, :run, &channel_mock/3)
|
||||
source = source_fixture()
|
||||
|
||||
assert %Ecto.Changeset{} = Sources.change_source_from_url(source, %{})
|
||||
assert %Ecto.Changeset{} = Sources.maybe_change_source_from_url(source, %{})
|
||||
end
|
||||
|
||||
test "it does not fetch source details if the original_url isn't in the changeset" do
|
||||
expect(YtDlpRunnerMock, :run, 0, &channel_mock/3)
|
||||
|
||||
changeset = Sources.change_source_from_url(%Source{}, %{name: "some updated name"})
|
||||
changeset = Sources.maybe_change_source_from_url(%Source{}, %{name: "some updated name"})
|
||||
|
||||
assert %Ecto.Changeset{} = changeset
|
||||
end
|
||||
@@ -472,7 +528,7 @@ defmodule Pinchflat.SourcesTest do
|
||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
||||
|
||||
changeset =
|
||||
Sources.change_source_from_url(%Source{}, %{
|
||||
Sources.maybe_change_source_from_url(%Source{}, %{
|
||||
original_url: "https://www.youtube.com/channel/abc123"
|
||||
})
|
||||
|
||||
@@ -486,7 +542,7 @@ defmodule Pinchflat.SourcesTest do
|
||||
media_profile_id = media_profile.id
|
||||
|
||||
changeset =
|
||||
Sources.change_source_from_url(%Source{}, %{
|
||||
Sources.maybe_change_source_from_url(%Source{}, %{
|
||||
original_url: "https://www.youtube.com/channel/abc123",
|
||||
media_profile_id: media_profile.id
|
||||
})
|
||||
@@ -507,7 +563,7 @@ defmodule Pinchflat.SourcesTest do
|
||||
end)
|
||||
|
||||
changeset =
|
||||
Sources.change_source_from_url(%Source{}, %{
|
||||
Sources.maybe_change_source_from_url(%Source{}, %{
|
||||
original_url: "https://www.youtube.com/channel/abc123"
|
||||
})
|
||||
|
||||
|
||||
@@ -108,4 +108,39 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
||||
assert {:error, %Jason.DecodeError{}} = MediaCollection.get_source_details(@channel_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_source_metadata/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"})
|
||||
end)
|
||||
|
||||
assert {:ok, res} = MediaCollection.get_source_metadata(@channel_url)
|
||||
|
||||
assert %{"channel" => "TheUselessTrials"} = res
|
||||
end
|
||||
|
||||
test "it passes the expected args to the backend runner" do
|
||||
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
|
||||
assert opts == [playlist_items: 0]
|
||||
assert ot == "playlist:%()j"
|
||||
|
||||
{:ok, "{}"}
|
||||
end)
|
||||
|
||||
assert {:ok, _} = MediaCollection.get_source_metadata(@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_metadata(@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_metadata(@channel_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user