got media source test back working

This commit is contained in:
Kieran Eglin 2024-02-01 21:05:13 -08:00
parent d336f6f46c
commit d54f9f8a79
No known key found for this signature in database
GPG key ID: 193984967FCF432D
21 changed files with 197 additions and 190 deletions

View file

@ -10,8 +10,8 @@ defmodule Pinchflat.Media.MediaItem do
alias Pinchflat.MediaSource.Channel alias Pinchflat.MediaSource.Channel
alias Pinchflat.Media.MediaMetadata alias Pinchflat.Media.MediaMetadata
@required_fields ~w(media_id channel_id)a @required_fields ~w(media_id source_id)a
@allowed_fields ~w(title media_id media_filepath channel_id subtitle_filepaths)a @allowed_fields ~w(title media_id media_filepath source_id subtitle_filepaths)a
schema "media_items" do schema "media_items" do
field :title, :string field :title, :string
@ -22,7 +22,7 @@ defmodule Pinchflat.Media.MediaItem do
# Will very likely revisit because I can't leave well-enough alone. # Will very likely revisit because I can't leave well-enough alone.
field :subtitle_filepaths, {:array, {:array, :string}}, default: [] field :subtitle_filepaths, {:array, {:array, :string}}, default: []
belongs_to :channel, Channel belongs_to :channel, Channel, foreign_key: :source_id
has_one :metadata, MediaMetadata, on_replace: :update has_one :metadata, MediaMetadata, on_replace: :update
@ -37,6 +37,6 @@ defmodule Pinchflat.Media.MediaItem do
|> cast(attrs, @allowed_fields) |> cast(attrs, @allowed_fields)
|> cast_assoc(:metadata, with: &MediaMetadata.changeset/2, required: false) |> cast_assoc(:metadata, with: &MediaMetadata.changeset/2, required: false)
|> validate_required(@required_fields) |> validate_required(@required_fields)
|> unique_constraint([:media_id, :channel_id]) |> unique_constraint([:media_id, :source_id])
end end
end end

View file

@ -15,7 +15,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.Channel do
Returns {:ok, %ChannelDetails{}} | {:error, any, ...}. Returns {:ok, %ChannelDetails{}} | {:error, any, ...}.
""" """
def get_channel_details(channel_url) do def get_source_details(channel_url) do
opts = [:skip_download, playlist_end: 1] opts = [:skip_download, playlist_end: 1]
with {:ok, output} <- backend_runner().run(channel_url, opts, "%(.{channel,channel_id})j"), with {:ok, output} <- backend_runner().run(channel_url, opts, "%(.{channel,channel_id})j"),

View file

@ -20,8 +20,8 @@ defmodule Pinchflat.MediaClient.ChannelDetails do
Returns {:ok, map()} | {:error, any, ...}. Returns {:ok, map()} | {:error, any, ...}.
""" """
def get_channel_details(channel_url, backend \\ :yt_dlp) do def get_source_details(channel_url, backend \\ :yt_dlp) do
channel_module(backend).get_channel_details(channel_url) channel_module(backend).get_source_details(channel_url)
end end
@doc """ @doc """

View file

@ -15,7 +15,7 @@ defmodule Pinchflat.MediaSource do
@doc """ @doc """
Returns the list of channels. Returns [%Channel{}, ...] Returns the list of channels. Returns [%Channel{}, ...]
""" """
def list_channels do def list_sources do
Repo.all(Channel) Repo.all(Channel)
end end
@ -24,7 +24,7 @@ defmodule Pinchflat.MediaSource do
Returns %Channel{}. Raises `Ecto.NoResultsError` if the Channel does not exist. Returns %Channel{}. Raises `Ecto.NoResultsError` if the Channel does not exist.
""" """
def get_channel!(id), do: Repo.get!(Channel, id) def get_source!(id), do: Repo.get!(Channel, id)
@doc """ @doc """
Creates a channel. May attempt to pull additional channel details from the Creates a channel. May attempt to pull additional channel details from the
@ -33,9 +33,9 @@ defmodule Pinchflat.MediaSource do
Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}} Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}}
""" """
def create_channel(attrs) do def create_source(attrs) do
%Channel{} %Channel{}
|> change_channel_from_url(attrs) |> change_source_from_url(attrs)
|> commit_and_start_indexing() |> commit_and_start_indexing()
end end
@ -45,12 +45,12 @@ defmodule Pinchflat.MediaSource do
Returns [%MediaItem{}, ...] | [%Ecto.Changeset{}, ...] Returns [%MediaItem{}, ...] | [%Ecto.Changeset{}, ...]
""" """
def index_media_items(%Channel{} = channel) do def index_media_items(%Channel{} = source) do
{:ok, media_ids} = ChannelDetails.get_video_ids(channel.original_url) {:ok, media_ids} = ChannelDetails.get_video_ids(source.original_url)
media_ids media_ids
|> Enum.map(fn media_id -> |> Enum.map(fn media_id ->
attrs = %{channel_id: channel.id, media_id: media_id} attrs = %{source_id: source.id, media_id: media_id}
case Media.create_media_item(attrs) do case Media.create_media_item(attrs) do
{:ok, media_item} -> media_item {:ok, media_item} -> media_item
@ -69,9 +69,9 @@ defmodule Pinchflat.MediaSource do
Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}} Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}}
""" """
def update_channel(%Channel{} = channel, attrs) do def update_source(%Channel{} = channel, attrs) do
channel channel
|> change_channel_from_url(attrs) |> change_source_from_url(attrs)
|> commit_and_start_indexing() |> commit_and_start_indexing()
end end
@ -80,7 +80,7 @@ defmodule Pinchflat.MediaSource do
Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}} Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}}
""" """
def delete_channel(%Channel{} = channel) do def delete_source(%Channel{} = channel) do
Tasks.delete_tasks_for(channel) Tasks.delete_tasks_for(channel)
Repo.delete(channel) Repo.delete(channel)
end end
@ -88,7 +88,7 @@ defmodule Pinchflat.MediaSource do
@doc """ @doc """
Returns an `%Ecto.Changeset{}` for tracking channel changes. Returns an `%Ecto.Changeset{}` for tracking channel changes.
""" """
def change_channel(%Channel{} = channel, attrs \\ %{}) do def change_source(%Channel{} = channel, attrs \\ %{}) do
Channel.changeset(channel, attrs) Channel.changeset(channel, attrs)
end end
@ -101,26 +101,26 @@ defmodule Pinchflat.MediaSource do
This means that it'll go for it even if a changeset is otherwise invalid. This This means that it'll go for it even if a changeset is otherwise invalid. This
is pretty easy to change, but for MVP I'm not concerned. is pretty easy to change, but for MVP I'm not concerned.
""" """
def change_channel_from_url(%Channel{} = channel, attrs) do def change_source_from_url(%Channel{} = channel, attrs) do
case change_channel(channel, attrs) do case change_source(channel, attrs) do
%Ecto.Changeset{changes: %{original_url: _}} = changeset -> %Ecto.Changeset{changes: %{original_url: _}} = changeset ->
add_channel_details_to_changeset(channel, changeset) add_source_details_to_changeset(channel, changeset)
changeset -> changeset ->
changeset changeset
end end
end end
defp add_channel_details_to_changeset(channel, changeset) do defp add_source_details_to_changeset(channel, changeset) do
%Ecto.Changeset{changes: changes} = changeset %Ecto.Changeset{changes: changes} = changeset
case ChannelDetails.get_channel_details(changes.original_url) do case ChannelDetails.get_source_details(changes.original_url) do
{:ok, %ChannelDetails{} = channel_details} -> {:ok, %ChannelDetails{} = channel_details} ->
change_channel( change_source(
channel, channel,
Map.merge(changes, %{ Map.merge(changes, %{
name: channel_details.name, name: channel_details.name,
channel_id: channel_details.id collection_id: channel_details.id
}) })
) )

