Skip to content

Commit

Permalink
More renaming; renamed channel details module
Browse files Browse the repository at this point in the history
  • Loading branch information
kieraneglin committed Feb 2, 2024
1 parent d822aaa commit 2250695
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .iex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ alias Pinchflat.Media
alias Pinchflat.Profiles
alias Pinchflat.MediaSource

alias Pinchflat.MediaClient.{ChannelDetails, VideoDownloader}
alias Pinchflat.MediaClient.{SourceDetails, VideoDownloader}
2 changes: 1 addition & 1 deletion lib/pinchflat/media.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ 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{}, ...].
Expand Down
6 changes: 3 additions & 3 deletions lib/pinchflat/media_client/backends/yt_dlp/channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.Channel do
"""

use Pinchflat.MediaClient.Backends.YtDlp.VideoCollection
alias Pinchflat.MediaClient.ChannelDetails
alias Pinchflat.MediaClient.SourceDetails

@doc """
Gets a channel's ID and name from its URL.
Expand All @@ -13,14 +13,14 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.Channel do
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, ...}.
Returns {:ok, %SourceDetails{}} | {:error, any, ...}.
"""
def get_source_details(channel_url) do
opts = [:skip_download, 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"])}
{:ok, SourceDetails.new(parsed_json["channel_id"], parsed_json["channel"])}
else
err -> err
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Pinchflat.MediaClient.ChannelDetails do
defmodule Pinchflat.MediaClient.SourceDetails do
@moduledoc """
This is the integration layer for actually working with channels.
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).
Expand All @@ -16,24 +16,24 @@ defmodule Pinchflat.MediaClient.ChannelDetails do
end

@doc """
Gets a channel's ID and name from its URL, using the given backend.
Gets a source's ID and name from its URL, using the given backend.
Returns {:ok, map()} | {:error, any, ...}.
"""
def get_source_details(channel_url, backend \\ :yt_dlp) do
channel_module(backend).get_source_details(channel_url)
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 channel URL, using the given backend.
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(channel_url, backend \\ :yt_dlp) do
channel_module(backend).get_video_ids(channel_url)
def get_video_ids(source_url, backend \\ :yt_dlp) do
source_module(backend).get_video_ids(source_url)
end

defp channel_module(backend) do
defp source_module(backend) do
case backend do
:yt_dlp -> YtDlpChannel
end
Expand Down
72 changes: 36 additions & 36 deletions lib/pinchflat/media_source.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ defmodule Pinchflat.MediaSource do
alias Pinchflat.Media
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.MediaSource.Source
alias Pinchflat.MediaClient.ChannelDetails
alias Pinchflat.MediaClient.SourceDetails

@doc """
Returns the list of channels. Returns [%Source{}, ...]
Returns the list of sources. Returns [%Source{}, ...]
"""
def list_sources do
Repo.all(Source)
end

@doc """
Gets a single channel.
Gets a single source.
Returns %Source{}. Raises `Ecto.NoResultsError` if the Channel does not exist.
"""
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, %Source{}} | {:error, %Ecto.Changeset{}}
Expand All @@ -46,7 +46,7 @@ defmodule Pinchflat.MediaSource do
Returns [%MediaItem{}, ...] | [%Ecto.Changeset{}, ...]
"""
def index_media_items(%Source{} = source) do
{:ok, media_ids} = ChannelDetails.get_video_ids(source.original_url)
{:ok, media_ids} = SourceDetails.get_video_ids(source.original_url)

media_ids
|> Enum.map(fn media_id ->
Expand All @@ -60,103 +60,103 @@ 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 `SourceTasks.kickoff_indexing_task`)
Returns {:ok, %Source{}} | {:error, %Ecto.Changeset{}}
"""
def update_source(%Source{} = channel, attrs) do
channel
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, %Source{}} | {:error, %Ecto.Changeset{}}
"""
def delete_source(%Source{} = 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_source(%Source{} = channel, attrs \\ %{}) do
Source.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_source_from_url(%Source{} = channel, attrs) do
case change_source(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_source_details_to_changeset(channel, changeset)
add_source_details_to_changeset(source, changeset)

changeset ->
changeset
end
end

defp add_source_details_to_changeset(channel, changeset) do
defp add_source_details_to_changeset(source, changeset) do
%Ecto.Changeset{changes: changes} = changeset

case ChannelDetails.get_source_details(changes.original_url) do
{:ok, %ChannelDetails{} = channel_details} ->
case SourceDetails.get_source_details(changes.original_url) do
{:ok, %SourceDetails{} = source_details} ->
change_source(
channel,
source,
Map.merge(changes, %{
name: channel_details.name,
collection_id: channel_details.id
name: source_details.name,
collection_id: source_details.id
})
)

{:error, runner_error, _status_code} ->
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
end

defp commit_and_start_indexing(changeset) do
case Repo.insert_or_update(changeset) do
{:ok, %Source{} = 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}} ->
SourceTasks.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
SourceTasks.kickoff_indexing_task(channel)
SourceTasks.kickoff_indexing_task(source)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pinchflat/tasks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ defmodule Pinchflat.Tasks do
def create_task(%Oban.Job{} = job, attached_record) do
attached_record_attr =
case attached_record do
%Source{} = channel -> %{source_id: channel.id}
%Source{} = source -> %{source_id: source.id}
%MediaItem{} = media_item -> %{media_item_id: media_item.id}
end

Expand Down
6 changes: 3 additions & 3 deletions test/pinchflat/media_client/backends/yt_dlp/channel_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.ChannelTest do
use ExUnit.Case, async: true
import Mox

alias Pinchflat.MediaClient.ChannelDetails
alias Pinchflat.MediaClient.SourceDetails
alias Pinchflat.MediaClient.Backends.YtDlp.Channel

@channel_url "https://www.youtube.com/c/TheUselessTrials"

setup :verify_on_exit!

describe "get_source_details/1" do
test "it returns a %ChannelDetails{} with data on success" 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} = Channel.get_source_details(@channel_url)
assert %ChannelDetails{id: "UCQH2", name: "TheUselessTrials"} = res
assert %SourceDetails{id: "UCQH2", name: "TheUselessTrials"} = res
end

test "it passes the expected args to the backend runner" do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
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"

setup :verify_on_exit!

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

Expand All @@ -24,16 +24,16 @@ defmodule Pinchflat.MediaClient.ChannelDetailsTest do
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end)

assert {:ok, _} = ChannelDetails.get_source_details(@channel_url)
assert {:ok, _} = SourceDetails.get_source_details(@channel_url)
end

test "it returns a struct composed of the returned data" do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
{:ok, "{\"channel\": \"TheUselessTrials\", \"channel_id\": \"UCQH2\"}"}
end)

assert {:ok, res} = ChannelDetails.get_source_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

Expand All @@ -46,15 +46,15 @@ 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
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
{: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
2 changes: 1 addition & 1 deletion test/pinchflat/media_source_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ defmodule Pinchflat.MediaSourceTest do
})

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

Expand Down

0 comments on commit 2250695

Please sign in to comment.