diff --git a/lib/pinchflat/profiles/media_profile.ex b/lib/pinchflat/profiles/media_profile.ex index 3111640..da86df3 100644 --- a/lib/pinchflat/profiles/media_profile.ex +++ b/lib/pinchflat/profiles/media_profile.ex @@ -28,6 +28,7 @@ defmodule Pinchflat.Profiles.MediaProfile do livestream_behaviour preferred_resolution redownload_delay_days + marked_for_deletion_at )a @required_fields ~w(name output_path_template)a @@ -65,6 +66,8 @@ defmodule Pinchflat.Profiles.MediaProfile do field :livestream_behaviour, Ecto.Enum, values: ~w(include exclude only)a, default: :include field :preferred_resolution, Ecto.Enum, values: ~w(4320p 2160p 1080p 720p 480p 360p audio)a, default: :"1080p" + field :marked_for_deletion_at, :utc_datetime + has_many :sources, Source timestamps(type: :utc_datetime) diff --git a/lib/pinchflat/sources/source.ex b/lib/pinchflat/sources/source.ex index 418c828..b6d9154 100644 --- a/lib/pinchflat/sources/source.ex +++ b/lib/pinchflat/sources/source.ex @@ -35,6 +35,7 @@ defmodule Pinchflat.Sources.Source do title_filter_regex media_profile_id output_path_template_override + marked_for_deletion_at )a # Expensive API calls are made when a source is inserted/updated so @@ -87,6 +88,8 @@ defmodule Pinchflat.Sources.Source do field :fanart_filepath, :string field :banner_filepath, :string + field :marked_for_deletion_at, :utc_datetime + belongs_to :media_profile, MediaProfile has_one :metadata, SourceMetadata, on_replace: :update diff --git a/lib/pinchflat_web/controllers/sources/source_controller.ex b/lib/pinchflat_web/controllers/sources/source_controller.ex index 71f0b56..1a0dd56 100644 --- a/lib/pinchflat_web/controllers/sources/source_controller.ex +++ b/lib/pinchflat_web/controllers/sources/source_controller.ex @@ -17,6 +17,7 @@ defmodule PinchflatWeb.Sources.SourceController do source_query = from s in Source, as: :source, + where: is_nil(s.marked_for_deletion_at), inner_join: mp in assoc(s, :media_profile), preload: [media_profile: mp], order_by: [asc: s.custom_name], @@ -127,12 +128,14 @@ defmodule PinchflatWeb.Sources.SourceController do def delete(conn, %{"id" => id} = params) do delete_files = Map.get(params, "delete_files", false) source = Sources.get_source!(id) + + {:ok, _} = Sources.update_source(source, %{marked_for_deletion_at: DateTime.utc_now()}) SourceDeletionWorker.kickoff(source, %{delete_files: delete_files}) conn |> put_flash( :info, - "Started deletion job in background. May take a while to complete depending on the number of media items." + "Source deletion started. This may take a while to complete for large sources." ) |> redirect(to: ~p"/sources") end diff --git a/priv/repo/erd.png b/priv/repo/erd.png index 50e0ec4..70f395c 100644 Binary files a/priv/repo/erd.png and b/priv/repo/erd.png differ diff --git a/priv/repo/migrations/20240722183656_add_marked_for_deletion_at_to_sources_and_profiles.exs b/priv/repo/migrations/20240722183656_add_marked_for_deletion_at_to_sources_and_profiles.exs new file mode 100644 index 0000000..81a99e0 --- /dev/null +++ b/priv/repo/migrations/20240722183656_add_marked_for_deletion_at_to_sources_and_profiles.exs @@ -0,0 +1,13 @@ +defmodule Pinchflat.Repo.Migrations.AddMarkedForDeletionAtToSources do + use Ecto.Migration + + def change do + alter table(:sources) do + add :marked_for_deletion_at, :utc_datetime + end + + alter table(:media_profiles) do + add :marked_for_deletion_at, :utc_datetime + end + end +end diff --git a/test/pinchflat_web/controllers/source_controller_test.exs b/test/pinchflat_web/controllers/source_controller_test.exs index 9e8872d..06b86c0 100644 --- a/test/pinchflat_web/controllers/source_controller_test.exs +++ b/test/pinchflat_web/controllers/source_controller_test.exs @@ -33,8 +33,15 @@ defmodule PinchflatWeb.SourceControllerTest do describe "index" do test "lists all sources", %{conn: conn} do + source = source_fixture() 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 end @@ -127,9 +134,21 @@ defmodule PinchflatWeb.SourceControllerTest do end end - describe "delete source when just deleting the records" do + describe "delete source in all cases" do setup [:create_source] + test "redirects to the sources page", %{conn: conn, source: source} do + conn = delete(conn, ~p"/sources/#{source}") + assert redirected_to(conn) == ~p"/sources" + end + + test "sets marked_for_deletion_at", %{conn: conn, source: source} do + delete(conn, ~p"/sources/#{source}") + assert Repo.reload!(source).marked_for_deletion_at + end + end + + describe "delete source when just deleting the records" do test "deletes chosen source and media_items", %{conn: conn, source: source, media_item: media_item} do delete(conn, ~p"/sources/#{source}") @@ -137,11 +156,6 @@ defmodule PinchflatWeb.SourceControllerTest do assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end end - test "redirects to the sources page", %{conn: conn, source: source} do - conn = delete(conn, ~p"/sources/#{source}") - assert redirected_to(conn) == ~p"/sources" - end - test "does not delete the files", %{conn: conn, source: source, media_item: media_item} do delete(conn, ~p"/sources/#{source}") assert File.exists?(media_item.media_filepath) @@ -158,11 +172,6 @@ defmodule PinchflatWeb.SourceControllerTest do assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end end - test "redirects to the sources page", %{conn: conn, source: source} do - conn = delete(conn, ~p"/sources/#{source}?delete_files=true") - assert redirected_to(conn) == ~p"/sources" - end - test "deletes the files", %{conn: conn, source: source, media_item: media_item} do delete(conn, ~p"/sources/#{source}?delete_files=true") refute File.exists?(media_item.media_filepath)