View file

@ -9,12 +9,13 @@ defmodule Pinchflat.MediaSource.Channel do
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
@allowed_fields ~w(name channel_id index_frequency_minutes original_url media_profile_id)a @allowed_fields ~w(name collection_id collection_type index_frequency_minutes original_url media_profile_id)a
@required_fields @allowed_fields -- ~w(index_frequency_minutes)a @required_fields @allowed_fields -- ~w(index_frequency_minutes)a
schema "channels" do schema "sources" do
field :name, :string field :name, :string
field :channel_id, :string field :collection_id, :string
field :collection_type, Ecto.Enum, values: [:channel, :playlist]
field :index_frequency_minutes, :integer field :index_frequency_minutes, :integer
# This should only be used for user reference going forward # This should only be used for user reference going forward
# as the channel_id should be used for all API calls # as the channel_id should be used for all API calls
@ -22,16 +23,16 @@ defmodule Pinchflat.MediaSource.Channel do
belongs_to :media_profile, MediaProfile belongs_to :media_profile, MediaProfile
has_many :media_items, MediaItem has_many :media_items, MediaItem, foreign_key: :source_id
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)
end end
@doc false @doc false
def changeset(channel, attrs) do def changeset(source, attrs) do
channel source
|> cast(attrs, @allowed_fields) |> cast(attrs, @allowed_fields)
|> validate_required(@required_fields) |> validate_required(@required_fields)
|> unique_constraint([:channel_id, :media_profile_id]) |> unique_constraint([:collection_id, :media_profile_id])
end end
end end

View file

@ -71,7 +71,7 @@ defmodule Pinchflat.Tasks do
def create_task(%Oban.Job{} = job, attached_record) do def create_task(%Oban.Job{} = job, attached_record) do
attached_record_attr = attached_record_attr =
case attached_record do case attached_record do
%Channel{} = channel -> %{channel_id: channel.id} %Channel{} = channel -> %{source_id: channel.id}
%MediaItem{} = media_item -> %{media_item_id: media_item.id} %MediaItem{} = media_item -> %{media_item_id: media_item.id}
end end
@ -113,7 +113,7 @@ defmodule Pinchflat.Tasks do
def delete_tasks_for(attached_record) do def delete_tasks_for(attached_record) do
tasks = tasks =
case attached_record do case attached_record do
%Channel{} = channel -> list_tasks_for(:channel_id, channel.id) %Channel{} = source -> list_tasks_for(:source_id, source.id)
%MediaItem{} = media_item -> list_tasks_for(:media_item_id, media_item.id) %MediaItem{} = media_item -> list_tasks_for(:media_item_id, media_item.id)
end end
@ -130,7 +130,7 @@ defmodule Pinchflat.Tasks do
def delete_pending_tasks_for(attached_record) do def delete_pending_tasks_for(attached_record) do
tasks = tasks =
case attached_record do case attached_record do
%Channel{} = channel -> list_pending_tasks_for(:channel_id, channel.id) %Channel{} = source -> list_pending_tasks_for(:source_id, source.id)
%MediaItem{} = media_item -> list_pending_tasks_for(:media_item_id, media_item.id) %MediaItem{} = media_item -> list_pending_tasks_for(:media_item_id, media_item.id)
end end

View file

@ -11,7 +11,7 @@ defmodule Pinchflat.Tasks.Task do
schema "tasks" do schema "tasks" do
belongs_to :job, Oban.Job belongs_to :job, Oban.Job
belongs_to :channel, Channel belongs_to :channel, Channel, foreign_key: :source_id
belongs_to :media_item, MediaItem belongs_to :media_item, MediaItem
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)
@ -20,7 +20,7 @@ defmodule Pinchflat.Tasks.Task do
@doc false @doc false
def changeset(task, attrs) do def changeset(task, attrs) do
task task
|> cast(attrs, [:job_id, :channel_id, :media_item_id]) |> cast(attrs, [:job_id, :source_id, :media_item_id])
|> validate_required([:job_id]) |> validate_required([:job_id])
end end
end end

View file

@ -38,7 +38,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
Returns :ok | {:ok, %Task{}} Returns :ok | {:ok, %Task{}}
""" """
def perform(%Oban.Job{args: %{"id" => channel_id}}) do def perform(%Oban.Job{args: %{"id" => channel_id}}) do
channel = MediaSource.get_channel!(channel_id) channel = MediaSource.get_source!(channel_id)
if channel.index_frequency_minutes <= 0 do if channel.index_frequency_minutes <= 0 do
:ok :ok

