@@ -23,6 +22,11 @@
<:col :let={media_profile} label="Preferred Resolution">
<%= media_profile.preferred_resolution %>
+ <:col :let={media_profile} label="Sources">
+ <.subtle_link href={~p"/media_profiles/#{media_profile.id}/#tab-sources"}>
+ <.localized_number number={media_profile.source_count} />
+
+
<:col :let={media_profile} label="" class="flex justify-end">
<.icon_link href={~p"/media_profiles/#{media_profile.id}/edit"} icon="hero-pencil-square" class="mr-4" />
diff --git a/lib/pinchflat_web/controllers/sources/source_controller.ex b/lib/pinchflat_web/controllers/sources/source_controller.ex
index f8b63b5..678618b 100644
--- a/lib/pinchflat_web/controllers/sources/source_controller.ex
+++ b/lib/pinchflat_web/controllers/sources/source_controller.ex
@@ -1,12 +1,11 @@
defmodule PinchflatWeb.Sources.SourceController do
use PinchflatWeb, :controller
- use Pinchflat.Media.MediaQuery
+ use Pinchflat.Sources.SourcesQuery
alias Pinchflat.Repo
alias Pinchflat.Tasks
alias Pinchflat.Sources
alias Pinchflat.Sources.Source
- alias Pinchflat.Media.MediaItem
alias Pinchflat.Profiles.MediaProfile
alias Pinchflat.Media.FileSyncingWorker
alias Pinchflat.Sources.SourceDeletionWorker
@@ -15,33 +14,7 @@ defmodule PinchflatWeb.Sources.SourceController do
alias Pinchflat.Metadata.SourceMetadataStorageWorker
def index(conn, _params) do
- source_query =
- from s in Source,
- as: :source,
- inner_join: mp in assoc(s, :media_profile),
- where: is_nil(s.marked_for_deletion_at) and is_nil(mp.marked_for_deletion_at),
- preload: [media_profile: mp],
- order_by: [asc: s.custom_name],
- select: map(s, ^Source.__schema__(:fields)),
- select_merge: %{
- downloaded_count:
- subquery(
- from m in MediaItem,
- where: m.source_id == parent_as(:source).id,
- where: ^MediaQuery.downloaded(),
- select: count(m.id)
- ),
- pending_count:
- subquery(
- from m in MediaItem,
- join: s in assoc(m, :source),
- where: m.source_id == parent_as(:source).id,
- where: ^MediaQuery.pending(),
- select: count(m.id)
- )
- }
-
- render(conn, :index, sources: Repo.all(source_query))
+ render(conn, :index)
end
def new(conn, params) do
diff --git a/lib/pinchflat_web/controllers/sources/source_html/index.html.heex b/lib/pinchflat_web/controllers/sources/source_html/index.html.heex
index 5913d30..55cd45a 100644
--- a/lib/pinchflat_web/controllers/sources/source_html/index.html.heex
+++ b/lib/pinchflat_web/controllers/sources/source_html/index.html.heex
@@ -12,32 +12,7 @@
- <.table rows={@sources} table_class="text-black dark:text-white">
- <:col :let={source} label="Name">
- <.subtle_link href={~p"/sources/#{source.id}"}>
- <%= StringUtils.truncate(source.custom_name || source.collection_name, 35) %>
-
-
- <:col :let={source} label="Type"><%= source.collection_type %>
- <:col :let={source} label="Pending"><.localized_number number={source.pending_count} />
- <:col :let={source} label="Downloaded"><.localized_number number={source.downloaded_count} />
- <:col :let={source} label="Retention">
- <%= if source.retention_period_days && source.retention_period_days > 0 do %>
- <.localized_number number={source.retention_period_days} />
- <.pluralize count={source.retention_period_days} word="day" />
- <% else %>
- ∞
- <% end %>
-
- <:col :let={source} label="Media Profile">
- <.subtle_link href={~p"/media_profiles/#{source.media_profile_id}"}>
- <%= source.media_profile.name %>
-
-
- <:col :let={source} label="" class="flex place-content-evenly">
- <.icon_link href={~p"/sources/#{source.id}/edit"} icon="hero-pencil-square" class="mx-1" />
-
-
+ <%= live_render(@conn, PinchflatWeb.Sources.IndexTableLive) %>
diff --git a/lib/pinchflat_web/controllers/sources/source_html/index_table_live.ex b/lib/pinchflat_web/controllers/sources/source_html/index_table_live.ex
new file mode 100644
index 0000000..9c4992d
--- /dev/null
+++ b/lib/pinchflat_web/controllers/sources/source_html/index_table_live.ex
@@ -0,0 +1,103 @@
+defmodule PinchflatWeb.Sources.IndexTableLive do
+ use PinchflatWeb, :live_view
+ use Pinchflat.Media.MediaQuery
+ use Pinchflat.Sources.SourcesQuery
+
+ alias Pinchflat.Repo
+ alias Pinchflat.Sources
+ alias Pinchflat.Sources.Source
+ alias Pinchflat.Media.MediaItem
+
+ def render(assigns) do
+ ~H"""
+ <.table rows={@sources} table_class="text-white">
+ <:col :let={source} label="Name">
+ <.subtle_link href={~p"/sources/#{source.id}"}>
+ <%= StringUtils.truncate(source.custom_name || source.collection_name, 35) %>
+
+
+ <:col :let={source} label="Pending">
+ <.subtle_link href={~p"/sources/#{source.id}/#tab-pending"}>
+ <.localized_number number={source.pending_count} />
+
+
+ <:col :let={source} label="Downloaded">
+ <.subtle_link href={~p"/sources/#{source.id}/#tab-downloaded"}>
+ <.localized_number number={source.downloaded_count} />
+
+
+ <:col :let={source} label="Retention">
+ <%= if source.retention_period_days && source.retention_period_days > 0 do %>
+ <.localized_number number={source.retention_period_days} />
+ <.pluralize count={source.retention_period_days} word="day" />
+ <% else %>
+
∞
+ <% end %>
+
+ <:col :let={source} label="Media Profile">
+ <.subtle_link href={~p"/media_profiles/#{source.media_profile_id}"}>
+ <%= source.media_profile.name %>
+
+
+ <:col :let={source} label="Enabled?">
+ <.input
+ name={"source[#{source.id}][enabled]"}
+ value={source.enabled}
+ id={"source_#{source.id}_enabled"}
+ phx-hook="formless-input"
+ data-subscribe="change"
+ data-event-name="toggle_enabled"
+ data-identifier={source.id}
+ type="toggle"
+ />
+
+ <:col :let={source} label="" class="flex place-content-evenly">
+ <.icon_link href={~p"/sources/#{source.id}/edit"} icon="hero-pencil-square" class="mx-1" />
+
+
+ """
+ end
+
+ def mount(_params, _session, socket) do
+ {:ok, assign(socket, %{sources: get_sources()})}
+ end
+
+ def handle_event("formless-input", %{"event" => "toggle_enabled"} = params, socket) do
+ source = Sources.get_source!(params["id"])
+ should_enable = params["value"] == "true"
+
+ {:ok, _} = Sources.update_source(source, %{enabled: should_enable})
+
+ {:noreply, assign(socket, %{sources: get_sources()})}
+ end
+
+ defp get_sources do
+ query =
+ from s in Source,
+ as: :source,
+ inner_join: mp in assoc(s, :media_profile),
+ where: is_nil(s.marked_for_deletion_at) and is_nil(mp.marked_for_deletion_at),
+ preload: [media_profile: mp],
+ order_by: [asc: s.custom_name],
+ select: map(s, ^Source.__schema__(:fields)),
+ select_merge: %{
+ downloaded_count:
+ subquery(
+ from m in MediaItem,
+ where: m.source_id == parent_as(:source).id,
+ where: ^MediaQuery.downloaded(),
+ select: count(m.id)
+ ),
+ pending_count:
+ subquery(
+ from m in MediaItem,
+ join: s in assoc(m, :source),
+ where: m.source_id == parent_as(:source).id,
+ where: ^MediaQuery.pending(),
+ select: count(m.id)
+ )
+ }
+
+ Repo.all(query)
+ end
+end
diff --git a/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex b/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex
index fd14a3a..678bf55 100644
--- a/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex
+++ b/lib/pinchflat_web/controllers/sources/source_html/media_item_table_live.ex
@@ -1,4 +1,4 @@
-defmodule Pinchflat.Sources.MediaItemTableLive do
+defmodule PinchflatWeb.Sources.MediaItemTableLive do
use PinchflatWeb, :live_view
use Pinchflat.Media.MediaQuery
diff --git a/lib/pinchflat_web/controllers/sources/source_html/show.html.heex b/lib/pinchflat_web/controllers/sources/source_html/show.html.heex
index 0e9459a..f232f42 100644
--- a/lib/pinchflat_web/controllers/sources/source_html/show.html.heex
+++ b/lib/pinchflat_web/controllers/sources/source_html/show.html.heex
@@ -39,21 +39,21 @@
<:tab title="Pending" id="pending">
<%= live_render(
@conn,
- Pinchflat.Sources.MediaItemTableLive,
+ PinchflatWeb.Sources.MediaItemTableLive,
session: %{"source_id" => @source.id, "media_state" => "pending"}
) %>
<:tab title="Downloaded" id="downloaded">
<%= live_render(
@conn,
- Pinchflat.Sources.MediaItemTableLive,
+ PinchflatWeb.Sources.MediaItemTableLive,
session: %{"source_id" => @source.id, "media_state" => "downloaded"}
) %>
<:tab title="Other" id="other">
<%= live_render(
@conn,
- Pinchflat.Sources.MediaItemTableLive,
+ PinchflatWeb.Sources.MediaItemTableLive,
session: %{"source_id" => @source.id, "media_state" => "other"}
) %>
diff --git a/priv/repo/erd.png b/priv/repo/erd.png
index c6566d8..6237757 100644
Binary files a/priv/repo/erd.png and b/priv/repo/erd.png differ
diff --git a/priv/repo/migrations/20241120204407_add_enabled_to_sources.exs b/priv/repo/migrations/20241120204407_add_enabled_to_sources.exs
new file mode 100644
index 0000000..e98150b
--- /dev/null
+++ b/priv/repo/migrations/20241120204407_add_enabled_to_sources.exs
@@ -0,0 +1,9 @@
+defmodule Pinchflat.Repo.Migrations.AddEnabledToSources do
+ use Ecto.Migration
+
+ def change do
+ alter table(:sources) do
+ add :enabled, :boolean, default: true, null: false
+ end
+ end
+end
diff --git a/test/pinchflat/fast_indexing/fast_indexing_helpers_test.exs b/test/pinchflat/fast_indexing/fast_indexing_helpers_test.exs
index 5422018..34f5952 100644
--- a/test/pinchflat/fast_indexing/fast_indexing_helpers_test.exs
+++ b/test/pinchflat/fast_indexing/fast_indexing_helpers_test.exs
@@ -1,6 +1,7 @@
defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
use Pinchflat.DataCase
+ import Pinchflat.TasksFixtures
import Pinchflat.MediaFixtures
import Pinchflat.SourcesFixtures
import Pinchflat.ProfilesFixtures
@@ -8,6 +9,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
alias Pinchflat.Tasks
alias Pinchflat.Settings
alias Pinchflat.Media.MediaItem
+ alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.FastIndexingHelpers
@@ -19,6 +21,23 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
{:ok, [source: source_fixture()]}
end
+ describe "kickoff_indexing_task/1" do
+ test "deletes any existing fast indexing tasks", %{source: source} do
+ {:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
+ task = task_fixture(source_id: source.id, job_id: job.id)
+
+ assert Repo.reload!(task)
+ assert {:ok, _} = FastIndexingHelpers.kickoff_indexing_task(source)
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
+ end
+
+ test "kicks off a new fast indexing task", %{source: source} do
+ assert {:ok, _} = FastIndexingHelpers.kickoff_indexing_task(source)
+ assert [worker] = all_enqueued(worker: FastIndexingWorker)
+ assert worker.args["id"] == source.id
+ end
+ end
+
describe "kickoff_download_tasks_from_youtube_rss_feed/1" do
test "enqueues a new worker for each new media_id in the source's RSS feed", %{source: source} do
expect(HTTPClientMock, :get, fn _url -> {:ok, "
test_1"} end)
diff --git a/test/pinchflat/slow_indexing/slow_indexing_helpers_test.exs b/test/pinchflat/slow_indexing/slow_indexing_helpers_test.exs
index 36abcd7..b3e8004 100644
--- a/test/pinchflat/slow_indexing/slow_indexing_helpers_test.exs
+++ b/test/pinchflat/slow_indexing/slow_indexing_helpers_test.exs
@@ -23,6 +23,36 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
assert_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
end
+ test "schedules a job for the future based on when the source was last indexed" do
+ source = source_fixture(index_frequency_minutes: 30, last_indexed_at: now_minus(5, :minutes))
+
+ assert {:ok, _} = SlowIndexingHelpers.kickoff_indexing_task(source)
+
+ [job] = all_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
+
+ assert_in_delta DateTime.diff(job.scheduled_at, DateTime.utc_now(), :minute), 25, 1
+ end
+
+ test "schedules a job immediately if the source was indexed far in the past" do
+ source = source_fixture(index_frequency_minutes: 30, last_indexed_at: now_minus(60, :minutes))
+
+ assert {:ok, _} = SlowIndexingHelpers.kickoff_indexing_task(source)
+
+ [job] = all_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
+
+ assert_in_delta DateTime.diff(job.scheduled_at, DateTime.utc_now(), :second), 0, 1
+ end
+
+ test "schedules a job immediately if the source has never been indexed" do
+ source = source_fixture(index_frequency_minutes: 30, last_indexed_at: nil)
+
+ assert {:ok, _} = SlowIndexingHelpers.kickoff_indexing_task(source)
+
+ [job] = all_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
+
+ assert_in_delta DateTime.diff(job.scheduled_at, DateTime.utc_now(), :second), 0, 1
+ end
+
test "creates and attaches a task" do
source = source_fixture(index_frequency_minutes: 1)
@@ -92,6 +122,56 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
end
end
+ describe "delete_indexing_tasks/2" do
+ setup do
+ source = source_fixture()
+
+ {:ok, %{source: source}}
+ end
+
+ test "deletes slow indexing tasks for the source", %{source: source} do
+ {:ok, job} = Oban.insert(MediaCollectionIndexingWorker.new(%{"id" => source.id}))
+ _task = task_fixture(source_id: source.id, job_id: job.id)
+
+ assert_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
+ assert :ok = SlowIndexingHelpers.delete_indexing_tasks(source)
+ refute_enqueued(worker: MediaCollectionIndexingWorker)
+ end
+
+ test "deletes fast indexing tasks for the source", %{source: source} do
+ {:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
+ _task = task_fixture(source_id: source.id, job_id: job.id)
+
+ assert_enqueued(worker: FastIndexingWorker, args: %{"id" => source.id})
+ assert :ok = SlowIndexingHelpers.delete_indexing_tasks(source)
+ refute_enqueued(worker: FastIndexingWorker)
+ end
+
+ test "doesn't normally delete currently executing tasks", %{source: source} do
+ {:ok, job} = Oban.insert(MediaCollectionIndexingWorker.new(%{"id" => source.id}))
+ task = task_fixture(source_id: source.id, job_id: job.id)
+
+ from(Oban.Job, where: [id: ^job.id], update: [set: [state: "executing"]])
+ |> Repo.update_all([])
+
+ assert Repo.reload!(task)
+ assert :ok = SlowIndexingHelpers.delete_indexing_tasks(source)
+ assert Repo.reload!(task)
+ end
+
+ test "can optionally delete currently executing tasks", %{source: source} do
+ {:ok, job} = Oban.insert(MediaCollectionIndexingWorker.new(%{"id" => source.id}))
+ task = task_fixture(source_id: source.id, job_id: job.id)
+
+ from(Oban.Job, where: [id: ^job.id], update: [set: [state: "executing"]])
+ |> Repo.update_all([])
+
+ assert Repo.reload!(task)
+ assert :ok = SlowIndexingHelpers.delete_indexing_tasks(source, include_executing: true)
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
+ end
+ end
+
describe "index_and_enqueue_download_for_media_items/1" do
setup do
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts ->
diff --git a/test/pinchflat/sources_test.exs b/test/pinchflat/sources_test.exs
index ee57e17..a87917e 100644
--- a/test/pinchflat/sources_test.exs
+++ b/test/pinchflat/sources_test.exs
@@ -418,6 +418,100 @@ defmodule Pinchflat.SourcesTest do
assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
end
+ test "updates with invalid data returns error changeset" do
+ source = source_fixture()
+
+ assert {:error, %Ecto.Changeset{}} =
+ Sources.update_source(source, @invalid_source_attrs)
+
+ assert source == Sources.get_source!(source.id)
+ end
+
+ test "updating will kickoff a metadata storage worker if the original_url changes" do
+ expect(YtDlpRunnerMock, :run, &playlist_mock/4)
+ source = source_fixture()
+ update_attrs = %{original_url: "https://www.youtube.com/channel/cba321"}
+
+ assert {:ok, %Source{} = source} = Sources.update_source(source, update_attrs)
+
+ assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
+ end
+
+ test "updating will not kickoff a metadata storage worker other attrs change" do
+ source = source_fixture()
+ update_attrs = %{name: "some new name"}
+
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+
+ refute_enqueued(worker: SourceMetadataStorageWorker)
+ end
+ end
+
+ describe "update_source/3 when testing media download tasks" do
+ test "enabling the download_media attribute will schedule a download task" do
+ source = source_fixture(download_media: false)
+ media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
+ update_attrs = %{download_media: true}
+
+ refute_enqueued(worker: MediaDownloadWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
+ end
+
+ test "disabling the download_media attribute will cancel the download task" do
+ source = source_fixture(download_media: true, enabled: true)
+ media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
+ update_attrs = %{download_media: false}
+ DownloadingHelpers.enqueue_pending_download_tasks(source)
+
+ assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: MediaDownloadWorker)
+ end
+
+ test "enabling download_media will not schedule a task if the source is disabled" do
+ source = source_fixture(download_media: false, enabled: false)
+ _media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
+ update_attrs = %{download_media: true}
+
+ refute_enqueued(worker: MediaDownloadWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: MediaDownloadWorker)
+ end
+
+ test "disabling a source will cancel any pending download tasks" do
+ source = source_fixture(download_media: true, enabled: true)
+ media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
+ update_attrs = %{enabled: false}
+ DownloadingHelpers.enqueue_pending_download_tasks(source)
+
+ assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: MediaDownloadWorker)
+ end
+
+ test "enabling a source will schedule a download task if download_media is true" do
+ source = source_fixture(download_media: true, enabled: false)
+ media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
+ update_attrs = %{enabled: true}
+
+ refute_enqueued(worker: MediaDownloadWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
+ end
+
+ test "enabling a source will not schedule a download task if download_media is false" do
+ source = source_fixture(download_media: false, enabled: false)
+ _media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
+ update_attrs = %{enabled: true}
+
+ refute_enqueued(worker: MediaDownloadWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: MediaDownloadWorker)
+ end
+ end
+
+ describe "update_source/3 when testing slow indexing" do
test "updating the index frequency to >0 will re-schedule the indexing task" do
source = source_fixture()
update_attrs = %{index_frequency_minutes: 123}
@@ -462,27 +556,47 @@ defmodule Pinchflat.SourcesTest do
refute_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
end
- test "enabling the download_media attribute will schedule a download task" do
- source = source_fixture(download_media: false)
- media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
- update_attrs = %{download_media: true}
+ test "disabling a source will delete any pending tasks" do
+ source = source_fixture()
+ update_attrs = %{enabled: false}
+
+ {:ok, job} = Oban.insert(MediaCollectionIndexingWorker.new(%{"id" => source.id}))
+ task = task_fixture(source_id: source.id, job_id: job.id)
- refute_enqueued(worker: MediaDownloadWorker)
assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
- assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
+
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
end
- test "disabling the download_media attribute will cancel the download task" do
- source = source_fixture(download_media: true)
- media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
- update_attrs = %{download_media: false}
- DownloadingHelpers.enqueue_pending_download_tasks(source)
+ test "updating the index frequency will not create a task if the source is disabled" do
+ source = source_fixture(enabled: false)
+ update_attrs = %{index_frequency_minutes: 123}
- assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
+ refute_enqueued(worker: MediaCollectionIndexingWorker)
assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
- refute_enqueued(worker: MediaDownloadWorker)
+ refute_enqueued(worker: MediaCollectionIndexingWorker)
end
+ test "enabling a source will create a task if the index frequency is >0" do
+ source = source_fixture(enabled: false, index_frequency_minutes: 123)
+ update_attrs = %{enabled: true}
+
+ refute_enqueued(worker: MediaCollectionIndexingWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ assert_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
+ end
+
+ test "enabling a source will not create a task if the index frequency is 0" do
+ source = source_fixture(enabled: false, index_frequency_minutes: 0)
+ update_attrs = %{enabled: true}
+
+ refute_enqueued(worker: MediaCollectionIndexingWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: MediaCollectionIndexingWorker)
+ end
+ end
+
+ describe "update_source/3 when testing fast indexing" do
test "enabling fast_index will schedule a fast indexing task" do
source = source_fixture(fast_index: false)
update_attrs = %{fast_index: true}
@@ -503,15 +617,6 @@ defmodule Pinchflat.SourcesTest do
refute_enqueued(worker: FastIndexingWorker)
end
- test "updates with invalid data returns error changeset" do
- source = source_fixture()
-
- assert {:error, %Ecto.Changeset{}} =
- Sources.update_source(source, @invalid_source_attrs)
-
- assert source == Sources.get_source!(source.id)
- end
-
test "fast_index forces the index frequency to be a default value" do
source = source_fixture(%{fast_index: true})
update_attrs = %{index_frequency_minutes: 0}
@@ -530,23 +635,43 @@ defmodule Pinchflat.SourcesTest do
assert source.index_frequency_minutes == 0
end
- test "updating will kickoff a metadata storage worker if the original_url changes" do
- expect(YtDlpRunnerMock, :run, &playlist_mock/4)
+ test "disabling a source will delete any pending tasks" do
source = source_fixture()
- update_attrs = %{original_url: "https://www.youtube.com/channel/cba321"}
+ update_attrs = %{enabled: false}
- assert {:ok, %Source{} = source} = Sources.update_source(source, update_attrs)
-
- assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
- end
-
- test "updating will not kickoff a metadata storage worker other attrs change" do
- source = source_fixture()
- update_attrs = %{name: "some new name"}
+ {:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
+ task = task_fixture(source_id: source.id, job_id: job.id)
assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
- refute_enqueued(worker: SourceMetadataStorageWorker)
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
+ end
+
+ test "updating fast indexing will not create a task if the source is disabled" do
+ source = source_fixture(enabled: false, fast_index: false)
+ update_attrs = %{fast_index: true}
+
+ refute_enqueued(worker: FastIndexingWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: FastIndexingWorker)
+ end
+
+ test "enabling a source will create a task if fast_index is true" do
+ source = source_fixture(enabled: false, fast_index: true)
+ update_attrs = %{enabled: true}
+
+ refute_enqueued(worker: FastIndexingWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ assert_enqueued(worker: FastIndexingWorker, args: %{"id" => source.id})
+ end
+
+ test "enabling a source will not create a task if fast_index is false" do
+ source = source_fixture(enabled: false, fast_index: false)
+ update_attrs = %{enabled: true}
+
+ refute_enqueued(worker: FastIndexingWorker)
+ assert {:ok, %Source{}} = Sources.update_source(source, update_attrs)
+ refute_enqueued(worker: FastIndexingWorker)
end
end
diff --git a/test/pinchflat/tasks_test.exs b/test/pinchflat/tasks_test.exs
index 4b99f12..53d1c32 100644
--- a/test/pinchflat/tasks_test.exs
+++ b/test/pinchflat/tasks_test.exs
@@ -247,7 +247,7 @@ defmodule Pinchflat.TasksTest do
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
end
- test "deletion can optionall include executing tasks" do
+ test "deletion can optionally include executing tasks" do
source = source_fixture()
task = task_fixture(source_id: source.id)
diff --git a/test/pinchflat_web/controllers/source_controller_test.exs b/test/pinchflat_web/controllers/source_controller_test.exs
index 5a6e0e7..42b574d 100644
--- a/test/pinchflat_web/controllers/source_controller_test.exs
+++ b/test/pinchflat_web/controllers/source_controller_test.exs
@@ -34,27 +34,10 @@ defmodule PinchflatWeb.SourceControllerTest do
end
describe "index" do
- test "lists all sources", %{conn: conn} do
- source = source_fixture()
+ # Most of the tests are in `index_table_list_test.exs`
+ test "returns 200", %{conn: conn} do
conn = get(conn, ~p"/sources")
-
assert html_response(conn, 200) =~ "Sources"
- assert html_response(conn, 200) =~ source.custom_name
- end
-
- test "omits sources that have marked_for_deletion_at set", %{conn: conn} do
- source = source_fixture(marked_for_deletion_at: DateTime.utc_now())
- conn = get(conn, ~p"/sources")
-
- refute html_response(conn, 200) =~ source.custom_name
- end
-
- test "omits sources who's media profile has marked_for_deletion_at set", %{conn: conn} do
- media_profile = media_profile_fixture(marked_for_deletion_at: DateTime.utc_now())
- source = source_fixture(media_profile_id: media_profile.id)
- conn = get(conn, ~p"/sources")
-
- refute html_response(conn, 200) =~ source.custom_name
end
end
diff --git a/test/pinchflat_web/controllers/sources/index_table_live_test.exs b/test/pinchflat_web/controllers/sources/index_table_live_test.exs
new file mode 100644
index 0000000..659bd04
--- /dev/null
+++ b/test/pinchflat_web/controllers/sources/index_table_live_test.exs
@@ -0,0 +1,55 @@
+defmodule PinchflatWeb.Sources.IndexTableLiveTest do
+ use PinchflatWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import Pinchflat.SourcesFixtures
+ import Pinchflat.ProfilesFixtures
+
+ alias Pinchflat.Sources.Source
+ alias PinchflatWeb.Sources.IndexTableLive
+
+ describe "initial rendering" do
+ test "lists all sources", %{conn: conn} do
+ source = source_fixture()
+
+ {:ok, _view, html} = live_isolated(conn, IndexTableLive)
+
+ assert html =~ source.custom_name
+ end
+
+ test "omits sources that have marked_for_deletion_at set", %{conn: conn} do
+ source = source_fixture(marked_for_deletion_at: DateTime.utc_now())
+
+ {:ok, _view, html} = live_isolated(conn, IndexTableLive)
+
+ refute html =~ source.custom_name
+ end
+
+ test "omits sources who's media profile has marked_for_deletion_at set", %{conn: conn} do
+ media_profile = media_profile_fixture(marked_for_deletion_at: DateTime.utc_now())
+ source = source_fixture(media_profile_id: media_profile.id)
+
+ {:ok, _view, html} = live_isolated(conn, IndexTableLive)
+
+ refute html =~ source.custom_name
+ end
+ end
+
+ describe "when a source is enabled or disabled" do
+ test "updates the source's enabled status", %{conn: conn} do
+ source = source_fixture(enabled: true)
+ {:ok, view, _html} = live_isolated(conn, IndexTableLive)
+
+ params = %{
+ "event" => "toggle_enabled",
+ "id" => source.id,
+ "value" => "false"
+ }
+
+ # Send an event to the server directly
+ render_change(view, "formless-input", params)
+
+ assert %{enabled: false} = Repo.get!(Source, source.id)
+ end
+ end
+end
diff --git a/test/pinchflat_web/controllers/sources/media_item_table_live_test.exs b/test/pinchflat_web/controllers/sources/media_item_table_live_test.exs
index 772e305..b941f84 100644
--- a/test/pinchflat_web/controllers/sources/media_item_table_live_test.exs
+++ b/test/pinchflat_web/controllers/sources/media_item_table_live_test.exs
@@ -6,7 +6,7 @@ defmodule PinchflatWeb.Sources.MediaItemTableLiveTest do
import Pinchflat.SourcesFixtures
import Pinchflat.ProfilesFixtures
- alias Pinchflat.Sources.MediaItemTableLive
+ alias PinchflatWeb.Sources.MediaItemTableLive
setup do
source = source_fixture()
diff --git a/test/support/fixtures/sources_fixtures.ex b/test/support/fixtures/sources_fixtures.ex
index d54ba5f..9d699dc 100644
--- a/test/support/fixtures/sources_fixtures.ex
+++ b/test/support/fixtures/sources_fixtures.ex
@@ -20,6 +20,7 @@ defmodule Pinchflat.SourcesFixtures do
Enum.into(
attrs,
%{
+ enabled: true,
collection_name: "Source ##{:rand.uniform(1_000_000)}",
collection_id: Base.encode16(:crypto.hash(:md5, "#{:rand.uniform(1_000_000)}")),
collection_type: "channel",