Hooked up collection_type to form; refactored enqueue_pending_media_downloads

This commit is contained in:
Kieran Eglin 2024-02-02 12:44:00 -08:00
parent 77b8a3e56f
commit 32bcf38d0e
No known key found for this signature in database
GPG key ID: 193984967FCF432D
8 changed files with 89 additions and 23 deletions

View file

@ -2,6 +2,7 @@ alias Pinchflat.Repo
alias Pinchflat.Tasks.Task alias Pinchflat.Tasks.Task
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Media.MediaMetadata alias Pinchflat.Media.MediaMetadata
alias Pinchflat.MediaSource.Source alias Pinchflat.MediaSource.Source
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile

View file

@ -100,6 +100,8 @@ defmodule Pinchflat.MediaSource do
Note that this fetches source 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 This means that it'll go for it even if a changeset is otherwise invalid. This
is pretty easy to change, but for MVP I'm not concerned. is pretty easy to change, but for MVP I'm not concerned.
IDEA: Maybe I could discern `collection_type` based on the original URL?
""" """
def change_source_from_url(%Source{} = source, attrs) do def change_source_from_url(%Source{} = source, attrs) do
case change_source(source, attrs) do case change_source(source, attrs) do

View file

@ -9,7 +9,16 @@ defmodule Pinchflat.MediaSource.Source do
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
@allowed_fields ~w(collection_name collection_id collection_type index_frequency_minutes original_url media_profile_id)a @allowed_fields ~w(
collection_name
collection_id
collection_type
friendly_name
index_frequency_minutes
original_url
media_profile_id
)a
@required_fields @allowed_fields -- ~w(index_frequency_minutes friendly_name)a @required_fields @allowed_fields -- ~w(index_frequency_minutes friendly_name)a
schema "sources" do schema "sources" do
@ -17,7 +26,7 @@ defmodule Pinchflat.MediaSource.Source do
field :collection_name, :string field :collection_name, :string
field :collection_id, :string field :collection_id, :string
field :collection_type, Ecto.Enum, values: [:channel, :playlist] field :collection_type, Ecto.Enum, values: [:channel, :playlist]
field :index_frequency_minutes, :integer field :index_frequency_minutes, :integer, default: 60 * 24
# This should only be used for user reference going forward # This should only be used for user reference going forward
# as the collection_id should be used for all API calls # as the collection_id should be used for all API calls
field :original_url, :string field :original_url, :string

View file

@ -3,9 +3,11 @@ defmodule Pinchflat.Tasks.SourceTasks do
This module contains methods for managing tasks (workers) related to sources. This module contains methods for managing tasks (workers) related to sources.
""" """
alias Pinchflat.Media
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.MediaSource.Source alias Pinchflat.MediaSource.Source
alias Pinchflat.Workers.MediaIndexingWorker alias Pinchflat.Workers.MediaIndexingWorker
alias Pinchflat.Workers.VideoDownloadWorker
@doc """ @doc """
Starts tasks for indexing a source's media. Starts tasks for indexing a source's media.
@ -30,4 +32,27 @@ defmodule Pinchflat.Tasks.SourceTasks do
end end
end end
end end
@doc """
Starts tasks for downloading videos for any of a sources _pending_ media items.
NOTE: this starts a download for each media item that is pending,
not just the ones that were indexed in this job run. This should ensure
that any stragglers are caught if, for some reason, they weren't enqueued
or somehow got de-queued.
I'm not sure of a case where this would happen, but it's cheap insurance.
Returns :ok
"""
def enqueue_pending_media_downloads(%Source{} = source) do
source
|> Media.list_pending_media_items_for()
|> Enum.each(fn media_item ->
media_item
|> Map.take([:id])
|> VideoDownloadWorker.new()
|> Tasks.create_job_with_task(media_item)
end)
end
end end

View file