View file

@ -6,19 +6,19 @@ defmodule PinchflatWeb.MediaSources.ChannelController do
alias Pinchflat.MediaSource.Channel alias Pinchflat.MediaSource.Channel
def index(conn, _params) do def index(conn, _params) do
channels = MediaSource.list_channels() channels = MediaSource.list_sources()
render(conn, :index, channels: channels) render(conn, :index, channels: channels)
end end
def new(conn, _params) do def new(conn, _params) do
changeset = MediaSource.change_channel(%Channel{}) changeset = MediaSource.change_source(%Channel{})
render(conn, :new, changeset: changeset, media_profiles: media_profiles()) render(conn, :new, changeset: changeset, media_profiles: media_profiles())
end end
def create(conn, %{"channel" => channel_params}) do def create(conn, %{"channel" => channel_params}) do
case MediaSource.create_channel(channel_params) do case MediaSource.create_source(channel_params) do
{:ok, channel} -> {:ok, channel} ->
conn conn
|> put_flash(:info, "Channel created successfully.") |> put_flash(:info, "Channel created successfully.")
@ -30,22 +30,22 @@ defmodule PinchflatWeb.MediaSources.ChannelController do
end end
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
channel = MediaSource.get_channel!(id) channel = MediaSource.get_source!(id)
render(conn, :show, channel: channel) render(conn, :show, channel: channel)
end end
def edit(conn, %{"id" => id}) do def edit(conn, %{"id" => id}) do
channel = MediaSource.get_channel!(id) channel = MediaSource.get_source!(id)
changeset = MediaSource.change_channel(channel) changeset = MediaSource.change_source(channel)
render(conn, :edit, channel: channel, changeset: changeset, media_profiles: media_profiles()) render(conn, :edit, channel: channel, changeset: changeset, media_profiles: media_profiles())
end end
def update(conn, %{"id" => id, "channel" => channel_params}) do def update(conn, %{"id" => id, "channel" => channel_params}) do
channel = MediaSource.get_channel!(id) channel = MediaSource.get_source!(id)
case MediaSource.update_channel(channel, channel_params) do case MediaSource.update_source(channel, channel_params) do
{:ok, channel} -> {:ok, channel} ->
conn conn
|> put_flash(:info, "Channel updated successfully.") |> put_flash(:info, "Channel updated successfully.")
@ -61,8 +61,8 @@ defmodule PinchflatWeb.MediaSources.ChannelController do
end end
def delete(conn, %{"id" => id}) do def delete(conn, %{"id" => id}) do
channel = MediaSource.get_channel!(id) channel = MediaSource.get_source!(id)
{:ok, _channel} = MediaSource.delete_channel(channel) {:ok, _channel} = MediaSource.delete_source(channel)
conn conn
|> put_flash(:info, "Channel deleted successfully.") |> put_flash(:info, "Channel deleted successfully.")

View file

@ -9,6 +9,7 @@ defmodule Pinchflat.Repo.Migrations.RenameChannelAndRelatedFields do
add :collection_id, :string, null: false add :collection_id, :string, null: false
add :original_url, :string, null: false add :original_url, :string, null: false
add :media_profile_id, references(:media_profiles, on_delete: :restrict), null: false add :media_profile_id, references(:media_profiles, on_delete: :restrict), null: false
add :index_frequency_minutes, :integer, default: 60 * 24, null: false
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)
end end

View file

@ -9,13 +9,13 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.ChannelTest do
setup :verify_on_exit! setup :verify_on_exit!
describe "get_channel_details/1" do describe "get_source_details/1" do
test "it returns a %ChannelDetails{} with data on success" do test "it returns a %ChannelDetails{} with data on success" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"} {:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end) end)
assert {:ok, res} = Channel.get_channel_details(@channel_url) assert {:ok, res} = Channel.get_source_details(@channel_url)
assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} = res assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} = res
end end
@ -27,19 +27,19 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.ChannelTest do
{:ok, "{}"} {:ok, "{}"}
end) end)
assert {:ok, _} = Channel.get_channel_details(@channel_url) assert {:ok, _} = Channel.get_source_details(@channel_url)
end end
test "it returns an error if the runner returns an error" do test "it returns an error if the runner returns an error" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
assert {:error, "Big issue", 1} = Channel.get_channel_details(@channel_url) assert {:error, "Big issue", 1} = Channel.get_source_details(@channel_url)
end end
test "it returns an error if the output is not JSON" do test "it returns an error if the output is not JSON" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end)
assert {:error, %Jason.DecodeError{}} = Channel.get_channel_details(@channel_url) assert {:error, %Jason.DecodeError{}} = Channel.get_source_details(@channel_url)
end end
end end
end end

View file

@ -15,7 +15,7 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
end end
end end
describe "get_channel_details/2" do describe "get_source_details/2" do
test "it passes the expected arguments to the backend" do test "it passes the expected arguments to the backend" do
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot -> expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
assert opts == [:skip_download, playlist_end: 1] assert opts == [:skip_download, playlist_end: 1]
@ -24,7 +24,7 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"} {:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end) end)
assert {:ok, _} = ChannelDetails.get_channel_details(@channel_url) assert {:ok, _} = ChannelDetails.get_source_details(@channel_url)
end end
test "it returns a struct composed of the returned data" do test "it returns a struct composed of the returned data" do
@ -32,7 +32,7 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"} {:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end) end)
assert {:ok, res} = ChannelDetails.get_channel_details(@channel_url) assert {:ok, res} = ChannelDetails.get_source_details(@channel_url)
assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} = res assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} = res
end end
end end

View file

