diff --git a/.iex.exs b/.iex.exs
index bc26864..d23dd0c 100644
--- a/.iex.exs
+++ b/.iex.exs
@@ -3,7 +3,7 @@ alias Pinchflat.Repo
alias Pinchflat.Tasks.Task
alias Pinchflat.Media.MediaItem
alias Pinchflat.Media.MediaMetadata
-alias Pinchflat.MediaSource.Channel
+alias Pinchflat.MediaSource.Source
alias Pinchflat.Profiles.MediaProfile
alias Pinchflat.Tasks
@@ -11,4 +11,4 @@ alias Pinchflat.Media
alias Pinchflat.Profiles
alias Pinchflat.MediaSource
-alias Pinchflat.MediaClient.{ChannelDetails, VideoDownloader}
+alias Pinchflat.MediaClient.{SourceDetails, VideoDownloader}
diff --git a/lib/pinchflat/media.ex b/lib/pinchflat/media.ex
index c9b0371..fe864ae 100644
--- a/lib/pinchflat/media.ex
+++ b/lib/pinchflat/media.ex
@@ -8,7 +8,7 @@ defmodule Pinchflat.Media do
alias Pinchflat.Repo
alias Pinchflat.Tasks
alias Pinchflat.Media.MediaItem
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
@doc """
Returns the list of media_items. Returns [%MediaItem{}, ...].
@@ -18,15 +18,15 @@ defmodule Pinchflat.Media do
end
@doc """
- Returns a list of pending media_items for a given channel, where
+ Returns a list of pending media_items for a given source, where
pending means the `media_filepath` is `nil`.
Returns [%MediaItem{}, ...].
"""
- def list_pending_media_items_for(%Channel{} = channel) do
+ def list_pending_media_items_for(%Source{} = source) do
from(
m in MediaItem,
- where: m.channel_id == ^channel.id and is_nil(m.media_filepath)
+ where: m.source_id == ^source.id and is_nil(m.media_filepath)
)
|> Repo.all()
end
diff --git a/lib/pinchflat/media/media_item.ex b/lib/pinchflat/media/media_item.ex
index 9db15d3..430c3e5 100644
--- a/lib/pinchflat/media/media_item.ex
+++ b/lib/pinchflat/media/media_item.ex
@@ -7,11 +7,11 @@ defmodule Pinchflat.Media.MediaItem do
import Ecto.Changeset
alias Pinchflat.Tasks.Task
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
alias Pinchflat.Media.MediaMetadata
- @required_fields ~w(media_id channel_id)a
- @allowed_fields ~w(title media_id media_filepath channel_id subtitle_filepaths)a
+ @required_fields ~w(media_id source_id)a
+ @allowed_fields ~w(title media_id media_filepath source_id subtitle_filepaths)a
schema "media_items" do
field :title, :string
@@ -22,7 +22,7 @@ defmodule Pinchflat.Media.MediaItem do
# Will very likely revisit because I can't leave well-enough alone.
field :subtitle_filepaths, {:array, {:array, :string}}, default: []
- belongs_to :channel, Channel
+ belongs_to :source, Source
has_one :metadata, MediaMetadata, on_replace: :update
@@ -37,6 +37,6 @@ defmodule Pinchflat.Media.MediaItem do
|> cast(attrs, @allowed_fields)
|> cast_assoc(:metadata, with: &MediaMetadata.changeset/2, required: false)
|> validate_required(@required_fields)
- |> unique_constraint([:media_id, :channel_id])
+ |> unique_constraint([:media_id, :source_id])
end
end
diff --git a/lib/pinchflat/media_client/backends/yt_dlp/channel.ex b/lib/pinchflat/media_client/backends/yt_dlp/channel.ex
deleted file mode 100644
index d97f88d..0000000
--- a/lib/pinchflat/media_client/backends/yt_dlp/channel.ex
+++ /dev/null
@@ -1,32 +0,0 @@
-defmodule Pinchflat.MediaClient.Backends.YtDlp.Channel do
- @moduledoc """
- Contains utilities for working with a channel's videos
- """
-
- use Pinchflat.MediaClient.Backends.YtDlp.VideoCollection
- alias Pinchflat.MediaClient.ChannelDetails
-
- @doc """
- Gets a channel's ID and name from its URL.
-
- yt-dlp does not _really_ have channel-specific functions, so
- instead we're fetching just the first video (using playlist_end: 1)
- and parsing the channel ID and name from _its_ metadata
-
- Returns {:ok, %ChannelDetails{}} | {:error, any, ...}.
- """
- def get_channel_details(channel_url) do
- opts = [playlist_end: 1]
-
- with {:ok, output} <- backend_runner().run(channel_url, opts, "%(.{channel,channel_id})j"),
- {:ok, parsed_json} <- Phoenix.json_library().decode(output) do
- {:ok, ChannelDetails.new(parsed_json["channel_id"], parsed_json["channel"])}
- else
- err -> err
- end
- end
-
- defp backend_runner do
- Application.get_env(:pinchflat, :yt_dlp_runner)
- end
-end
diff --git a/lib/pinchflat/media_client/backends/yt_dlp/video_collection.ex b/lib/pinchflat/media_client/backends/yt_dlp/video_collection.ex
index d89c064..624689b 100644
--- a/lib/pinchflat/media_client/backends/yt_dlp/video_collection.ex
+++ b/lib/pinchflat/media_client/backends/yt_dlp/video_collection.ex
@@ -1,28 +1,47 @@
defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollection do
@moduledoc """
- Contains utilities for working with collections of videos (ie: channels, playlists).
-
- Meant to be included in other modules but can be used on its own. Channels and playlists
- will have many of their own methods, but also share a lot of methods. This module is for
- those shared methods.
+ Contains utilities for working with collections of
+ videos (aka: a source [ie: channels, playlists]).
"""
- defmacro __using__(_) do
- quote do
- @doc """
- Returns a list of strings representing the video ids in the collection.
+ alias Pinchflat.MediaClient.SourceDetails
- Returns {:ok, [binary()]} | {:error, any, ...}.
- """
- def get_video_ids(url, command_opts \\ []) do
- runner = Application.get_env(:pinchflat, :yt_dlp_runner)
- opts = command_opts ++ [:simulate, :skip_download]
+ @doc """
+ Returns a list of strings representing the video ids in the collection.
- case runner.run(url, opts, "%(id)s") do
- {:ok, output} -> {:ok, String.split(output, "\n", trim: true)}
- res -> res
- end
- end
+ Returns {:ok, [binary()]} | {:error, any, ...}.
+ """
+ def get_video_ids(url, command_opts \\ []) do
+ runner = Application.get_env(:pinchflat, :yt_dlp_runner)
+ opts = command_opts ++ [:simulate, :skip_download]
+
+ case runner.run(url, opts, "%(id)s") do
+ {:ok, output} -> {:ok, String.split(output, "\n", trim: true)}
+ res -> res
end
end
+
+ @doc """
+ Gets a source's ID and name from its URL.
+
+ yt-dlp does not _really_ have source-specific functions, so
+ instead we're fetching just the first video (using playlist_end: 1)
+ and parsing the source ID and name from _its_ metadata
+
+ Returns {:ok, %SourceDetails{}} | {:error, any, ...}.
+ """
+ def get_source_details(source_url) do
+ opts = [:skip_download, playlist_end: 1]
+
+ with {:ok, output} <- backend_runner().run(source_url, opts, "%(.{channel,channel_id})j"),
+ {:ok, parsed_json} <- Phoenix.json_library().decode(output) do
+ {:ok, SourceDetails.new(parsed_json["channel_id"], parsed_json["channel"])}
+ else
+ err -> err
+ end
+ end
+
+ defp backend_runner do
+ Application.get_env(:pinchflat, :yt_dlp_runner)
+ end
end
diff --git a/lib/pinchflat/media_client/channel_details.ex b/lib/pinchflat/media_client/channel_details.ex
deleted file mode 100644
index 021238b..0000000
--- a/lib/pinchflat/media_client/channel_details.ex
+++ /dev/null
@@ -1,41 +0,0 @@
-defmodule Pinchflat.MediaClient.ChannelDetails do
- @moduledoc """
- This is the integration layer for actually working with channels.
-
- Technically hardcodes the yt-dlp backend for now, but should leave
- it open-ish for future expansion (just in case).
- """
- @enforce_keys [:id, :name]
- defstruct [:id, :name]
-
- alias Pinchflat.MediaClient.Backends.YtDlp.Channel, as: YtDlpChannel
-
- @doc false
- def new(id, name) do
- %__MODULE__{id: id, name: name}
- end
-
- @doc """
- Gets a channel's ID and name from its URL, using the given backend.
-
- Returns {:ok, map()} | {:error, any, ...}.
- """
- def get_channel_details(channel_url, backend \\ :yt_dlp) do
- channel_module(backend).get_channel_details(channel_url)
- end
-
- @doc """
- Returns a list of video IDs for the given channel URL, using the given backend.
-
- Returns {:ok, list(binary())} | {:error, any, ...}.
- """
- def get_video_ids(channel_url, backend \\ :yt_dlp) do
- channel_module(backend).get_video_ids(channel_url)
- end
-
- defp channel_module(backend) do
- case backend do
- :yt_dlp -> YtDlpChannel
- end
- end
-end
diff --git a/lib/pinchflat/media_client/source_details.ex b/lib/pinchflat/media_client/source_details.ex
new file mode 100644
index 0000000..75c7077
--- /dev/null
+++ b/lib/pinchflat/media_client/source_details.ex
@@ -0,0 +1,41 @@
+defmodule Pinchflat.MediaClient.SourceDetails do
+ @moduledoc """
+ This is the integration layer for actually working with sources.
+
+ Technically hardcodes the yt-dlp backend for now, but should leave
+ it open-ish for future expansion (just in case).
+ """
+ @enforce_keys [:id, :name]
+ defstruct [:id, :name]
+
+ alias Pinchflat.MediaClient.Backends.YtDlp.VideoCollection, as: YtDlpSource
+
+ @doc false
+ def new(id, name) do
+ %__MODULE__{id: id, name: name}
+ end
+
+ @doc """
+ Gets a source's ID and name from its URL, using the given backend.
+
+ Returns {:ok, map()} | {:error, any, ...}.
+ """
+ def get_source_details(source_url, backend \\ :yt_dlp) do
+ source_module(backend).get_source_details(source_url)
+ end
+
+ @doc """
+ Returns a list of video IDs for the given source URL, using the given backend.
+
+ Returns {:ok, list(binary())} | {:error, any, ...}.
+ """
+ def get_video_ids(source_url, backend \\ :yt_dlp) do
+ source_module(backend).get_video_ids(source_url)
+ end
+
+ defp source_module(backend) do
+ case backend do
+ :yt_dlp -> YtDlpSource
+ end
+ end
+end
diff --git a/lib/pinchflat/media_client/video_downloader.ex b/lib/pinchflat/media_client/video_downloader.ex
index a993f59..68d7621 100644
--- a/lib/pinchflat/media_client/video_downloader.ex
+++ b/lib/pinchflat/media_client/video_downloader.ex
@@ -25,8 +25,8 @@ defmodule Pinchflat.MediaClient.VideoDownloader do
Returns {:ok, %MediaItem{}} | {:error, any, ...any}
"""
def download_for_media_item(%MediaItem{} = media_item, backend \\ :yt_dlp) do
- item_with_preloads = Repo.preload(media_item, [:metadata, channel: :media_profile])
- media_profile = item_with_preloads.channel.media_profile
+ item_with_preloads = Repo.preload(media_item, [:metadata, source: :media_profile])
+ media_profile = item_with_preloads.source.media_profile
case download_for_media_profile(media_item.media_id, media_profile, backend) do
{:ok, parsed_json} ->
diff --git a/lib/pinchflat/media_source.ex b/lib/pinchflat/media_source.ex
index 8f8b6f7..88dddd7 100644
--- a/lib/pinchflat/media_source.ex
+++ b/lib/pinchflat/media_source.ex
@@ -8,34 +8,34 @@ defmodule Pinchflat.MediaSource do
alias Pinchflat.Tasks
alias Pinchflat.Media
- alias Pinchflat.Tasks.ChannelTasks
- alias Pinchflat.MediaSource.Channel
- alias Pinchflat.MediaClient.ChannelDetails
+ alias Pinchflat.Tasks.SourceTasks
+ alias Pinchflat.MediaSource.Source
+ alias Pinchflat.MediaClient.SourceDetails
@doc """
- Returns the list of channels. Returns [%Channel{}, ...]
+ Returns the list of sources. Returns [%Source{}, ...]
"""
- def list_channels do
- Repo.all(Channel)
+ def list_sources do
+ Repo.all(Source)
end
@doc """
- Gets a single channel.
+ Gets a single source.
- Returns %Channel{}. Raises `Ecto.NoResultsError` if the Channel does not exist.
+ Returns %Source{}. Raises `Ecto.NoResultsError` if the Source does not exist.
"""
- def get_channel!(id), do: Repo.get!(Channel, id)
+ def get_source!(id), do: Repo.get!(Source, id)
@doc """
- Creates a channel. May attempt to pull additional channel details from the
- original_url (if provided). Will attempt to start indexing the channel's
+ Creates a source. May attempt to pull additional source details from the
+ original_url (if provided). Will attempt to start indexing the source's
media if successfully inserted.
- Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}}
+ Returns {:ok, %Source{}} | {:error, %Ecto.Changeset{}}
"""
- def create_channel(attrs) do
- %Channel{}
- |> change_channel_from_url(attrs)
+ def create_source(attrs) do
+ %Source{}
+ |> change_source_from_url(attrs)
|> commit_and_start_indexing()
end
@@ -45,12 +45,12 @@ defmodule Pinchflat.MediaSource do
Returns [%MediaItem{}, ...] | [%Ecto.Changeset{}, ...]
"""
- def index_media_items(%Channel{} = channel) do
- {:ok, media_ids} = ChannelDetails.get_video_ids(channel.original_url)
+ def index_media_items(%Source{} = source) do
+ {:ok, media_ids} = SourceDetails.get_video_ids(source.original_url)
media_ids
|> 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
{:ok, media_item} -> media_item
@@ -60,67 +60,67 @@ defmodule Pinchflat.MediaSource do
end
@doc """
- Updates a channel. May attempt to pull additional channel details from the
- original_url (if changed). May attempt to start indexing the channel's
+ Updates a source. May attempt to pull additional source details from the
+ original_url (if changed). May attempt to start indexing the source's
media if the indexing frequency has been changed.
Existing indexing tasks will be cancelled if the indexing frequency has been
- changed (logic in `ChannelTasks.kickoff_indexing_task`)
+ changed (logic in `SourceTasks.kickoff_indexing_task`)
- Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}}
+ Returns {:ok, %Source{}} | {:error, %Ecto.Changeset{}}
"""
- def update_channel(%Channel{} = channel, attrs) do
- channel
- |> change_channel_from_url(attrs)
+ def update_source(%Source{} = source, attrs) do
+ source
+ |> change_source_from_url(attrs)
|> commit_and_start_indexing()
end
@doc """
- Deletes a channel and it's associated tasks (of any state).
+ Deletes a source and it's associated tasks (of any state).
- Returns {:ok, %Channel{}} | {:error, %Ecto.Changeset{}}
+ Returns {:ok, %Source{}} | {:error, %Ecto.Changeset{}}
"""
- def delete_channel(%Channel{} = channel) do
- Tasks.delete_tasks_for(channel)
- Repo.delete(channel)
+ def delete_source(%Source{} = source) do
+ Tasks.delete_tasks_for(source)
+ Repo.delete(source)
end
@doc """
- Returns an `%Ecto.Changeset{}` for tracking channel changes.
+ Returns an `%Ecto.Changeset{}` for tracking source changes.
"""
- def change_channel(%Channel{} = channel, attrs \\ %{}) do
- Channel.changeset(channel, attrs)
+ def change_source(%Source{} = source, attrs \\ %{}) do
+ Source.changeset(source, attrs)
end
@doc """
- Returns an `%Ecto.Changeset{}` for tracking channel changes and additionally
- fetches channel details from the original_url (if provided). If the channel
+ Returns an `%Ecto.Changeset{}` for tracking source changes and additionally
+ fetches source details from the original_url (if provided). If the source
details cannot be fetched, an error is added to the changeset.
- Note that this fetches channel details as long as the `original_url` is present.
+ Note that this fetches source details as long as the `original_url` is present.
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.
"""
- def change_channel_from_url(%Channel{} = channel, attrs) do
- case change_channel(channel, attrs) do
+ def change_source_from_url(%Source{} = source, attrs) do
+ case change_source(source, attrs) do
%Ecto.Changeset{changes: %{original_url: _}} = changeset ->
- add_channel_details_to_changeset(channel, changeset)
+ add_source_details_to_changeset(source, changeset)
changeset ->
changeset
end
end
- defp add_channel_details_to_changeset(channel, changeset) do
+ defp add_source_details_to_changeset(source, changeset) do
%Ecto.Changeset{changes: changes} = changeset
- case ChannelDetails.get_channel_details(changes.original_url) do
- {:ok, %ChannelDetails{} = channel_details} ->
- change_channel(
- channel,
+ case SourceDetails.get_source_details(changes.original_url) do
+ {:ok, %SourceDetails{} = source_details} ->
+ change_source(
+ source,
Map.merge(changes, %{
- name: channel_details.name,
- channel_id: channel_details.id
+ name: source_details.name,
+ collection_id: source_details.id
})
)
@@ -128,7 +128,7 @@ defmodule Pinchflat.MediaSource do
Ecto.Changeset.add_error(
changeset,
:original_url,
- "could not fetch channel details from URL",
+ "could not fetch source details from URL",
error: runner_error
)
end
@@ -136,27 +136,27 @@ defmodule Pinchflat.MediaSource do
defp commit_and_start_indexing(changeset) do
case Repo.insert_or_update(changeset) do
- {:ok, %Channel{} = channel} ->
- maybe_run_indexing_task(changeset, channel)
+ {:ok, %Source{} = source} ->
+ maybe_run_indexing_task(changeset, source)
- {:ok, channel}
+ {:ok, source}
err ->
err
end
end
- defp maybe_run_indexing_task(changeset, channel) do
+ defp maybe_run_indexing_task(changeset, source) do
case changeset.data do
# If the changeset is new (not persisted), attempt indexing no matter what
%{__meta__: %{state: :built}} ->
- ChannelTasks.kickoff_indexing_task(channel)
+ SourceTasks.kickoff_indexing_task(source)
# If the record has been persisted, only attempt indexing if the
# indexing frequency has been changed
%{__meta__: %{state: :loaded}} ->
if Map.has_key?(changeset.changes, :index_frequency_minutes) do
- ChannelTasks.kickoff_indexing_task(channel)
+ SourceTasks.kickoff_indexing_task(source)
end
end
end
diff --git a/lib/pinchflat/media_source/channel.ex b/lib/pinchflat/media_source/source.ex
similarity index 51%
rename from lib/pinchflat/media_source/channel.ex
rename to lib/pinchflat/media_source/source.ex
index e70030c..b633bf8 100644
--- a/lib/pinchflat/media_source/channel.ex
+++ b/lib/pinchflat/media_source/source.ex
@@ -1,6 +1,6 @@
-defmodule Pinchflat.MediaSource.Channel do
+defmodule Pinchflat.MediaSource.Source do
@moduledoc """
- The Channel schema.
+ The Source schema.
"""
use Ecto.Schema
@@ -9,29 +9,30 @@ defmodule Pinchflat.MediaSource.Channel do
alias Pinchflat.Media.MediaItem
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
- schema "channels" do
+ schema "sources" do
field :name, :string
- field :channel_id, :string
+ field :collection_id, :string
+ field :collection_type, Ecto.Enum, values: [:channel, :playlist]
field :index_frequency_minutes, :integer
# This should only be used for user reference going forward
- # as the channel_id should be used for all API calls
+ # as the collection_id should be used for all API calls
field :original_url, :string
belongs_to :media_profile, MediaProfile
- has_many :media_items, MediaItem
+ has_many :media_items, MediaItem, foreign_key: :source_id
timestamps(type: :utc_datetime)
end
@doc false
- def changeset(channel, attrs) do
- channel
+ def changeset(source, attrs) do
+ source
|> cast(attrs, @allowed_fields)
|> validate_required(@required_fields)
- |> unique_constraint([:channel_id, :media_profile_id])
+ |> unique_constraint([:collection_id, :media_profile_id])
end
end
diff --git a/lib/pinchflat/profiles/media_profile.ex b/lib/pinchflat/profiles/media_profile.ex
index 39bb6db..10964c9 100644
--- a/lib/pinchflat/profiles/media_profile.ex
+++ b/lib/pinchflat/profiles/media_profile.ex
@@ -6,7 +6,7 @@ defmodule Pinchflat.Profiles.MediaProfile do
use Ecto.Schema
import Ecto.Changeset
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
@allowed_fields ~w(
name
@@ -27,7 +27,7 @@ defmodule Pinchflat.Profiles.MediaProfile do
field :embed_subs, :boolean, default: true
field :sub_langs, :string, default: "en"
- has_many :channels, Channel
+ has_many :sources, Source
timestamps(type: :utc_datetime)
end
diff --git a/lib/pinchflat/tasks.ex b/lib/pinchflat/tasks.ex
index 052b137..642864c 100644
--- a/lib/pinchflat/tasks.ex
+++ b/lib/pinchflat/tasks.ex
@@ -8,7 +8,7 @@ defmodule Pinchflat.Tasks do
alias Pinchflat.Tasks.Task
alias Pinchflat.Media.MediaItem
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
@doc """
Returns the list of tasks. Returns [%Task{}, ...]
@@ -57,7 +57,7 @@ defmodule Pinchflat.Tasks do
@doc """
Creates a task.
- Accepts map() | %Oban.Job{}, %Channel{} | %Oban.Job{}, %MediaItem{}.
+ Accepts map() | %Oban.Job{}, %Source{} | %Oban.Job{}, %MediaItem{}.
Returns {:ok, %Task{}} | {:error, %Ecto.Changeset{}}.
"""
def create_task(attrs) do
@@ -71,7 +71,7 @@ defmodule Pinchflat.Tasks do
def create_task(%Oban.Job{} = job, attached_record) do
attached_record_attr =
case attached_record do
- %Channel{} = channel -> %{channel_id: channel.id}
+ %Source{} = source -> %{source_id: source.id}
%MediaItem{} = media_item -> %{media_item_id: media_item.id}
end
@@ -113,7 +113,7 @@ defmodule Pinchflat.Tasks do
def delete_tasks_for(attached_record) do
tasks =
case attached_record do
- %Channel{} = channel -> list_tasks_for(:channel_id, channel.id)
+ %Source{} = source -> list_tasks_for(:source_id, source.id)
%MediaItem{} = media_item -> list_tasks_for(:media_item_id, media_item.id)
end
@@ -130,7 +130,7 @@ defmodule Pinchflat.Tasks do
def delete_pending_tasks_for(attached_record) do
tasks =
case attached_record do
- %Channel{} = channel -> list_pending_tasks_for(:channel_id, channel.id)
+ %Source{} = source -> list_pending_tasks_for(:source_id, source.id)
%MediaItem{} = media_item -> list_pending_tasks_for(:media_item_id, media_item.id)
end
diff --git a/lib/pinchflat/tasks/channel_tasks.ex b/lib/pinchflat/tasks/source_tasks.ex
similarity index 65%
rename from lib/pinchflat/tasks/channel_tasks.ex
rename to lib/pinchflat/tasks/source_tasks.ex
index a2b1cd2..0d5aa08 100644
--- a/lib/pinchflat/tasks/channel_tasks.ex
+++ b/lib/pinchflat/tasks/source_tasks.ex
@@ -1,28 +1,28 @@
-defmodule Pinchflat.Tasks.ChannelTasks do
+defmodule Pinchflat.Tasks.SourceTasks do
@moduledoc """
- This module contains methods for managing tasks (workers) related to channels.
+ This module contains methods for managing tasks (workers) related to sources.
"""
alias Pinchflat.Tasks
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
alias Pinchflat.Workers.MediaIndexingWorker
@doc """
- Starts tasks for indexing a channel's media.
+ Starts tasks for indexing a source's media.
Returns {:ok, :should_not_index} | {:ok, %Task{}}.
"""
- def kickoff_indexing_task(%Channel{} = channel) do
- Tasks.delete_pending_tasks_for(channel)
+ def kickoff_indexing_task(%Source{} = source) do
+ Tasks.delete_pending_tasks_for(source)
- if channel.index_frequency_minutes <= 0 do
+ if source.index_frequency_minutes <= 0 do
{:ok, :should_not_index}
else
- channel
+ source
|> Map.take([:id])
# Schedule this one immediately, but future ones will be on an interval
|> MediaIndexingWorker.new()
- |> Tasks.create_job_with_task(channel)
+ |> Tasks.create_job_with_task(source)
|> case do
# This should never return {:error, :duplicate_job} since we just deleted
# any pending tasks. I'm being assertive about it so it's obvious if I'm wrong
diff --git a/lib/pinchflat/tasks/task.ex b/lib/pinchflat/tasks/task.ex
index 9d53b60..34cf113 100644
--- a/lib/pinchflat/tasks/task.ex
+++ b/lib/pinchflat/tasks/task.ex
@@ -7,11 +7,11 @@ defmodule Pinchflat.Tasks.Task do
import Ecto.Changeset
alias Pinchflat.Media.MediaItem
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
schema "tasks" do
belongs_to :job, Oban.Job
- belongs_to :channel, Channel
+ belongs_to :source, Source
belongs_to :media_item, MediaItem
timestamps(type: :utc_datetime)
@@ -20,7 +20,7 @@ defmodule Pinchflat.Tasks.Task do
@doc false
def changeset(task, attrs) do
task
- |> cast(attrs, [:job_id, :channel_id, :media_item_id])
+ |> cast(attrs, [:job_id, :source_id, :media_item_id])
|> validate_required([:job_id])
end
end
diff --git a/lib/pinchflat/workers/media_indexing_worker.ex b/lib/pinchflat/workers/media_indexing_worker.ex
index 4869b0d..166a4bd 100644
--- a/lib/pinchflat/workers/media_indexing_worker.ex
+++ b/lib/pinchflat/workers/media_indexing_worker.ex
@@ -14,10 +14,10 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
@impl Oban.Worker
@doc """
- The ID is that of a channel _record_, not a YouTube channel ID. Indexes
- the provided channel, kicks off downloads for each new MediaItem, and
+ The ID is that of a source _record_, not a YouTube channel/playlist ID. Indexes
+ the provided source, kicks off downloads for each new MediaItem, and
reschedules the job to run again in the future (as determined by the
- channel's `index_frequency_minutes` field).
+ souce's `index_frequency_minutes` field).
README: Re-scheduling here works a little different than you may expect.
The reschedule time is relative to the time the job has actually _completed_.
@@ -37,24 +37,24 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
Returns :ok | {:ok, %Task{}}
"""
- def perform(%Oban.Job{args: %{"id" => channel_id}}) do
- channel = MediaSource.get_channel!(channel_id)
+ def perform(%Oban.Job{args: %{"id" => source_id}}) do
+ source = MediaSource.get_source!(source_id)
- if channel.index_frequency_minutes <= 0 do
+ if source.index_frequency_minutes <= 0 do
:ok
else
- index_media_and_reschedule(channel)
+ index_media_and_reschedule(source)
end
end
- defp index_media_and_reschedule(channel) do
- MediaSource.index_media_items(channel)
- enqueue_video_downloads(channel)
+ defp index_media_and_reschedule(source) do
+ MediaSource.index_media_items(source)
+ enqueue_video_downloads(source)
- channel
+ source
|> Map.take([:id])
- |> MediaIndexingWorker.new(schedule_in: channel.index_frequency_minutes * 60)
- |> Tasks.create_job_with_task(channel)
+ |> MediaIndexingWorker.new(schedule_in: source.index_frequency_minutes * 60)
+ |> Tasks.create_job_with_task(source)
|> case do
{:ok, task} -> {:ok, task}
{:error, :duplicate_job} -> {:ok, :job_exists}
@@ -67,8 +67,8 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
# or somehow got de-queued.
#
# I'm not sure of a case where this would happen, but it's cheap insurance.
- defp enqueue_video_downloads(channel) do
- channel
+ defp enqueue_video_downloads(source) do
+ source
|> Media.list_pending_media_items_for()
|> Enum.each(fn media_item ->
media_item
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_controller.ex b/lib/pinchflat_web/controllers/media_sources/channel_controller.ex
deleted file mode 100644
index 08de4f4..0000000
--- a/lib/pinchflat_web/controllers/media_sources/channel_controller.ex
+++ /dev/null
@@ -1,75 +0,0 @@
-defmodule PinchflatWeb.MediaSources.ChannelController do
- use PinchflatWeb, :controller
-
- alias Pinchflat.Profiles
- alias Pinchflat.MediaSource
- alias Pinchflat.MediaSource.Channel
-
- def index(conn, _params) do
- channels = MediaSource.list_channels()
-
- render(conn, :index, channels: channels)
- end
-
- def new(conn, _params) do
- changeset = MediaSource.change_channel(%Channel{})
-
- render(conn, :new, changeset: changeset, media_profiles: media_profiles())
- end
-
- def create(conn, %{"channel" => channel_params}) do
- case MediaSource.create_channel(channel_params) do
- {:ok, channel} ->
- conn
- |> put_flash(:info, "Channel created successfully.")
- |> redirect(to: ~p"/media_sources/channels/#{channel}")
-
- {:error, %Ecto.Changeset{} = changeset} ->
- render(conn, :new, changeset: changeset, media_profiles: media_profiles())
- end
- end
-
- def show(conn, %{"id" => id}) do
- channel = MediaSource.get_channel!(id)
-
- render(conn, :show, channel: channel)
- end
-
- def edit(conn, %{"id" => id}) do
- channel = MediaSource.get_channel!(id)
- changeset = MediaSource.change_channel(channel)
-
- render(conn, :edit, channel: channel, changeset: changeset, media_profiles: media_profiles())
- end
-
- def update(conn, %{"id" => id, "channel" => channel_params}) do
- channel = MediaSource.get_channel!(id)
-
- case MediaSource.update_channel(channel, channel_params) do
- {:ok, channel} ->
- conn
- |> put_flash(:info, "Channel updated successfully.")
- |> redirect(to: ~p"/media_sources/channels/#{channel}")
-
- {:error, %Ecto.Changeset{} = changeset} ->
- render(conn, :edit,
- channel: channel,
- changeset: changeset,
- media_profiles: media_profiles()
- )
- end
- end
-
- def delete(conn, %{"id" => id}) do
- channel = MediaSource.get_channel!(id)
- {:ok, _channel} = MediaSource.delete_channel(channel)
-
- conn
- |> put_flash(:info, "Channel deleted successfully.")
- |> redirect(to: ~p"/media_sources/channels")
- end
-
- defp media_profiles do
- Profiles.list_media_profiles()
- end
-end
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_html/edit.html.heex b/lib/pinchflat_web/controllers/media_sources/channel_html/edit.html.heex
deleted file mode 100644
index c65d810..0000000
--- a/lib/pinchflat_web/controllers/media_sources/channel_html/edit.html.heex
+++ /dev/null
@@ -1,12 +0,0 @@
-<.header>
- Edit Channel <%= @channel.id %>
- <:subtitle>Use this form to manage channel records in your database.
-
-
-<.channel_form
- changeset={@changeset}
- media_profiles={@media_profiles}
- action={~p"/media_sources/channels/#{@channel}"}
-/>
-
-<.back navigate={~p"/media_sources/channels"}>Back to channels
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_html/index.html.heex b/lib/pinchflat_web/controllers/media_sources/channel_html/index.html.heex
deleted file mode 100644
index b78a61b..0000000
--- a/lib/pinchflat_web/controllers/media_sources/channel_html/index.html.heex
+++ /dev/null
@@ -1,28 +0,0 @@
-<.header>
- Listing Channels
- <:actions>
- <.link href={~p"/media_sources/channels/new"}>
- <.button>New Channel
-
-
-
-
-<.table id="channels" rows={@channels} row_click={&JS.navigate(~p"/media_sources/channels/#{&1}")}>
- <:col :let={channel} label="Name"><%= channel.name %>
- <:col :let={channel} label="Channel"><%= channel.channel_id %>
- <:action :let={channel}>
-
- <.link navigate={~p"/media_sources/channels/#{channel}"}>Show
-
- <.link navigate={~p"/media_sources/channels/#{channel}/edit"}>Edit
-
- <:action :let={channel}>
- <.link
- href={~p"/media_sources/channels/#{channel}"}
- method="delete"
- data-confirm="Are you sure?"
- >
- Delete
-
-
-
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_html/new.html.heex b/lib/pinchflat_web/controllers/media_sources/channel_html/new.html.heex
deleted file mode 100644
index b09a477..0000000
--- a/lib/pinchflat_web/controllers/media_sources/channel_html/new.html.heex
+++ /dev/null
@@ -1,12 +0,0 @@
-<.header>
- New Channel
- <:subtitle>Use this form to manage channel records in your database.
-
-
-<.channel_form
- changeset={@changeset}
- media_profiles={@media_profiles}
- action={~p"/media_sources/channels"}
-/>
-
-<.back navigate={~p"/media_sources/channels"}>Back to channels
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_html/show.html.heex b/lib/pinchflat_web/controllers/media_sources/channel_html/show.html.heex
deleted file mode 100644
index 0f59e3b..0000000
--- a/lib/pinchflat_web/controllers/media_sources/channel_html/show.html.heex
+++ /dev/null
@@ -1,17 +0,0 @@
-<.header>
- Channel <%= @channel.id %>
- <:subtitle>This is a channel record from your database.
- <:actions>
- <.link href={~p"/media_sources/channels/#{@channel}/edit"}>
- <.button>Edit channel
-
-
-
-
-<.list>
- <:item title="Channel Name"><%= @channel.name %>
- <:item title="Channel ID"><%= @channel.channel_id %>
- <:item title="Original URL"><%= @channel.original_url %>
-
-
-<.back navigate={~p"/media_sources/channels"}>Back to channels
diff --git a/lib/pinchflat_web/controllers/media_sources/source_controller.ex b/lib/pinchflat_web/controllers/media_sources/source_controller.ex
new file mode 100644
index 0000000..b90cf1e
--- /dev/null
+++ b/lib/pinchflat_web/controllers/media_sources/source_controller.ex
@@ -0,0 +1,75 @@
+defmodule PinchflatWeb.MediaSources.SourceController do
+ use PinchflatWeb, :controller
+
+ alias Pinchflat.Profiles
+ alias Pinchflat.MediaSource
+ alias Pinchflat.MediaSource.Source
+
+ def index(conn, _params) do
+ sources = MediaSource.list_sources()
+
+ render(conn, :index, sources: sources)
+ end
+
+ def new(conn, _params) do
+ changeset = MediaSource.change_source(%Source{})
+
+ render(conn, :new, changeset: changeset, media_profiles: media_profiles())
+ end
+
+ def create(conn, %{"source" => source_params}) do
+ case MediaSource.create_source(source_params) do
+ {:ok, source} ->
+ conn
+ |> put_flash(:info, "Source created successfully.")
+ |> redirect(to: ~p"/media_sources/sources/#{source}")
+
+ {:error, %Ecto.Changeset{} = changeset} ->
+ render(conn, :new, changeset: changeset, media_profiles: media_profiles())
+ end
+ end
+
+ def show(conn, %{"id" => id}) do
+ source = MediaSource.get_source!(id)
+
+ render(conn, :show, source: source)
+ end
+
+ def edit(conn, %{"id" => id}) do
+ source = MediaSource.get_source!(id)
+ changeset = MediaSource.change_source(source)
+
+ render(conn, :edit, source: source, changeset: changeset, media_profiles: media_profiles())
+ end
+
+ def update(conn, %{"id" => id, "source" => source_params}) do
+ source = MediaSource.get_source!(id)
+
+ case MediaSource.update_source(source, source_params) do
+ {:ok, source} ->
+ conn
+ |> put_flash(:info, "Source updated successfully.")
+ |> redirect(to: ~p"/media_sources/sources/#{source}")
+
+ {:error, %Ecto.Changeset{} = changeset} ->
+ render(conn, :edit,
+ source: source,
+ changeset: changeset,
+ media_profiles: media_profiles()
+ )
+ end
+ end
+
+ def delete(conn, %{"id" => id}) do
+ source = MediaSource.get_source!(id)
+ {:ok, _source} = MediaSource.delete_source(source)
+
+ conn
+ |> put_flash(:info, "Source deleted successfully.")
+ |> redirect(to: ~p"/media_sources/sources")
+ end
+
+ defp media_profiles do
+ Profiles.list_media_profiles()
+ end
+end
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_html.ex b/lib/pinchflat_web/controllers/media_sources/source_html.ex
similarity index 77%
rename from lib/pinchflat_web/controllers/media_sources/channel_html.ex
rename to lib/pinchflat_web/controllers/media_sources/source_html.ex
index 0feb191..9531a59 100644
--- a/lib/pinchflat_web/controllers/media_sources/channel_html.ex
+++ b/lib/pinchflat_web/controllers/media_sources/source_html.ex
@@ -1,16 +1,16 @@
-defmodule PinchflatWeb.MediaSources.ChannelHTML do
+defmodule PinchflatWeb.MediaSources.SourceHTML do
use PinchflatWeb, :html
- embed_templates "channel_html/*"
+ embed_templates "source_html/*"
@doc """
- Renders a channel form.
+ Renders a source form.
"""
attr :changeset, Ecto.Changeset, required: true
attr :action, :string, required: true
attr :media_profiles, :list, required: true
- def channel_form(assigns)
+ def source_form(assigns)
def friendly_index_frequencies do
[
diff --git a/lib/pinchflat_web/controllers/media_sources/source_html/edit.html.heex b/lib/pinchflat_web/controllers/media_sources/source_html/edit.html.heex
new file mode 100644
index 0000000..d1afc04
--- /dev/null
+++ b/lib/pinchflat_web/controllers/media_sources/source_html/edit.html.heex
@@ -0,0 +1,12 @@
+<.header>
+ Edit Source <%= @source.id %>
+ <:subtitle>Use this form to manage source records in your database.
+
+
+<.source_form
+ changeset={@changeset}
+ media_profiles={@media_profiles}
+ action={~p"/media_sources/sources/#{@source}"}
+/>
+
+<.back navigate={~p"/media_sources/sources"}>Back to sources
diff --git a/lib/pinchflat_web/controllers/media_sources/source_html/index.html.heex b/lib/pinchflat_web/controllers/media_sources/source_html/index.html.heex
new file mode 100644
index 0000000..348873d
--- /dev/null
+++ b/lib/pinchflat_web/controllers/media_sources/source_html/index.html.heex
@@ -0,0 +1,24 @@
+<.header>
+ Listing Sources
+ <:actions>
+ <.link href={~p"/media_sources/sources/new"}>
+ <.button>New Source
+
+
+
+
+<.table id="sources" rows={@sources} row_click={&JS.navigate(~p"/media_sources/sources/#{&1}")}>
+ <:col :let={source} label="Name"><%= source.name %>
+ <:col :let={source} label="Source"><%= source.collection_id %>
+ <:action :let={source}>
+
+ <.link navigate={~p"/media_sources/sources/#{source}"}>Show
+
+ <.link navigate={~p"/media_sources/sources/#{source}/edit"}>Edit
+
+ <:action :let={source}>
+ <.link href={~p"/media_sources/sources/#{source}"} method="delete" data-confirm="Are you sure?">
+ Delete
+
+
+
diff --git a/lib/pinchflat_web/controllers/media_sources/source_html/new.html.heex b/lib/pinchflat_web/controllers/media_sources/source_html/new.html.heex
new file mode 100644
index 0000000..4ef5741
--- /dev/null
+++ b/lib/pinchflat_web/controllers/media_sources/source_html/new.html.heex
@@ -0,0 +1,12 @@
+<.header>
+ New Source
+ <:subtitle>Use this form to manage source records in your database.
+
+
+<.source_form
+ changeset={@changeset}
+ media_profiles={@media_profiles}
+ action={~p"/media_sources/sources"}
+/>
+
+<.back navigate={~p"/media_sources/sources"}>Back to sources
diff --git a/lib/pinchflat_web/controllers/media_sources/source_html/show.html.heex b/lib/pinchflat_web/controllers/media_sources/source_html/show.html.heex
new file mode 100644
index 0000000..5ae2176
--- /dev/null
+++ b/lib/pinchflat_web/controllers/media_sources/source_html/show.html.heex
@@ -0,0 +1,17 @@
+<.header>
+ Source <%= @source.id %>
+ <:subtitle>This is a source record from your database.
+ <:actions>
+ <.link href={~p"/media_sources/sources/#{@source}/edit"}>
+ <.button>Edit source
+
+
+
+
+<.list>
+ <:item title="Source Name"><%= @source.name %>
+ <:item title="Source ID"><%= @source.collection_id %>
+ <:item title="Original URL"><%= @source.original_url %>
+
+
+<.back navigate={~p"/media_sources/sources"}>Back to sources
diff --git a/lib/pinchflat_web/controllers/media_sources/channel_html/channel_form.html.heex b/lib/pinchflat_web/controllers/media_sources/source_html/source_form.html.heex
similarity index 73%
rename from lib/pinchflat_web/controllers/media_sources/channel_html/channel_form.html.heex
rename to lib/pinchflat_web/controllers/media_sources/source_html/source_form.html.heex
index b8b4d4e..f8a0a87 100644
--- a/lib/pinchflat_web/controllers/media_sources/channel_html/channel_form.html.heex
+++ b/lib/pinchflat_web/controllers/media_sources/source_html/source_form.html.heex
@@ -10,7 +10,8 @@
label="Media Profile"
/>
- <.input field={f[:original_url]} type="text" label="Channel URL" />
+ <.input field={f[:collection_type]} type="text" label="Collection Type" />
+ <.input field={f[:original_url]} type="text" label="Source URL" />
<.input
field={f[:index_frequency_minutes]}
@@ -20,6 +21,6 @@
/>
<:actions>
- <.button>Save Channel
+ <.button>Save Source
diff --git a/lib/pinchflat_web/router.ex b/lib/pinchflat_web/router.ex
index e0775ea..fd06da3 100644
--- a/lib/pinchflat_web/router.ex
+++ b/lib/pinchflat_web/router.ex
@@ -22,7 +22,7 @@ defmodule PinchflatWeb.Router do
resources "/media_profiles", MediaProfiles.MediaProfileController
scope "/media_sources", MediaSources do
- resources "/channels", ChannelController
+ resources "/sources", SourceController
end
end
diff --git a/priv/repo/migrations/20240202040724_rename_channel_and_related_fields.exs b/priv/repo/migrations/20240202040724_rename_channel_and_related_fields.exs
new file mode 100644
index 0000000..977756f
--- /dev/null
+++ b/priv/repo/migrations/20240202040724_rename_channel_and_related_fields.exs
@@ -0,0 +1,45 @@
+defmodule Pinchflat.Repo.Migrations.RenameChannelAndRelatedFields do
+ use Ecto.Migration
+
+ def change do
+ # Creation
+ create table(:sources) do
+ add :name, :string, null: false
+ add :collection_type, :string, null: false
+ add :collection_id, :string, null: false
+ add :original_url, :string, 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)
+ end
+
+ alter table(:media_items) do
+ add :source_id, references(:sources, on_delete: :restrict), null: false
+ end
+
+ alter table(:tasks) do
+ # `restrict` because we need to be sure to delete pending tasks when a source is deleted
+ add :source_id, references(:sources, on_delete: :restrict), null: true
+ end
+
+ create index(:sources, [:media_profile_id])
+ create unique_index(:sources, [:collection_id, :media_profile_id])
+
+ create index(:media_items, [:source_id])
+ create unique_index(:media_items, [:media_id, :source_id])
+
+ # Deletion
+ drop index(:media_items, [:channel_id])
+ drop unique_index(:media_items, [:media_id, :channel_id])
+
+ alter table(:tasks) do
+ # `restrict` because we need to be sure to delete pending tasks when a source is deleted
+ remove :channel_id, references(:channels, on_delete: :restrict), null: true
+ end
+
+ alter table(:media_items) do
+ remove :channel_id, references(:channels, on_delete: :restrict), null: true
+ end
+ end
+end
diff --git a/priv/repo/migrations/20240202043042_drop_channels_table.exs b/priv/repo/migrations/20240202043042_drop_channels_table.exs
new file mode 100644
index 0000000..6397b19
--- /dev/null
+++ b/priv/repo/migrations/20240202043042_drop_channels_table.exs
@@ -0,0 +1,21 @@
+defmodule Pinchflat.Repo.Migrations.DropChannelsTable do
+ use Ecto.Migration
+
+ def up do
+ drop table(:channels)
+ end
+
+ def down do
+ create table(:channels) do
+ add :name, :string, null: false
+ add :channel_id, :string, null: false
+ add :original_url, :string, null: false
+ add :media_profile_id, references(:media_profiles, on_delete: :restrict), null: false
+
+ timestamps(type: :utc_datetime)
+ end
+
+ create index(:channels, [:media_profile_id])
+ create unique_index(:channels, [:channel_id, :media_profile_id])
+ end
+end
diff --git a/test/pinchflat/media_client/backends/yt_dlp/channel_test.exs b/test/pinchflat/media_client/backends/yt_dlp/channel_test.exs
deleted file mode 100644
index 3f9d52f..0000000
--- a/test/pinchflat/media_client/backends/yt_dlp/channel_test.exs
+++ /dev/null
@@ -1,45 +0,0 @@
-defmodule Pinchflat.MediaClient.Backends.YtDlp.ChannelTest do
- use ExUnit.Case, async: true
- import Mox
-
- alias Pinchflat.MediaClient.ChannelDetails
- alias Pinchflat.MediaClient.Backends.YtDlp.Channel
-
- @channel_url "https://www.youtube.com/c/TheUselessTrials"
-
- setup :verify_on_exit!
-
- describe "get_channel_details/1" do
- test "it returns a %ChannelDetails{} with data on success" do
- expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
- {:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
- end)
-
- assert {:ok, res} = Channel.get_channel_details(@channel_url)
- assert %ChannelDetails{id: "UCQH2", name: "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_end: 1]
- assert ot == "%(.{channel,channel_id})j"
-
- {:ok, "{}"}
- end)
-
- assert {:ok, _} = Channel.get_channel_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} = Channel.get_channel_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{}} = Channel.get_channel_details(@channel_url)
- end
- end
-end
diff --git a/test/pinchflat/media_client/backends/yt_dlp/video_collection_test.exs b/test/pinchflat/media_client/backends/yt_dlp/video_collection_test.exs
index 3acae20..3c8bfe7 100644
--- a/test/pinchflat/media_client/backends/yt_dlp/video_collection_test.exs
+++ b/test/pinchflat/media_client/backends/yt_dlp/video_collection_test.exs
@@ -2,13 +2,10 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
use ExUnit.Case, async: true
import Mox
+ alias Pinchflat.MediaClient.SourceDetails
alias Pinchflat.MediaClient.Backends.YtDlp.VideoCollection
- @channel_url "https://www.youtube.com/@TheUselessTrials"
-
- defmodule VideoCollectionUser do
- use VideoCollection
- end
+ @channel_url "https://www.youtube.com/c/TheUselessTrials"
setup :verify_on_exit!
@@ -16,7 +13,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
test "returns a list of video ids with no blank elements" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "id1\nid2\n\nid3\n"} end)
- assert {:ok, ["id1", "id2", "id3"]} = VideoCollectionUser.get_video_ids(@channel_url)
+ assert {:ok, ["id1", "id2", "id3"]} = VideoCollection.get_video_ids(@channel_url)
end
test "it passes the expected default args" do
@@ -27,7 +24,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
{:ok, ""}
end)
- assert {:ok, _} = VideoCollectionUser.get_video_ids(@channel_url)
+ assert {:ok, _} = VideoCollection.get_video_ids(@channel_url)
end
test "it passes the expected custom args" do
@@ -37,13 +34,47 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.VideoCollectionTest do
{:ok, ""}
end)
- assert {:ok, _} = VideoCollectionUser.get_video_ids(@channel_url, [:custom_arg])
+ assert {:ok, _} = VideoCollection.get_video_ids(@channel_url, [:custom_arg])
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} = VideoCollectionUser.get_video_ids(@channel_url)
+ assert {:error, "Big issue", 1} = VideoCollection.get_video_ids(@channel_url)
+ end
+ end
+
+ describe "get_source_details/1" do
+ test "it returns a %SourceDetails{} with data on success" do
+ expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
+ {:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
+ end)
+
+ assert {:ok, res} = VideoCollection.get_source_details(@channel_url)
+ assert %SourceDetails{id: "UCQH2", name: "TheUselessTrials"} = res
+ end
+
+ test "it passes the expected args to the backend runner" do
+ expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
+ assert opts == [:skip_download, playlist_end: 1]
+ assert ot == "%(.{channel,channel_id})j"
+
+ {:ok, "{}"}
+ end)
+
+ assert {:ok, _} = VideoCollection.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} = VideoCollection.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{}} = VideoCollection.get_source_details(@channel_url)
end
end
end
diff --git a/test/pinchflat/media_client/channel_details_test.exs b/test/pinchflat/media_client/source_details_test.exs
similarity index 63%
rename from test/pinchflat/media_client/channel_details_test.exs
rename to test/pinchflat/media_client/source_details_test.exs
index 920fc02..244e65d 100644
--- a/test/pinchflat/media_client/channel_details_test.exs
+++ b/test/pinchflat/media_client/source_details_test.exs
@@ -1,8 +1,8 @@
-defmodule Pinchflat.MediaClient.ChannelDetailsTest do
+defmodule Pinchflat.MediaClient.SourceDetailsTest do
use ExUnit.Case, async: true
import Mox
- alias Pinchflat.MediaClient.ChannelDetails
+ alias Pinchflat.MediaClient.SourceDetails
@channel_url "https://www.youtube.com/c/TheUselessTrials"
@@ -10,21 +10,21 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
describe "new/2" do
test "it returns a struct with the given values" do
- assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} =
- ChannelDetails.new("UCQH2", "TheUselessTrials")
+ assert %SourceDetails{id: "UCQH2", name: "TheUselessTrials"} =
+ SourceDetails.new("UCQH2", "TheUselessTrials")
end
end
- describe "get_channel_details/2" do
+ describe "get_source_details/2" do
test "it passes the expected arguments to the backend" do
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
- assert opts == [playlist_end: 1]
+ assert opts == [:skip_download, playlist_end: 1]
assert ot == "%(.{channel,channel_id})j"
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end)
- assert {:ok, _} = ChannelDetails.get_channel_details(@channel_url)
+ assert {:ok, _} = SourceDetails.get_source_details(@channel_url)
end
test "it returns a struct composed of the returned data" do
@@ -32,8 +32,8 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end)
- assert {:ok, res} = ChannelDetails.get_channel_details(@channel_url)
- assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} = res
+ assert {:ok, res} = SourceDetails.get_source_details(@channel_url)
+ assert %SourceDetails{id: "UCQH2", name: "TheUselessTrials"} = res
end
end
@@ -46,7 +46,7 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
{:ok, ""}
end)
- assert {:ok, _} = ChannelDetails.get_video_ids(@channel_url)
+ assert {:ok, _} = SourceDetails.get_video_ids(@channel_url)
end
test "it returns a list of strings" do
@@ -54,7 +54,7 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
{:ok, "video1\nvideo2\nvideo3"}
end)
- assert {:ok, ["video1", "video2", "video3"]} = ChannelDetails.get_video_ids(@channel_url)
+ assert {:ok, ["video1", "video2", "video3"]} = SourceDetails.get_video_ids(@channel_url)
end
end
end
diff --git a/test/pinchflat/media_client/video_downloader_test.exs b/test/pinchflat/media_client/video_downloader_test.exs
index 50d19fe..3d31530 100644
--- a/test/pinchflat/media_client/video_downloader_test.exs
+++ b/test/pinchflat/media_client/video_downloader_test.exs
@@ -11,7 +11,7 @@ defmodule Pinchflat.MediaClient.VideoDownloaderTest do
media_item =
Repo.preload(
media_item_fixture(%{title: nil, media_filepath: nil}),
- [:metadata, channel: :media_profile]
+ [:metadata, source: :media_profile]
)
{:ok, %{media_item: media_item}}
diff --git a/test/pinchflat/media_source_test.exs b/test/pinchflat/media_source_test.exs
index f46cfb3..e113df5 100644
--- a/test/pinchflat/media_source_test.exs
+++ b/test/pinchflat/media_source_test.exs
@@ -7,82 +7,85 @@ defmodule Pinchflat.MediaSourceTest do
alias Pinchflat.MediaSource
alias Pinchflat.Media.MediaItem
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
alias Pinchflat.Workers.MediaIndexingWorker
- @invalid_channel_attrs %{name: nil, channel_id: nil}
+ @invalid_source_attrs %{name: nil, collection_id: nil}
setup :verify_on_exit!
- describe "list_channels/0" do
- test "it returns all channels" do
- channel = channel_fixture()
- assert MediaSource.list_channels() == [channel]
+ describe "list_sources/0" do
+ test "it returns all sources" do
+ source = source_fixture()
+ assert MediaSource.list_sources() == [source]
end
end
- describe "get_channel!/1" do
- test "it returns the channel with given id" do
- channel = channel_fixture()
- assert MediaSource.get_channel!(channel.id) == channel
+ describe "get_source!/1" do
+ test "it returns the source with given id" do
+ source = source_fixture()
+ assert MediaSource.get_source!(source.id) == source
end
end
- describe "create_channel/1" do
- test "creates a channel and adds name + ID from runner response" do
+ describe "create_source/1" do
+ test "creates a source and adds name + ID from runner response" do
expect(YtDlpRunnerMock, :run, &runner_function_mock/3)
valid_attrs = %{
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 channel.name == "some name"
- assert String.starts_with?(channel.channel_id, "some_channel_id_")
+ assert {:ok, %Source{} = source} = MediaSource.create_source(valid_attrs)
+ assert source.name == "some name"
+ assert String.starts_with?(source.collection_id, "some_source_id_")
end
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
- 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 ->
{:ok,
Phoenix.json_library().encode!(%{
channel: "some name",
- channel_id: "some_channel_id_12345678"
+ channel_id: "some_source_id_12345678"
})}
end)
valid_once_attrs = %{
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 {:error, %Ecto.Changeset{}} = MediaSource.create_channel(valid_once_attrs)
+ assert {:ok, %Source{}} = MediaSource.create_source(valid_once_attrs)
+ assert {:error, %Ecto.Changeset{}} = MediaSource.create_source(valid_once_attrs)
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 ->
{:ok,
Phoenix.json_library().encode!(%{
channel: "some name",
- channel_id: "some_channel_id_12345678"
+ channel_id: "some_source_id_12345678"
})}
end)
valid_attrs = %{
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})
- channel_2_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})
+ 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_channel(channel_2_attrs)
+ assert {:ok, %Source{}} = MediaSource.create_source(source_1_attrs)
+ assert {:ok, %Source{}} = MediaSource.create_source(source_2_attrs)
end
test "creation will schedule the indexing task" do
@@ -90,12 +93,13 @@ defmodule Pinchflat.MediaSourceTest do
valid_attrs = %{
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, %Source{} = source} = MediaSource.create_source(valid_attrs)
- assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
+ assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end
end
@@ -103,181 +107,181 @@ defmodule Pinchflat.MediaSourceTest do
setup do
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo2\nvideo3"} end)
- {:ok, [channel: channel_fixture()]}
+ {:ok, [source: source_fixture()]}
end
- test "it creates a media_item record for each media ID returned", %{channel: channel} do
- assert media_items = MediaSource.index_media_items(channel)
+ test "it creates a media_item record for each media ID returned", %{source: source} do
+ assert media_items = MediaSource.index_media_items(source)
assert Enum.count(media_items) == 3
assert ["video1", "video2", "video3"] == Enum.map(media_items, & &1.media_id)
assert Enum.all?(media_items, fn %MediaItem{} -> true end)
end
- test "it attaches all media_items to the given channel", %{channel: channel} do
- channel_id = channel.id
- assert media_items = MediaSource.index_media_items(channel)
+ test "it attaches all media_items to the given source", %{source: source} do
+ source_id = source.id
+ assert media_items = MediaSource.index_media_items(source)
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
- test "it won't duplicate media_items based on media_id and channel", %{channel: channel} do
- _first_run = MediaSource.index_media_items(channel)
- _duplicate_run = MediaSource.index_media_items(channel)
+ test "it won't duplicate media_items based on media_id and source", %{source: source} do
+ _first_run = MediaSource.index_media_items(source)
+ _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
end
- test "it can duplicate media_ids for different channels", %{channel: channel} do
- other_channel = channel_fixture()
+ test "it can duplicate media_ids for different sources", %{source: source} do
+ other_source = source_fixture()
- media_items = MediaSource.index_media_items(channel)
- media_items_other_channel = MediaSource.index_media_items(other_channel)
+ media_items = MediaSource.index_media_items(source)
+ media_items_other_source = MediaSource.index_media_items(other_source)
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) ==
- Enum.map(media_items_other_channel, & &1.media_id)
+ Enum.map(media_items_other_source, & &1.media_id)
end
- test "it returns a list of media_items or changesets", %{channel: channel} do
- first_run = MediaSource.index_media_items(channel)
- duplicate_run = MediaSource.index_media_items(channel)
+ test "it returns a list of media_items or changesets", %{source: source} do
+ first_run = MediaSource.index_media_items(source)
+ duplicate_run = MediaSource.index_media_items(source)
assert Enum.all?(first_run, fn %MediaItem{} -> true end)
assert Enum.all?(duplicate_run, fn %Ecto.Changeset{} -> true end)
end
end
- describe "update_channel/2" do
- test "updates with valid data updates the channel" do
- channel = channel_fixture()
+ describe "update_source/2" do
+ test "updates with valid data updates the source" do
+ source = source_fixture()
update_attrs = %{name: "some updated name"}
- assert {:ok, %Channel{} = channel} = MediaSource.update_channel(channel, update_attrs)
- assert channel.name == "some updated name"
+ assert {:ok, %Source{} = source} = MediaSource.update_source(source, update_attrs)
+ assert source.name == "some updated name"
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)
- channel = channel_fixture()
+ source = source_fixture()
update_attrs = %{original_url: "https://www.youtube.com/channel/abc123"}
- assert {:ok, %Channel{} = channel} = MediaSource.update_channel(channel, update_attrs)
- assert channel.name == "some name"
- assert String.starts_with?(channel.channel_id, "some_channel_id_")
+ assert {:ok, %Source{} = source} = MediaSource.update_source(source, update_attrs)
+ assert source.name == "some name"
+ assert String.starts_with?(source.collection_id, "some_source_id_")
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)
- channel = channel_fixture()
+ source = source_fixture()
update_attrs = %{name: "some updated name"}
- assert {:ok, %Channel{}} = MediaSource.update_channel(channel, update_attrs)
+ assert {:ok, %Source{}} = MediaSource.update_source(source, update_attrs)
end
test "updating the index frequency will re-schedule the indexing task" do
- channel = channel_fixture()
+ source = source_fixture()
update_attrs = %{index_frequency_minutes: 123}
- assert {:ok, %Channel{} = channel} = MediaSource.update_channel(channel, update_attrs)
- assert channel.index_frequency_minutes == 123
- assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
+ assert {:ok, %Source{} = source} = MediaSource.update_source(source, update_attrs)
+ assert source.index_frequency_minutes == 123
+ assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end
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"}
- assert {:ok, %Channel{}} = MediaSource.update_channel(channel, update_attrs)
- refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
+ assert {:ok, %Source{}} = MediaSource.update_source(source, update_attrs)
+ refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end
test "updates with invalid data returns error changeset" do
- channel = channel_fixture()
+ source = source_fixture()
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
- describe "delete_channel/1" do
- test "it deletes the channel" do
- channel = channel_fixture()
- assert {:ok, %Channel{}} = MediaSource.delete_channel(channel)
- assert_raise Ecto.NoResultsError, fn -> MediaSource.get_channel!(channel.id) end
+ describe "delete_source/1" do
+ test "it deletes the source" do
+ source = source_fixture()
+ assert {:ok, %Source{}} = MediaSource.delete_source(source)
+ assert_raise Ecto.NoResultsError, fn -> MediaSource.get_source!(source.id) end
end
- test "it returns a channel changeset" do
- channel = channel_fixture()
- assert %Ecto.Changeset{} = MediaSource.change_channel(channel)
+ test "it returns a source changeset" do
+ source = source_fixture()
+ assert %Ecto.Changeset{} = MediaSource.change_source(source)
end
test "deletion also deletes all associated tasks" do
- channel = channel_fixture()
- task = task_fixture(channel_id: channel.id)
+ source = source_fixture()
+ task = task_fixture(source_id: source.id)
- assert {:ok, %Channel{}} = MediaSource.delete_channel(channel)
+ assert {:ok, %Source{}} = MediaSource.delete_source(source)
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
end
end
- describe "change_channel/2" do
+ describe "change_source/2" 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
- describe "change_channel_from_url/2" do
+ describe "change_source_from_url/2" do
test "it returns a changeset" do
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
- 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)
- changeset = MediaSource.change_channel_from_url(%Channel{}, %{name: "some updated name"})
+ changeset = MediaSource.change_source_from_url(%Source{}, %{name: "some updated name"})
assert %Ecto.Changeset{} = changeset
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)
changeset =
- MediaSource.change_channel_from_url(%Channel{}, %{
+ MediaSource.change_source_from_url(%Source{}, %{
original_url: "https://www.youtube.com/channel/abc123"
})
assert %Ecto.Changeset{} = changeset
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)
media_profile = media_profile_fixture()
media_profile_id = media_profile.id
changeset =
- MediaSource.change_channel_from_url(%Channel{}, %{
+ MediaSource.change_source_from_url(%Source{}, %{
original_url: "https://www.youtube.com/channel/abc123",
media_profile_id: media_profile.id
})
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 %{
name: "some name",
@@ -292,12 +296,12 @@ defmodule Pinchflat.MediaSourceTest do
end)
changeset =
- MediaSource.change_channel_from_url(%Channel{}, %{
+ MediaSource.change_source_from_url(%Source{}, %{
original_url: "https://www.youtube.com/channel/abc123"
})
assert %Ecto.Changeset{} = changeset
- assert errors_on(changeset).original_url == ["could not fetch channel details from URL"]
+ assert errors_on(changeset).original_url == ["could not fetch source details from URL"]
end
end
@@ -306,7 +310,7 @@ defmodule Pinchflat.MediaSourceTest do
:ok,
Phoenix.json_library().encode!(%{
channel: "some name",
- channel_id: "some_channel_id_#{:rand.uniform(1_000_000)}"
+ channel_id: "some_source_id_#{:rand.uniform(1_000_000)}"
})
}
end
diff --git a/test/pinchflat/media_test.exs b/test/pinchflat/media_test.exs
index d63c77d..13d7b4b 100644
--- a/test/pinchflat/media_test.exs
+++ b/test/pinchflat/media_test.exs
@@ -30,23 +30,23 @@ defmodule Pinchflat.MediaTest do
end
describe "list_pending_media_items_for/1" do
- test "it returns pending media_items for a given channel" do
- channel = channel_fixture()
- media_item = media_item_fixture(%{channel_id: channel.id, media_filepath: nil})
+ test "it returns pending media_items for a given source" do
+ source = source_fixture()
+ media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil})
- assert Media.list_pending_media_items_for(channel) == [media_item]
+ assert Media.list_pending_media_items_for(source) == [media_item]
end
test "it does not return media_items with media_filepath" do
- channel = channel_fixture()
+ source = source_fixture()
_media_item =
media_item_fixture(%{
- channel_id: channel.id,
+ source_id: source.id,
media_filepath: "/video/#{Faker.File.file_name(:video)}"
})
- assert Media.list_pending_media_items_for(channel) == []
+ assert Media.list_pending_media_items_for(source) == []
end
end
@@ -63,7 +63,7 @@ defmodule Pinchflat.MediaTest do
media_id: Faker.String.base64(12),
title: Faker.Commerce.product_name(),
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)
@@ -85,7 +85,7 @@ defmodule Pinchflat.MediaTest do
media_id: Faker.String.base64(12),
title: Faker.Commerce.product_name(),
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)
diff --git a/test/pinchflat/tasks/channel_tasks_test.exs b/test/pinchflat/tasks/channel_tasks_test.exs
deleted file mode 100644
index 2314523..0000000
--- a/test/pinchflat/tasks/channel_tasks_test.exs
+++ /dev/null
@@ -1,45 +0,0 @@
-defmodule Pinchflat.Tasks.ChannelTasksTest do
- use Pinchflat.DataCase
-
- import Pinchflat.TasksFixtures
- import Pinchflat.MediaSourceFixtures
-
- alias Pinchflat.Tasks.Task
- alias Pinchflat.Tasks.ChannelTasks
- alias Pinchflat.Workers.MediaIndexingWorker
-
- describe "kickoff_indexing_task/1" do
- test "it does not schedule a job if the interval is <= 0" do
- channel = channel_fixture(index_frequency_minutes: -1)
-
- assert {:ok, :should_not_index} = ChannelTasks.kickoff_indexing_task(channel)
-
- refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
- end
-
- test "it schedules a job if the interval is > 0" do
- channel = channel_fixture(index_frequency_minutes: 1)
-
- assert {:ok, _} = ChannelTasks.kickoff_indexing_task(channel)
-
- assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
- end
-
- test "it creates and attaches a task if the interval is > 0" do
- channel = channel_fixture(index_frequency_minutes: 1)
-
- assert {:ok, %Task{} = task} = ChannelTasks.kickoff_indexing_task(channel)
-
- assert task.channel_id == channel.id
- end
-
- test "it deletes any pending tasks for the channel" do
- channel = channel_fixture()
- task = task_fixture(channel_id: channel.id)
-
- assert {:ok, _} = ChannelTasks.kickoff_indexing_task(channel)
-
- assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
- end
- end
-end
diff --git a/test/pinchflat/tasks/source_tasks_test.exs b/test/pinchflat/tasks/source_tasks_test.exs
new file mode 100644
index 0000000..1e9c38f
--- /dev/null
+++ b/test/pinchflat/tasks/source_tasks_test.exs
@@ -0,0 +1,45 @@
+defmodule Pinchflat.Tasks.SourceTasksTest do
+ use Pinchflat.DataCase
+
+ import Pinchflat.TasksFixtures
+ import Pinchflat.MediaSourceFixtures
+
+ alias Pinchflat.Tasks.Task
+ alias Pinchflat.Tasks.SourceTasks
+ alias Pinchflat.Workers.MediaIndexingWorker
+
+ describe "kickoff_indexing_task/1" do
+ test "it does not schedule a job if the interval is <= 0" do
+ source = source_fixture(index_frequency_minutes: -1)
+
+ assert {:ok, :should_not_index} = SourceTasks.kickoff_indexing_task(source)
+
+ refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
+ end
+
+ test "it schedules a job if the interval is > 0" do
+ source = source_fixture(index_frequency_minutes: 1)
+
+ assert {:ok, _} = SourceTasks.kickoff_indexing_task(source)
+
+ assert_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
+ end
+
+ test "it creates and attaches a task if the interval is > 0" do
+ source = source_fixture(index_frequency_minutes: 1)
+
+ assert {:ok, %Task{} = task} = SourceTasks.kickoff_indexing_task(source)
+
+ assert task.source_id == source.id
+ end
+
+ test "it deletes any pending tasks for the source" do
+ source = source_fixture()
+ task = task_fixture(source_id: source.id)
+
+ assert {:ok, _} = SourceTasks.kickoff_indexing_task(source)
+
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
+ end
+ end
+end
diff --git a/test/pinchflat/tasks_test.exs b/test/pinchflat/tasks_test.exs
index fbcc670..d89c5b0 100644
--- a/test/pinchflat/tasks_test.exs
+++ b/test/pinchflat/tasks_test.exs
@@ -21,11 +21,11 @@ defmodule Pinchflat.TasksTest do
end
test "it does not delete the other record when a job gets deleted" do
- task = Repo.preload(task_fixture(), [:channel, :job])
+ task = Repo.preload(task_fixture(), [:source, :job])
{:ok, _} = Repo.delete(task.job)
- assert Repo.reload!(task.channel)
+ assert Repo.reload!(task.source)
end
end
@@ -40,14 +40,14 @@ defmodule Pinchflat.TasksTest do
test "it lets you specify which record type/ID to join on" do
task = task_fixture()
- assert Tasks.list_tasks_for(:channel_id, task.channel_id) == [task]
+ assert Tasks.list_tasks_for(:source_id, task.source_id) == [task]
end
test "it lets you specify which job states to include" do
task = task_fixture()
- assert Tasks.list_tasks_for(:channel_id, task.channel_id, [:available]) == [task]
- assert Tasks.list_tasks_for(:channel_id, task.channel_id, [:cancelled]) == []
+ assert Tasks.list_tasks_for(:source_id, task.source_id, [:available]) == [task]
+ assert Tasks.list_tasks_for(:source_id, task.source_id, [:cancelled]) == []
end
end
@@ -55,14 +55,14 @@ defmodule Pinchflat.TasksTest do
test "it lists pending tasks" do
task = task_fixture()
- assert Tasks.list_pending_tasks_for(:channel_id, task.channel_id) == [task]
+ assert Tasks.list_pending_tasks_for(:source_id, task.source_id) == [task]
end
test "it does not list non-pending tasks" do
task = Repo.preload(task_fixture(), :job)
:ok = Oban.cancel_job(task.job)
- assert Tasks.list_pending_tasks_for(:channel_id, task.channel_id) == []
+ assert Tasks.list_pending_tasks_for(:source_id, task.source_id) == []
end
end
@@ -84,14 +84,14 @@ defmodule Pinchflat.TasksTest do
assert {:error, %Ecto.Changeset{}} = Tasks.create_task(@invalid_attrs)
end
- test "accepts a job and channel" do
+ test "accepts a job and source" do
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, source)
assert task.job_id == job.id
- assert task.channel_id == channel.id
+ assert task.source_id == source.id
end
test "accepts a job and media item" do
@@ -115,25 +115,25 @@ defmodule Pinchflat.TasksTest do
end
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(%{}), source)
- assert task.channel_id == channel.id
+ assert task.source_id == source.id
end
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])
- assert {:ok, %Task{}} = Tasks.create_job_with_task(job, channel)
- assert {:error, :duplicate_job} = Tasks.create_job_with_task(job, channel)
+ assert {:ok, %Task{}} = Tasks.create_job_with_task(job, source)
+ assert {:error, :duplicate_job} = Tasks.create_job_with_task(job, source)
end
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{}, source)
end
end
@@ -155,11 +155,11 @@ defmodule Pinchflat.TasksTest do
end
describe "delete_tasks_for/1" do
- test "it deletes tasks attached to a channel" do
- channel = channel_fixture()
- task = task_fixture(channel_id: channel.id)
+ test "it deletes tasks attached to a source" do
+ source = source_fixture()
+ task = task_fixture(source_id: source.id)
- assert :ok = Tasks.delete_tasks_for(channel)
+ assert :ok = Tasks.delete_tasks_for(source)
assert_raise Ecto.NoResultsError, fn -> Tasks.get_task!(task.id) end
end
@@ -173,20 +173,20 @@ defmodule Pinchflat.TasksTest do
end
describe "delete_pending_tasks_for/1" do
- test "it deletes pending tasks attached to a channel" do
- channel = channel_fixture()
- task = task_fixture(channel_id: channel.id)
+ test "it deletes pending tasks attached to a source" do
+ source = source_fixture()
+ task = task_fixture(source_id: source.id)
- assert :ok = Tasks.delete_pending_tasks_for(channel)
+ assert :ok = Tasks.delete_pending_tasks_for(source)
assert_raise Ecto.NoResultsError, fn -> Tasks.get_task!(task.id) end
end
test "it does not delete non-pending tasks" do
- channel = channel_fixture()
- task = Repo.preload(task_fixture(channel_id: channel.id), :job)
+ source = source_fixture()
+ task = Repo.preload(task_fixture(source_id: source.id), :job)
:ok = Oban.cancel_job(task.job)
- assert :ok = Tasks.delete_pending_tasks_for(channel)
+ assert :ok = Tasks.delete_pending_tasks_for(source)
assert Tasks.get_task!(task.id)
end
diff --git a/test/pinchflat/workers/media_indexing_worker_test.exs b/test/pinchflat/workers/media_indexing_worker_test.exs
index dd044c5..7a9a87b 100644
--- a/test/pinchflat/workers/media_indexing_worker_test.exs
+++ b/test/pinchflat/workers/media_indexing_worker_test.exs
@@ -12,36 +12,36 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
setup :verify_on_exit!
describe "perform/1" 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 source shouldn't be indexed" do
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: source.id})
end
- test "it does not reschedule if the channel shouldn't be indexed" do
+ test "it does not reschedule if the source shouldn't be indexed" do
expect(YtDlpRunnerMock, :run, 0, fn _url, _opts, _ot -> {:ok, ""} end)
- channel = channel_fixture(index_frequency_minutes: -1)
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ source = source_fixture(index_frequency_minutes: -1)
+ perform_job(MediaIndexingWorker, %{id: source.id})
- refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => channel.id})
+ refute_enqueued(worker: MediaIndexingWorker, args: %{"id" => source.id})
end
- test "it indexes the channel if it should be indexed" do
+ test "it indexes the source if it should be indexed" do
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: source.id})
end
test "it kicks off a download job for each pending media item" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1"} end)
- channel = channel_fixture(index_frequency_minutes: 10)
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ source = source_fixture(index_frequency_minutes: 10)
+ perform_job(MediaIndexingWorker, %{id: source.id})
assert [_] = all_enqueued(worker: VideoDownloadWorker)
end
@@ -49,9 +49,9 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest 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)
- channel = channel_fixture(index_frequency_minutes: 10)
- media_item_fixture(%{channel_id: channel.id, media_filepath: nil})
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ source = source_fixture(index_frequency_minutes: 10)
+ media_item_fixture(%{source_id: source.id, media_filepath: nil})
+ perform_job(MediaIndexingWorker, %{id: source.id})
assert [_, _] = all_enqueued(worker: VideoDownloadWorker)
end
@@ -59,8 +59,8 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest 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)
- channel = channel_fixture(index_frequency_minutes: 10)
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ source = source_fixture(index_frequency_minutes: 10)
+ perform_job(MediaIndexingWorker, %{id: source.id})
# Only one job should be enqueued, since the second video is a duplicate
assert [_] = all_enqueued(worker: VideoDownloadWorker)
@@ -69,41 +69,41 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
test "it reschedules the job based on the index frequency" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
- channel = channel_fixture(index_frequency_minutes: 10)
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ source = source_fixture(index_frequency_minutes: 10)
+ perform_job(MediaIndexingWorker, %{id: source.id})
assert_enqueued(
worker: MediaIndexingWorker,
- args: %{"id" => channel.id},
- scheduled_at: now_plus(channel.index_frequency_minutes, :minutes)
+ args: %{"id" => source.id},
+ scheduled_at: now_plus(source.index_frequency_minutes, :minutes)
)
end
test "it creates a task for the rescheduled job" do
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
assert_changed([from: 0, to: 1], task_count_fetcher, fn ->
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ perform_job(MediaIndexingWorker, %{id: source.id})
end)
end
test "it creates the basic media_item records" do
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 ->
- channel
+ source
|> Repo.preload(:media_items)
|> Map.get(:media_items)
|> Enum.map(fn media_item -> media_item.media_id end)
end
assert_changed([from: [], to: ["video1", "video2"]], media_item_fetcher, fn ->
- perform_job(MediaIndexingWorker, %{id: channel.id})
+ perform_job(MediaIndexingWorker, %{id: source.id})
end)
end
end
diff --git a/test/pinchflat/workers/video_download_worker_test.exs b/test/pinchflat/workers/video_download_worker_test.exs
index da4cb66..3a515f9 100644
--- a/test/pinchflat/workers/video_download_worker_test.exs
+++ b/test/pinchflat/workers/video_download_worker_test.exs
@@ -12,7 +12,7 @@ defmodule Pinchflat.Workers.VideoDownloadWorkerTest do
media_item =
Repo.preload(
media_item_fixture(%{media_filepath: nil}),
- [:metadata, channel: :media_profile]
+ [:metadata, source: :media_profile]
)
{:ok, %{media_item: media_item}}
diff --git a/test/pinchflat_web/controllers/channel_controller_test.exs b/test/pinchflat_web/controllers/channel_controller_test.exs
deleted file mode 100644
index 5e71f11..0000000
--- a/test/pinchflat_web/controllers/channel_controller_test.exs
+++ /dev/null
@@ -1,119 +0,0 @@
-defmodule PinchflatWeb.ChannelControllerTest do
- use PinchflatWeb.ConnCase
- import Mox
-
- import Pinchflat.ProfilesFixtures
- import Pinchflat.MediaSourceFixtures
-
- setup do
- media_profile = media_profile_fixture()
-
- {
- :ok,
- %{
- create_attrs: %{
- media_profile_id: media_profile.id,
- original_url: "https://www.youtube.com/channel/abc123"
- },
- update_attrs: %{
- original_url: "https://www.youtube.com/channel/321xyz"
- },
- invalid_attrs: %{original_url: nil, media_profile_id: nil}
- }
- }
- end
-
- setup :verify_on_exit!
-
- describe "index" do
- test "lists all channels", %{conn: conn} do
- conn = get(conn, ~p"/media_sources/channels")
- assert html_response(conn, 200) =~ "Listing Channels"
- end
- end
-
- describe "new channel" do
- test "renders form", %{conn: conn} do
- conn = get(conn, ~p"/media_sources/channels/new")
- assert html_response(conn, 200) =~ "New Channel"
- end
- end
-
- describe "create channel" do
- test "redirects to show when data is valid", %{conn: conn, create_attrs: create_attrs} do
- expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
- conn = post(conn, ~p"/media_sources/channels", channel: create_attrs)
-
- assert %{id: id} = redirected_params(conn)
- assert redirected_to(conn) == ~p"/media_sources/channels/#{id}"
-
- conn = get(conn, ~p"/media_sources/channels/#{id}")
- assert html_response(conn, 200) =~ "Channel #{id}"
- end
-
- test "renders errors when data is invalid", %{conn: conn, invalid_attrs: invalid_attrs} do
- conn = post(conn, ~p"/media_sources/channels", channel: invalid_attrs)
- assert html_response(conn, 200) =~ "New Channel"
- end
- end
-
- describe "edit channel" do
- setup [:create_channel]
-
- test "renders form for editing chosen channel", %{conn: conn, channel: channel} do
- conn = get(conn, ~p"/media_sources/channels/#{channel}/edit")
- assert html_response(conn, 200) =~ "Edit Channel"
- end
- end
-
- describe "update channel" do
- setup [:create_channel]
-
- test "redirects when data is valid", %{conn: conn, channel: channel, update_attrs: update_attrs} do
- expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
-
- conn = put(conn, ~p"/media_sources/channels/#{channel}", channel: update_attrs)
- assert redirected_to(conn) == ~p"/media_sources/channels/#{channel}"
-
- conn = get(conn, ~p"/media_sources/channels/#{channel}")
- assert html_response(conn, 200) =~ "https://www.youtube.com/channel/321xyz"
- end
-
- test "renders errors when data is invalid", %{
- conn: conn,
- channel: channel,
- invalid_attrs: invalid_attrs
- } do
- conn = put(conn, ~p"/media_sources/channels/#{channel}", channel: invalid_attrs)
- assert html_response(conn, 200) =~ "Edit Channel"
- end
- end
-
- describe "delete channel" do
- setup [:create_channel]
-
- test "deletes chosen channel", %{conn: conn, channel: channel} do
- conn = delete(conn, ~p"/media_sources/channels/#{channel}")
- assert redirected_to(conn) == ~p"/media_sources/channels"
-
- assert_error_sent 404, fn ->
- get(conn, ~p"/media_sources/channels/#{channel}")
- end
- end
- end
-
- defp create_channel(_) do
- channel = channel_fixture()
- %{channel: channel}
- end
-
- defp runner_function_mock(_url, _opts, _ot) do
- {
- :ok,
- Phoenix.json_library().encode!(%{
- channel: "some name",
- channel_id: "some_channel_id_#{:rand.uniform(1_000_000)}"
- })
- }
- end
-end
diff --git a/test/pinchflat_web/controllers/source_controller_test.exs b/test/pinchflat_web/controllers/source_controller_test.exs
new file mode 100644
index 0000000..f973e8d
--- /dev/null
+++ b/test/pinchflat_web/controllers/source_controller_test.exs
@@ -0,0 +1,120 @@
+defmodule PinchflatWeb.SourceControllerTest do
+ use PinchflatWeb.ConnCase
+ import Mox
+
+ import Pinchflat.ProfilesFixtures
+ import Pinchflat.MediaSourceFixtures
+
+ setup do
+ media_profile = media_profile_fixture()
+
+ {
+ :ok,
+ %{
+ create_attrs: %{
+ media_profile_id: media_profile.id,
+ collection_type: "channel",
+ original_url: "https://www.youtube.com/source/abc123"
+ },
+ update_attrs: %{
+ original_url: "https://www.youtube.com/source/321xyz"
+ },
+ invalid_attrs: %{original_url: nil, media_profile_id: nil}
+ }
+ }
+ end
+
+ setup :verify_on_exit!
+
+ describe "index" do
+ test "lists all sources", %{conn: conn} do
+ conn = get(conn, ~p"/media_sources/sources")
+ assert html_response(conn, 200) =~ "Listing Sources"
+ end
+ end
+
+ describe "new source" do
+ test "renders form", %{conn: conn} do
+ conn = get(conn, ~p"/media_sources/sources/new")
+ assert html_response(conn, 200) =~ "New Source"
+ end
+ end
+
+ describe "create source" do
+ test "redirects to show when data is valid", %{conn: conn, create_attrs: create_attrs} do
+ expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
+ conn = post(conn, ~p"/media_sources/sources", source: create_attrs)
+
+ assert %{id: id} = redirected_params(conn)
+ assert redirected_to(conn) == ~p"/media_sources/sources/#{id}"
+
+ conn = get(conn, ~p"/media_sources/sources/#{id}")
+ assert html_response(conn, 200) =~ "Source #{id}"
+ end
+
+ test "renders errors when data is invalid", %{conn: conn, invalid_attrs: invalid_attrs} do
+ conn = post(conn, ~p"/media_sources/sources", source: invalid_attrs)
+ assert html_response(conn, 200) =~ "New Source"
+ end
+ end
+
+ describe "edit source" do
+ setup [:create_source]
+
+ test "renders form for editing chosen source", %{conn: conn, source: source} do
+ conn = get(conn, ~p"/media_sources/sources/#{source}/edit")
+ assert html_response(conn, 200) =~ "Edit Source"
+ end
+ end
+
+ describe "update source" do
+ setup [:create_source]
+
+ test "redirects when data is valid", %{conn: conn, source: source, update_attrs: update_attrs} do
+ expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
+
+ conn = put(conn, ~p"/media_sources/sources/#{source}", source: update_attrs)
+ assert redirected_to(conn) == ~p"/media_sources/sources/#{source}"
+
+ conn = get(conn, ~p"/media_sources/sources/#{source}")
+ assert html_response(conn, 200) =~ "https://www.youtube.com/source/321xyz"
+ end
+
+ test "renders errors when data is invalid", %{
+ conn: conn,
+ source: source,
+ invalid_attrs: invalid_attrs
+ } do
+ conn = put(conn, ~p"/media_sources/sources/#{source}", source: invalid_attrs)
+ assert html_response(conn, 200) =~ "Edit Source"
+ end
+ end
+
+ describe "delete source" do
+ setup [:create_source]
+
+ test "deletes chosen source", %{conn: conn, source: source} do
+ conn = delete(conn, ~p"/media_sources/sources/#{source}")
+ assert redirected_to(conn) == ~p"/media_sources/sources"
+
+ assert_error_sent 404, fn ->
+ get(conn, ~p"/media_sources/sources/#{source}")
+ end
+ end
+ end
+
+ defp create_source(_) do
+ source = source_fixture()
+ %{source: source}
+ end
+
+ defp runner_function_mock(_url, _opts, _ot) do
+ {
+ :ok,
+ Phoenix.json_library().encode!(%{
+ channel: "some name",
+ channel_id: "some_source_id_#{:rand.uniform(1_000_000)}"
+ })
+ }
+ end
+end
diff --git a/test/support/fixtures/media_fixtures.ex b/test/support/fixtures/media_fixtures.ex
index 6d0d727..5f75cc4 100644
--- a/test/support/fixtures/media_fixtures.ex
+++ b/test/support/fixtures/media_fixtures.ex
@@ -16,7 +16,7 @@ defmodule Pinchflat.MediaFixtures do
media_id: Faker.String.base64(12),
title: Faker.Commerce.product_name(),
media_filepath: "/video/#{Faker.File.file_name(:video)}",
- channel_id: MediaSourceFixtures.channel_fixture().id
+ source_id: MediaSourceFixtures.source_fixture().id
})
|> Pinchflat.Media.create_media_item()
diff --git a/test/support/fixtures/media_source_fixtures.ex b/test/support/fixtures/media_source_fixtures.ex
index 007c9d4..178ccc9 100644
--- a/test/support/fixtures/media_source_fixtures.ex
+++ b/test/support/fixtures/media_source_fixtures.ex
@@ -6,18 +6,19 @@ defmodule Pinchflat.MediaSourceFixtures do
alias Pinchflat.Repo
alias Pinchflat.ProfilesFixtures
- alias Pinchflat.MediaSource.Channel
+ alias Pinchflat.MediaSource.Source
@doc """
- Generate a channel.
+ Generate a source.
"""
- def channel_fixture(attrs \\ %{}) do
- {:ok, channel} =
- %Channel{}
- |> Channel.changeset(
+ def source_fixture(attrs \\ %{}) do
+ {:ok, source} =
+ %Source{}
+ |> Source.changeset(
Enum.into(attrs, %{
- name: "Channel ##{:rand.uniform(1_000_000)}",
- channel_id: Base.encode16(:crypto.hash(:md5, "#{:rand.uniform(1_000_000)}")),
+ name: "Source ##{: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)}",
media_profile_id: ProfilesFixtures.media_profile_fixture().id,
index_frequency_minutes: 60
@@ -25,6 +26,6 @@ defmodule Pinchflat.MediaSourceFixtures do
)
|> Repo.insert()
- channel
+ source
end
end
diff --git a/test/support/fixtures/tasks_fixtures.ex b/test/support/fixtures/tasks_fixtures.ex
index c310ea6..7bb1121 100644
--- a/test/support/fixtures/tasks_fixtures.ex
+++ b/test/support/fixtures/tasks_fixtures.ex
@@ -14,7 +14,7 @@ defmodule Pinchflat.TasksFixtures do
{:ok, task} =
attrs
|> Enum.into(%{
- channel_id: MediaSourceFixtures.channel_fixture().id,
+ source_id: MediaSourceFixtures.source_fixture().id,
job_id: JobFixtures.job_fixture().id
})
|> Pinchflat.Tasks.create_task()