@ -7,10 +7,9 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
tags: ["media_source", "media_indexing"] tags: ["media_source", "media_indexing"]
alias __MODULE__ alias __MODULE__
alias Pinchflat.Media
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.MediaSource alias Pinchflat.MediaSource
alias Pinchflat.Workers.VideoDownloadWorker alias Pinchflat.Tasks.SourceTasks
@impl Oban.Worker @impl Oban.Worker
@doc """ @doc """
@ -49,7 +48,7 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
defp index_media_and_reschedule(source) do defp index_media_and_reschedule(source) do
MediaSource.index_media_items(source) MediaSource.index_media_items(source)
enqueue_video_downloads(source) SourceTasks.enqueue_pending_media_downloads(source)
source source
|> Map.take([:id]) |> Map.take([:id])
@ -60,21 +59,4 @@ defmodule Pinchflat.Workers.MediaIndexingWorker do
{:error, :duplicate_job} -> {:ok, :job_exists} {:error, :duplicate_job} -> {:ok, :job_exists}
end end
end end
# NOTE: this starts a download for each media item that is pending,
# not just the ones that were indexed in this job run. This should ensure
# that any stragglers are caught if, for some reason, they weren't enqueued
# 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(source) do
source
|> Media.list_pending_media_items_for()
|> Enum.each(fn media_item ->
media_item
|> Map.take([:id])
|> VideoDownloadWorker.new()
|> Tasks.create_job_with_task(media_item)
end)
end
end end

View file

@ -24,4 +24,11 @@ defmodule PinchflatWeb.MediaSources.SourceHTML do
{"Monthly", 30 * 24 * 60} {"Monthly", 30 * 24 * 60}
] ]
end end
def friendly_collection_types do
[
{"Channel", "channel"},
{"Playlist", "playlist"}
]
end
end end

View file

@ -10,7 +10,13 @@
label="Media Profile" label="Media Profile"
/> />
<.input field={f[:collection_type]} type="text" label="Collection Type" /> <.input
field={f[:collection_type]}
options={friendly_collection_types()}
type="select"
label="Collection Type"
/>
<.input field={f[:original_url]} type="text" label="Source URL" /> <.input field={f[:original_url]} type="text" label="Source URL" />
<.input <.input

View file

@ -2,11 +2,14 @@ defmodule Pinchflat.Tasks.SourceTasksTest do
use Pinchflat.DataCase use Pinchflat.DataCase
import Pinchflat.TasksFixtures import Pinchflat.TasksFixtures
import Pinchflat.MediaFixtures
import Pinchflat.MediaSourceFixtures import Pinchflat.MediaSourceFixtures
alias Pinchflat.Tasks
alias Pinchflat.Tasks.Task alias Pinchflat.Tasks.Task
alias Pinchflat.Tasks.SourceTasks alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Workers.MediaIndexingWorker alias Pinchflat.Workers.MediaIndexingWorker
alias Pinchflat.Workers.VideoDownloadWorker
describe "kickoff_indexing_task/1" do describe "kickoff_indexing_task/1" do
test "it does not schedule a job if the interval is <= 0" do test "it does not schedule a job if the interval is <= 0" do
@ -42,4 +45,35 @@ defmodule Pinchflat.Tasks.SourceTasksTest do
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
end end
end end
describe "enqueue_pending_media_downloads/1" do
test "it enqueues a job for each pending media item" do
source = source_fixture()
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
assert :ok = SourceTasks.enqueue_pending_media_downloads(source)
assert_enqueued(worker: VideoDownloadWorker, args: %{"id" => media_item.id})
end
test "it does not enqueue a job for media items with a filepath" do
source = source_fixture()
_media_item = media_item_fixture(source_id: source.id, media_filepath: "some/filepath.mp4")
assert :ok = SourceTasks.enqueue_pending_media_downloads(source)
refute_enqueued(worker: VideoDownloadWorker)
end
test "it attaches a task to each enqueued job" do
source = source_fixture()
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
assert [] = Tasks.list_tasks_for(:media_item_id, media_item.id)
assert :ok = SourceTasks.enqueue_pending_media_downloads(source)
assert [_] = Tasks.list_tasks_for(:media_item_id, media_item.id)
end
end
end end