@ -10,79 +10,82 @@ defmodule Pinchflat.MediaSourceTest do
alias Pinchflat.MediaSource.Channel alias Pinchflat.MediaSource.Channel
alias Pinchflat.Workers.MediaIndexingWorker alias Pinchflat.Workers.MediaIndexingWorker
@invalid_channel_attrs %{name: nil, channel_id: nil} @invalid_source_attrs %{name: nil, collection_id: nil}
setup :verify_on_exit! setup :verify_on_exit!
describe "list_channels/0" do describe "list_sources/0" do
test "it returns all channels" do test "it returns all sources" do
channel = channel_fixture() source = source_fixture()
assert MediaSource.list_channels() == [channel] assert MediaSource.list_sources() == [source]
end end
end end
describe "get_channel!/1" do describe "get_source!/1" do
test "it returns the channel with given id" do test "it returns the source with given id" do
channel = channel_fixture() source = source_fixture()
assert MediaSource.get_channel!(channel.id) == channel assert MediaSource.get_source!(source.id) == source
end end
end end
describe "create_channel/1" do describe "create_source/1" do
test "creates a channel and adds name + ID from runner response" do test "creates a source and adds name + ID from runner response" do
expect(YtDlpRunnerMock, :run, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, &runner_function_mock/3)
valid_attrs = %{ valid_attrs = %{
media_profile_id: media_profile_fixture().id, media_profile_id: media_profile_fixture().id,
original_url: "https://www.youtube.com/channel/abc123" original_url: "https://www.youtube.com/channel/abc123",
collection_type: "channel"
} }
assert {:ok, %Channel{} = channel} = MediaSource.create_channel(valid_attrs) assert {:ok, %Channel{} = source} = MediaSource.create_source(valid_attrs)
assert channel.name == "some name" assert source.name == "some name"
assert String.starts_with?(channel.channel_id, "some_channel_id_") assert String.starts_with?(source.collection_id, "some_source_id_")
end end
test "creation with invalid data returns error changeset" do test "creation with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = MediaSource.create_channel(@invalid_channel_attrs) assert {:error, %Ecto.Changeset{}} = MediaSource.create_source(@invalid_source_attrs)
end end
test "creation enforces uniqueness of channel_id scoped to the media_profile" do test "creation enforces uniqueness of source_id scoped to the media_profile" do
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot -> expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot ->
{:ok, {:ok,
Phoenix.json_library().encode!(%{ Phoenix.json_library().encode!(%{
channel: "some name", channel: "some name",
channel_id: "some_channel_id_12345678" channel_id: "some_source_id_12345678"
})} })}
end) end)
valid_once_attrs = %{ valid_once_attrs = %{
media_profile_id: media_profile_fixture().id, media_profile_id: media_profile_fixture().id,
original_url: "https://www.youtube.com/channel/abc123" original_url: "https://www.youtube.com/channel/abc123",
collection_type: "channel"
} }
assert {:ok, %Channel{}} = MediaSource.create_channel(valid_once_attrs) assert {:ok, %Channel{}} = MediaSource.create_source(valid_once_attrs)
assert {:error, %Ecto.Changeset{}} = MediaSource.create_channel(valid_once_attrs) assert {:error, %Ecto.Changeset{}} = MediaSource.create_source(valid_once_attrs)
end end
test "creation lets you duplicate channel_ids as long as the media profile is different" do test "creation lets you duplicate collection_ids as long as the media profile is different" do
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot -> expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot ->
{:ok, {:ok,
Phoenix.json_library().encode!(%{ Phoenix.json_library().encode!(%{
channel: "some name", channel: "some name",
channel_id: "some_channel_id_12345678" channel_id: "some_source_id_12345678"
})} })}
end) end)
valid_attrs = %{ valid_attrs = %{
name: "some name", name: "some name",
original_url: "https://www.youtube.com/channel/abc123" original_url: "https://www.youtube.com/channel/abc123",
collection_type: "channel"
} }
channel_1_attrs = Map.merge(valid_attrs, %{media_profile_id: media_profile_fixture().id}) source_1_attrs = Map.merge(valid_attrs, %{media_profile_id: media_profile_fixture().id})
channel_2_attrs = Map.merge(valid_attrs, %{media_profile_id: media_profile_fixture().id}) source_2_attrs = Map.merge(valid_attrs, %{media_profile_id: media_profile_fixture().id})
assert {:ok, %Channel{}} = MediaSource.create_channel(channel_1_attrs) assert {:ok, %Channel{}} = MediaSource.create_source(source_1_attrs)
assert {:ok, %Channel{}} = MediaSource.create_channel(channel_2_attrs) assert {:ok, %Channel{}} = MediaSource.create_source(source_2_attrs)
end end
test "creation will schedule the indexing task" do test "creation will schedule the indexing task" do
@ -90,12 +93,13 @@ defmodule Pinchflat.MediaSourceTest do
valid_attrs = %{ valid_attrs = %{
media_profile_id: media_profile_fixture().id, media_profile_id: media_profile_fixture().id,
original_url: "https://www.youtube.com/channel/abc123" original_url: "https://www.youtube.com/channel/abc123",
collection_type: "channel"
} }
assert {:ok, %Channel{} = channel} = MediaSource.create_channel(valid_attrs) assert {:ok, %Channel{} = source} = MediaSource.create_source(valid_attrs)
assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id}) assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end end
end end
@ -103,181 +107,181 @@ defmodule Pinchflat.MediaSourceTest do
setup do setup do
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo2\nvideo3"} end) stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo2\nvideo3"} end)
{:ok, [channel: channel_fixture()]} {:ok, [source: source_fixture()]}
end end
test "it creates a media_item record for each media ID returned", %{channel: channel} do test "it creates a media_item record for each media ID returned", %{source: source} do
assert media_items = MediaSource.index_media_items(channel) assert media_items = MediaSource.index_media_items(source)
assert Enum.count(media_items) == 3 assert Enum.count(media_items) == 3
assert ["video1", "video2", "video3"] == Enum.map(media_items, & &1.media_id) assert ["video1", "video2", "video3"] == Enum.map(media_items, & &1.media_id)
assert Enum.all?(media_items, fn %MediaItem{} -> true end) assert Enum.all?(media_items, fn %MediaItem{} -> true end)
end end
test "it attaches all media_items to the given channel", %{channel: channel} do test "it attaches all media_items to the given source", %{source: source} do
channel_id = channel.id source_id = source.id
assert media_items = MediaSource.index_media_items(channel) assert media_items = MediaSource.index_media_items(source)
assert Enum.count(media_items) == 3 assert Enum.count(media_items) == 3
assert Enum.all?(media_items, fn %MediaItem{channel_id: ^channel_id} -> true end) assert Enum.all?(media_items, fn %MediaItem{source_id: ^source_id} -> true end)
end end
test "it won't duplicate media_items based on media_id and channel", %{channel: channel} do test "it won't duplicate media_items based on media_id and source", %{source: source} do
_first_run = MediaSource.index_media_items(channel) _first_run = MediaSource.index_media_items(source)
_duplicate_run = MediaSource.index_media_items(channel) _duplicate_run = MediaSource.index_media_items(source)
media_items = Repo.preload(channel, :media_items).media_items media_items = Repo.preload(source, :media_items).media_items
assert Enum.count(media_items) == 3 assert Enum.count(media_items) == 3
end end
test "it can duplicate media_ids for different channels", %{channel: channel} do test "it can duplicate media_ids for different sources", %{source: source} do
other_channel = channel_fixture() other_source = source_fixture()
media_items = MediaSource.index_media_items(channel) media_items = MediaSource.index_media_items(source)
media_items_other_channel = MediaSource.index_media_items(other_channel) media_items_other_source = MediaSource.index_media_items(other_source)
assert Enum.count(media_items) == 3 assert Enum.count(media_items) == 3
assert Enum.count(media_items_other_channel) == 3 assert Enum.count(media_items_other_source) == 3
assert Enum.map(media_items, & &1.media_id) == assert Enum.map(media_items, & &1.media_id) ==
Enum.map(media_items_other_channel, & &1.media_id) Enum.map(media_items_other_source, & &1.media_id)
end end
test "it returns a list of media_items or changesets", %{channel: channel} do test "it returns a list of media_items or changesets", %{source: source} do
first_run = MediaSource.index_media_items(channel) first_run = MediaSource.index_media_items(source)
duplicate_run = MediaSource.index_media_items(channel) duplicate_run = MediaSource.index_media_items(source)
assert Enum.all?(first_run, fn %MediaItem{} -> true end) assert Enum.all?(first_run, fn %MediaItem{} -> true end)
assert Enum.all?(duplicate_run, fn %Ecto.Changeset{} -> true end) assert Enum.all?(duplicate_run, fn %Ecto.Changeset{} -> true end)
end end
end end
describe "update_channel/2" do describe "update_source/2" do
test "updates with valid data updates the channel" do test "updates with valid data updates the source" do
channel = channel_fixture() source = source_fixture()
update_attrs = %{name: "some updated name"} update_attrs = %{name: "some updated name"}
assert {:ok, %Channel{} = channel} = MediaSource.update_channel(channel, update_attrs) assert {:ok, %Channel{} = source} = MediaSource.update_source(source, update_attrs)
assert channel.name == "some updated name" assert source.name == "some updated name"
end end
test "updating the original_url will re-fetch the channel details" do test "updating the original_url will re-fetch the source details" do
expect(YtDlpRunnerMock, :run, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, &runner_function_mock/3)
channel = channel_fixture() source = source_fixture()
update_attrs = %{original_url: "https://www.youtube.com/channel/abc123"} update_attrs = %{original_url: "https://www.youtube.com/channel/abc123"}
assert {:ok, %Channel{} = channel} = MediaSource.update_channel(channel, update_attrs) assert {:ok, %Channel{} = source} = MediaSource.update_source(source, update_attrs)
assert channel.name == "some name" assert source.name == "some name"
assert String.starts_with?(channel.channel_id, "some_channel_id_") assert String.starts_with?(source.collection_id, "some_source_id_")
end end
test "not updating the original_url will not re-fetch the channel details" do test "not updating the original_url will not re-fetch the source details" do
expect(YtDlpRunnerMock, :run, 0, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, 0, &runner_function_mock/3)
channel = channel_fixture() source = source_fixture()
update_attrs = %{name: "some updated name"} update_attrs = %{name: "some updated name"}
assert {:ok, %Channel{}} = MediaSource.update_channel(channel, update_attrs) assert {:ok, %Channel{}} = MediaSource.update_source(source, update_attrs)
end end
test "updating the index frequency will re-schedule the indexing task" do test "updating the index frequency will re-schedule the indexing task" do
channel = channel_fixture() source = source_fixture()
update_attrs = %{index_frequency_minutes: 123} update_attrs = %{index_frequency_minutes: 123}
assert {:ok, %Channel{} = channel} = MediaSource.update_channel(channel, update_attrs) assert {:ok, %Channel{} = source} = MediaSource.update_source(source, update_attrs)
assert channel.index_frequency_minutes == 123 assert source.index_frequency_minutes == 123
assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id}) assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end end
test "not updating the index frequency will not re-schedule the indexing task" do test "not updating the index frequency will not re-schedule the indexing task" do
channel = channel_fixture() source = source_fixture()
update_attrs = %{name: "some updated name"} update_attrs = %{name: "some updated name"}
assert {:ok, %Channel{}} = MediaSource.update_channel(channel, update_attrs) assert {:ok, %Channel{}} = MediaSource.update_source(source, update_attrs)
refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id}) refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end end
test "updates with invalid data returns error changeset" do test "updates with invalid data returns error changeset" do
channel = channel_fixture() source = source_fixture()
assert {:error, %Ecto.Changeset{}} = assert {:error, %Ecto.Changeset{}} =
MediaSource.update_channel(channel, @invalid_channel_attrs) MediaSource.update_source(source, @invalid_source_attrs)
assert channel == MediaSource.get_channel!(channel.id) assert source == MediaSource.get_source!(source.id)
end end
end end
describe "delete_channel/1" do describe "delete_source/1" do
test "it deletes the channel" do test "it deletes the source" do
channel = channel_fixture() source = source_fixture()
assert {:ok, %Channel{}} = MediaSource.delete_channel(channel) assert {:ok, %Channel{}} = MediaSource.delete_source(source)
assert_raise Ecto.NoResultsError, fn -> MediaSource.get_channel!(channel.id) end assert_raise Ecto.NoResultsError, fn -> MediaSource.get_source!(source.id) end
end end
test "it returns a channel changeset" do test "it returns a source changeset" do
channel = channel_fixture() source = source_fixture()
assert %Ecto.Changeset{} = MediaSource.change_channel(channel) assert %Ecto.Changeset{} = MediaSource.change_source(source)
end end
test "deletion also deletes all associated tasks" do test "deletion also deletes all associated tasks" do
channel = channel_fixture() source = source_fixture()
task = task_fixture(channel_id: channel.id) task = task_fixture(source_id: source.id)
assert {:ok, %Channel{}} = MediaSource.delete_channel(channel) assert {:ok, %Channel{}} = MediaSource.delete_source(source)
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
end end
end end
describe "change_channel/2" do describe "change_source/2" do
test "it returns a changeset" do test "it returns a changeset" do
channel = channel_fixture() source = source_fixture()
assert %Ecto.Changeset{} = MediaSource.change_channel(channel) assert %Ecto.Changeset{} = MediaSource.change_source(source)
end end
end end
describe "change_channel_from_url/2" do describe "change_source_from_url/2" do
test "it returns a changeset" do test "it returns a changeset" do
stub(YtDlpRunnerMock, :run, &runner_function_mock/3) stub(YtDlpRunnerMock, :run, &runner_function_mock/3)
channel = channel_fixture() source = source_fixture()
assert %Ecto.Changeset{} = MediaSource.change_channel_from_url(channel, %{}) assert %Ecto.Changeset{} = MediaSource.change_source_from_url(source, %{})
end end
test "it does not fetch channel details if the original_url isn't in the changeset" do test "it does not fetch source details if the original_url isn't in the changeset" do
expect(YtDlpRunnerMock, :run, 0, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, 0, &runner_function_mock/3)
changeset = MediaSource.change_channel_from_url(%Channel{}, %{name: "some updated name"}) changeset = MediaSource.change_source_from_url(%Channel{}, %{name: "some updated name"})
assert %Ecto.Changeset{} = changeset assert %Ecto.Changeset{} = changeset
end end
test "it fetches channel details if the original_url is in the changeset" do test "it fetches source details if the original_url is in the changeset" do
expect(YtDlpRunnerMock, :run, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, &runner_function_mock/3)
changeset = changeset =
MediaSource.change_channel_from_url(%Channel{}, %{ MediaSource.change_source_from_url(%Channel{}, %{
original_url: "https://www.youtube.com/channel/abc123" original_url: "https://www.youtube.com/channel/abc123"
}) })
assert %Ecto.Changeset{} = changeset assert %Ecto.Changeset{} = changeset
end end
test "it adds channel details to the changeset, keeping the orignal details" do test "it adds source details to the changeset, keeping the orignal details" do
expect(YtDlpRunnerMock, :run, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, &runner_function_mock/3)
media_profile = media_profile_fixture() media_profile = media_profile_fixture()
media_profile_id = media_profile.id media_profile_id = media_profile.id
changeset = changeset =
MediaSource.change_channel_from_url(%Channel{}, %{ MediaSource.change_source_from_url(%Channel{}, %{
original_url: "https://www.youtube.com/channel/abc123", original_url: "https://www.youtube.com/channel/abc123",
media_profile_id: media_profile.id media_profile_id: media_profile.id
}) })
assert %Ecto.Changeset{} = changeset assert %Ecto.Changeset{} = changeset
assert String.starts_with?(changeset.changes.channel_id, "some_channel_id_") assert String.starts_with?(changeset.changes.collection_id, "some_source_id_")
assert %{ assert %{
name: "some name", name: "some name",
@ -292,7 +296,7 @@ defmodule Pinchflat.MediaSourceTest do
end) end)
changeset = changeset =
MediaSource.change_channel_from_url(%Channel{}, %{ MediaSource.change_source_from_url(%Channel{}, %{
original_url: "https://www.youtube.com/channel/abc123" original_url: "https://www.youtube.com/channel/abc123"
}) })
@ -306,7 +310,7 @@ defmodule Pinchflat.MediaSourceTest do
:ok, :ok,
Phoenix.json_library().encode!(%{ Phoenix.json_library().encode!(%{
channel: "some name", channel: "some name",
channel_id: "some_channel_id_#{:rand.uniform(1_000_000)}" channel_id: "some_source_id_#{:rand.uniform(1_000_000)}"
}) })
} }
end end

