Hooked up video downloading to the channel indexing pipeline

This commit is contained in:
Kieran Eglin 2024-01-29 20:36:59 -08:00
parent 95a766f3d4
commit a0d62a156a
No known key found for this signature in database
GPG key ID: 193984967FCF432D
3 changed files with 47 additions and 9 deletions

View file

@ -4,8 +4,8 @@ defmodule Pinchflat.Media do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Pinchflat.Repo
alias Pinchflat.Repo
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
@doc """ @doc """

View file

@ -9,12 +9,17 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
alias __MODULE__ alias __MODULE__
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.MediaSource alias Pinchflat.MediaSource
alias Pinchflat.Media.MediaItem
alias Pinchflat.Workers.VideoDownloadWorker
@impl Oban.Worker @impl Oban.Worker
@doc """ @doc """
The ID is that of a channel _record_, not a YouTube channel ID. 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
reschedules the job to run again in the future (as determined by the
channel's `index_frequency_minutes` field).
NOTE: Re-scheduling here works a little different than you may expect. 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_. The reschedule time is relative to the time the job has actually _completed_.
This has some benefits but also side effects to be aware of: This has some benefits but also side effects to be aware of:
@ -30,7 +35,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
IDEA: Should I use paging and do indexing in chunks? Is that even faster? IDEA: Should I use paging and do indexing in chunks? Is that even faster?
Returns :ok | {:ok, %Task{}}. Not that it matters. Returns :ok | {:ok, %Task{}}
""" """
def perform(%Oban.Job{args: %{"id" => channel_id}}) do def perform(%Oban.Job{args: %{"id" => channel_id}}) do
channel = MediaSource.get_channel!(channel_id) channel = MediaSource.get_channel!(channel_id)
@ -43,7 +48,20 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
end end
defp index_media_and_reschedule(channel) do defp index_media_and_reschedule(channel) do
MediaSource.index_media_items(channel) channel
|> MediaSource.index_media_items()
|> Enum.each(fn media_item_or_changeset ->
case media_item_or_changeset do
%MediaItem{} = media_item ->
media_item
|> Map.take([:id])
|> VideoDownloadWorker.new()
|> Oban.insert()
_ ->
nil
end
end)
channel channel
|> Map.take([:id]) |> Map.take([:id])

View file

@ -6,6 +6,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Workers.MediaIndexingWorker alias Pinchflat.Workers.MediaIndexingWorker
alias Pinchflat.Workers.VideoDownloadWorker
setup :verify_on_exit! setup :verify_on_exit!
@ -28,15 +29,34 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
end end
test "it indexes the channel if it should be indexed" do test "it indexes the channel if it should be indexed" do
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: 10) channel = channel_fixture(index_frequency_minutes: 10)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
end end
test "it kicks off a download job for each new 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})
assert [_] = all_enqueued(worker: VideoDownloadWorker)
end
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})
# Only one job should be enqueued, since the second video is a duplicate
assert [_] = all_enqueued(worker: VideoDownloadWorker)
end
test "it reschedules the job based on the index frequency" do test "it reschedules the job based on the index frequency" do
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: 10) channel = channel_fixture(index_frequency_minutes: 10)
perform_job(MediaIndexingWorker, %{id: channel.id}) perform_job(MediaIndexingWorker, %{id: channel.id})
@ -49,7 +69,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
end end
test "it creates a task for the rescheduled job" do test "it creates a task for the rescheduled job" do
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot -> {:ok, ""} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
channel = channel_fixture(index_frequency_minutes: 10) channel = channel_fixture(index_frequency_minutes: 10)
task_count_fetcher = fn -> Enum.count(Tasks.list_tasks()) end task_count_fetcher = fn -> Enum.count(Tasks.list_tasks()) end
@ -60,7 +80,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorkerTest do
end end
test "it creates the basic media_item records" do test "it creates the basic media_item records" do
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot -> {:ok, "video1\nvideo2"} end) expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "video1\nvideo2"} end)
channel = channel_fixture(index_frequency_minutes: 10) channel = channel_fixture(index_frequency_minutes: 10)