View file

@ -31,14 +31,14 @@ defmodule Pinchflat.MediaTest do
describe "list_pending_media_items_for/1" do describe "list_pending_media_items_for/1" do
test "it returns pending media_items for a given channel" do test "it returns pending media_items for a given channel" do
channel = channel_fixture() source = source_fixture()
media_item = media_item_fixture(%{channel_id: channel.id, media_filepath: nil}) media_item = media_item_fixture(%{channel_id: channel.id, media_filepath: nil})
assert Media.list_pending_media_items_for(channel) == [media_item] assert Media.list_pending_media_items_for(channel) == [media_item]
end end
test "it does not return media_items with media_filepath" do test "it does not return media_items with media_filepath" do
channel = channel_fixture() source = source_fixture()
_media_item = _media_item =
media_item_fixture(%{ media_item_fixture(%{
@ -63,7 +63,7 @@ defmodule Pinchflat.MediaTest do
media_id: Faker.String.base64(12), media_id: Faker.String.base64(12),
title: Faker.Commerce.product_name(), title: Faker.Commerce.product_name(),
media_filepath: "/video/#{Faker.File.file_name(:video)}", media_filepath: "/video/#{Faker.File.file_name(:video)}",
channel_id: channel_fixture().id source_id: source_fixture().id
} }
assert {:ok, %MediaItem{} = media_item} = Media.create_media_item(valid_attrs) assert {:ok, %MediaItem{} = media_item} = Media.create_media_item(valid_attrs)
@ -85,7 +85,7 @@ defmodule Pinchflat.MediaTest do
media_id: Faker.String.base64(12), media_id: Faker.String.base64(12),
title: Faker.Commerce.product_name(), title: Faker.Commerce.product_name(),
media_filepath: "/video/#{Faker.File.file_name(:video)}", media_filepath: "/video/#{Faker.File.file_name(:video)}",
channel_id: channel_fixture().id source_id: source_fixture().id
} }
assert {:ok, %MediaItem{} = media_item} = Media.update_media_item(media_item, update_attrs) assert {:ok, %MediaItem{} = media_item} = Media.update_media_item(media_item, update_attrs)

View file

@ -10,7 +10,7 @@ defmodule Pinchflat.Tasks.ChannelTasksTest do
describe "kickoff_indexing_task/1" do describe "kickoff_indexing_task/1" do
test "it does not schedule a job if the interval is <= 0" do test "it does not schedule a job if the interval is <= 0" do
channel = channel_fixture(index_frequency_minutes: -1) source = source_fixture(index_frequency_minutes: -1)
assert {:ok, :should_not_index} = ChannelTasks.kickoff_indexing_task(channel) assert {:ok, :should_not_index} = ChannelTasks.kickoff_indexing_task(channel)
@ -18,7 +18,7 @@ defmodule Pinchflat.Tasks.ChannelTasksTest do
end end
test "it schedules a job if the interval is > 0" do test "it schedules a job if the interval is > 0" do
channel = channel_fixture(index_frequency_minutes: 1) source = source_fixture(index_frequency_minutes: 1)
assert {:ok, _} = ChannelTasks.kickoff_indexing_task(channel) assert {:ok, _} = ChannelTasks.kickoff_indexing_task(channel)
@ -26,7 +26,7 @@ defmodule Pinchflat.Tasks.ChannelTasksTest do
end end
test "it creates and attaches a task if the interval is > 0" do test "it creates and attaches a task if the interval is > 0" do
channel = channel_fixture(index_frequency_minutes: 1) source = source_fixture(index_frequency_minutes: 1)
assert {:ok, %Task{} = task} = ChannelTasks.kickoff_indexing_task(channel) assert {:ok, %Task{} = task} = ChannelTasks.kickoff_indexing_task(channel)
@ -34,7 +34,7 @@ defmodule Pinchflat.Tasks.ChannelTasksTest do
end end
test "it deletes any pending tasks for the channel" do test "it deletes any pending tasks for the channel" do
channel = channel_fixture() source = source_fixture()
task = task_fixture(channel_id: channel.id) task = task_fixture(channel_id: channel.id)
assert {:ok, _} = ChannelTasks.kickoff_indexing_task(channel) assert {:ok, _} = ChannelTasks.kickoff_indexing_task(channel)

View file

@ -86,7 +86,7 @@ defmodule Pinchflat.TasksTest do
test "accepts a job and channel" do test "accepts a job and channel" do
job = job_fixture() job = job_fixture()
channel = channel_fixture() source = source_fixture()
assert {:ok, %Task{} = task} = Tasks.create_task(job, channel) assert {:ok, %Task{} = task} = Tasks.create_task(job, channel)
@ -115,7 +115,7 @@ defmodule Pinchflat.TasksTest do
end end
test "it creates a task record if successful" do test "it creates a task record if successful" do
channel = channel_fixture() source = source_fixture()
assert {:ok, %Task{} = task} = Tasks.create_job_with_task(TestJobWorker.new(%{}), channel) assert {:ok, %Task{} = task} = Tasks.create_job_with_task(TestJobWorker.new(%{}), channel)
@ -123,7 +123,7 @@ defmodule Pinchflat.TasksTest do
end end
test "it returns an error if the job already exists" do test "it returns an error if the job already exists" do
channel = channel_fixture() source = source_fixture()
job = TestJobWorker.new(%{foo: "bar"}, unique: [period: :infinity]) job = TestJobWorker.new(%{foo: "bar"}, unique: [period: :infinity])
assert {:ok, %Task{}} = Tasks.create_job_with_task(job, channel) assert {:ok, %Task{}} = Tasks.create_job_with_task(job, channel)
@ -131,7 +131,7 @@ defmodule Pinchflat.TasksTest do
end end
test "it returns an error if the job fails to enqueue" do test "it returns an error if the job fails to enqueue" do
channel = channel_fixture() source = source_fixture()
assert {:error, %Ecto.Changeset{}} = Tasks.create_job_with_task(%Ecto.Changeset{}, channel) assert {:error, %Ecto.Changeset{}} = Tasks.create_job_with_task(%Ecto.Changeset{}, channel)
end end
@ -156,7 +156,7 @@ defmodule Pinchflat.TasksTest do
describe "delete_tasks_for/1" do describe "delete_tasks_for/1" do
test "it deletes tasks attached to a channel" do test "it deletes tasks attached to a channel" do
channel = channel_fixture() source = source_fixture()
task = task_fixture(channel_id: channel.id) task = task_fixture(channel_id: channel.id)
assert :ok = Tasks.delete_tasks_for(channel) assert :ok = Tasks.delete_tasks_for(channel)
@ -174,7 +174,7 @@ defmodule Pinchflat.TasksTest do
describe "delete_pending_tasks_for/1" do describe "delete_pending_tasks_for/1" do
test "it deletes pending tasks attached to a channel" do test "it deletes pending tasks attached to a channel" do
channel = channel_fixture() source = source_fixture()
task = task_fixture(channel_id: channel.id) task = task_fixture(channel_id: channel.id)
assert :ok = Tasks.delete_pending_tasks_for(channel) assert :ok = Tasks.delete_pending_tasks_for(channel)
@ -182,7 +182,7 @@ defmodule Pinchflat.TasksTest do
end end
test "it does not delete non-pending tasks" do test "it does not delete non-pending tasks" do
channel = channel_fixture() source = source_fixture()
task = Repo.preload(task_fixture(channel_id: channel.id), :job) task = Repo.preload(task_fixture(channel_id: channel.id), :job)
:ok = Oban.cancel_job(task.job) :ok = Oban.cancel_job(task.job)

View file

@ -15,7 +15,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it does not do any indexing if the channel shouldn't be indexed" do test "it does not do any indexing if the channel shouldn't be indexed" do
expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: -1) source = source_fixture(index_frequency_minutes: -1)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
end end
@ -23,7 +23,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it does not reschedule if the channel shouldn't be indexed" do test "it does not reschedule if the channel shouldn't be indexed" do
expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: -1) source = source_fixture(index_frequency_minutes: -1)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id}) refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
@ -32,7 +32,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it indexes the channel if it should be indexed" do test "it indexes the channel if it should be indexed" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: 10) source = source_fixture(index_frequency_minutes: 10)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
end end
@ -40,7 +40,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it kicks off a download job for each pending media item" do test "it kicks off a download job for each pending media item" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1"} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1"} end)
channel = channel_fixture(index_frequency_minutes: 10) source = source_fixture(index_frequency_minutes: 10)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
assert [_] = all_enqueued(worker: VideoDownloadWorker) assert [_] = all_enqueued(worker: VideoDownloadWorker)
@ -49,7 +49,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it starts a job for any pending media item even if it's from another run" do test "it starts a job for any pending media item even if it's from another run" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1"} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1"} end)
channel = channel_fixture(index_frequency_minutes: 10) source = source_fixture(index_frequency_minutes: 10)
media_item_fixture(%{channel_id: channel.id, media_filepath: nil}) media_item_fixture(%{channel_id: channel.id, media_filepath: nil})
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
@ -59,7 +59,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it does not kick off a job for media items that could not be saved" do test "it does not kick off a job for media items that could not be saved" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo1"} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo1"} end)
channel = channel_fixture(index_frequency_minutes: 10) source = source_fixture(index_frequency_minutes: 10)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
# Only one job should be enqueued, since the second video is a duplicate # Only one job should be enqueued, since the second video is a duplicate
@ -69,7 +69,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it reschedules the job based on the index frequency" do test "it reschedules the job based on the index frequency" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: 10) source = source_fixture(index_frequency_minutes: 10)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
assert_enqueued( assert_enqueued(
@ -82,7 +82,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it creates a task for the rescheduled job" do test "it creates a task for the rescheduled job" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: 10) 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()) end
assert_changed([from: 0, to: 1], task_count_fetcher, fn -> assert_changed([from: 0, to: 1], task_count_fetcher, fn ->
@ -93,7 +93,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it creates the basic media_item records" do test "it creates the basic media_item records" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo2"} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo2"} end)
channel = channel_fixture(index_frequency_minutes: 10) source = source_fixture(index_frequency_minutes: 10)
media_item_fetcher = fn -> media_item_fetcher = fn ->
channel channel

View file

@ -58,7 +58,7 @@ defmodule PinchflatWeb.ChannelControllerTest do
end end
describe "edit channel" do describe "edit channel" do
setup [:create_channel] setup [:create_source]
test "renders form for editing chosen channel", %{conn: conn, channel: channel} do test "renders form for editing chosen channel", %{conn: conn, channel: channel} do
conn = get(conn, ~p"/media_sources/channels/#{channel}/edit") conn = get(conn, ~p"/media_sources/channels/#{channel}/edit")
@ -67,7 +67,7 @@ defmodule PinchflatWeb.ChannelControllerTest do
end end
describe "update channel" do describe "update channel" do
setup [:create_channel] setup [:create_source]
test "redirects when data is valid", %{conn: conn, channel: channel, update_attrs: update_attrs} do test "redirects when data is valid", %{conn: conn, channel: channel, update_attrs: update_attrs} do
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3) expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
@ -90,7 +90,7 @@ defmodule PinchflatWeb.ChannelControllerTest do
end end
describe "delete channel" do describe "delete channel" do
setup [:create_channel] setup [:create_source]
test "deletes chosen channel", %{conn: conn, channel: channel} do test "deletes chosen channel", %{conn: conn, channel: channel} do
conn = delete(conn, ~p"/media_sources/channels/#{channel}") conn = delete(conn, ~p"/media_sources/channels/#{channel}")
@ -102,8 +102,8 @@ defmodule PinchflatWeb.ChannelControllerTest do
end end
end end
defp create_channel(_) do defp create_source(_) do
channel = channel_fixture() source = source_fixture()
%{channel: channel} %{channel: channel}
end end

View file

@ -16,7 +16,7 @@ defmodule Pinchflat.MediaFixtures do
media_id: Faker.String.base64(12), media_id: Faker.String.base64(12),
title: Faker.Commerce.product_name(), title: Faker.Commerce.product_name(),
media_filepath: "/video/#{Faker.File.file_name(:video)}", media_filepath: "/video/#{Faker.File.file_name(:video)}",
channel_id: MediaSourceFixtures.channel_fixture().id channel_id: MediaSourceFixtures.source_fixture().id
}) })
|> Pinchflat.Media.create_media_item() |> Pinchflat.Media.create_media_item()

View file

@ -9,15 +9,16 @@ defmodule Pinchflat.MediaSourceFixtures do
alias Pinchflat.MediaSource.Channel alias Pinchflat.MediaSource.Channel
@doc """ @doc """
Generate a channel. Generate a source.
""" """
def channel_fixture(attrs \\ %{}) do def source_fixture(attrs \\ %{}) do
{:ok, channel} = {:ok, channel} =
%Channel{} %Channel{}
|> Channel.changeset( |> Channel.changeset(
Enum.into(attrs, %{ Enum.into(attrs, %{
name: "Channel ##{:rand.uniform(1_000_000)}", name: "Channel ##{:rand.uniform(1_000_000)}",
channel_id: Base.encode16(:crypto.hash(:md5, "#{:rand.uniform(1_000_000)}")), collection_id: Base.encode16(:crypto.hash(:md5, "#{:rand.uniform(1_000_000)}")),
collection_type: "channel",
original_url: "https://www.youtube.com/channel/#{Faker.String.base64(12)}", original_url: "https://www.youtube.com/channel/#{Faker.String.base64(12)}",
media_profile_id: ProfilesFixtures.media_profile_fixture().id, media_profile_id: ProfilesFixtures.media_profile_fixture().id,
index_frequency_minutes: 60 index_frequency_minutes: 60

View file

@ -14,7 +14,7 @@ defmodule Pinchflat.TasksFixtures do
{:ok, task} = {:ok, task} =
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
channel_id: MediaSourceFixtures.channel_fixture().id, source_id: MediaSourceFixtures.source_fixture().id,
job_id: JobFixtures.job_fixture().id job_id: JobFixtures.job_fixture().id
}) })
|> Pinchflat.Tasks.create_task() |> Pinchflat.Tasks.create_task()