@@ -15,13 +23,22 @@
<.button_dropdown text="Actions" class="justify-center w-full sm:w-50">
<:option>
<.link
- href={~p"/sources/#{@media_item.source_id}/media/#{@media_item}?delete_files=true"}
+ href={~p"/sources/#{@media_item.source_id}/media/#{@media_item}"}
method="delete"
- data-confirm="Are you sure you want to delete this record and all associated files on disk? This cannot be undone."
+ data-confirm="Are you sure you want to delete all files for this media item? This cannot be undone."
>
Delete Files
+ <:option>
+ <.link
+ href={~p"/sources/#{@media_item.source_id}/media/#{@media_item}?prevent_download=true"}
+ method="delete"
+ data-confirm="Are you sure you want to delete all files for this media item and prevent it from re-downloading in the future? This cannot be undone."
+ >
+ Delete and Ignore
+
+
@@ -32,6 +49,7 @@
<.media_preview media_item={@media_item} />
<% end %>
+
<%= @media_item.title %>
Attributes
Source:
diff --git a/lib/pinchflat_web/router.ex b/lib/pinchflat_web/router.ex
index 202f460..e56a4f2 100644
--- a/lib/pinchflat_web/router.ex
+++ b/lib/pinchflat_web/router.ex
@@ -32,7 +32,7 @@ defmodule PinchflatWeb.Router do
resources "/search", Searches.SearchController, only: [:show], singleton: true
resources "/sources", Sources.SourceController do
- resources "/media", MediaItems.MediaItemController, only: [:show, :delete]
+ resources "/media", MediaItems.MediaItemController, only: [:show, :edit, :update, :delete]
end
end
diff --git a/priv/repo/migrations/20240402192417_add_prevent_download_to_media_items.exs b/priv/repo/migrations/20240402192417_add_prevent_download_to_media_items.exs
new file mode 100644
index 0000000..c43f91f
--- /dev/null
+++ b/priv/repo/migrations/20240402192417_add_prevent_download_to_media_items.exs
@@ -0,0 +1,9 @@
+defmodule Pinchflat.Repo.Migrations.AddPreventDownloadToMediaItems do
+ use Ecto.Migration
+
+ def change do
+ alter table(:media_items) do
+ add :prevent_download, :boolean, default: false, null: false
+ end
+ end
+end
diff --git a/test/pinchflat/media_test.exs b/test/pinchflat/media_test.exs
index c73c421..cc435b8 100644
--- a/test/pinchflat/media_test.exs
+++ b/test/pinchflat/media_test.exs
@@ -233,6 +233,16 @@ defmodule Pinchflat.MediaTest do
end
end
+ describe "list_pending_media_items_for/1 when testing download prevention" do
+ test "returns only media items that are not prevented from downloading" do
+ source = source_fixture()
+ _prevented_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, prevent_download: true})
+ media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, prevent_download: false})
+
+ assert Media.list_pending_media_items_for(source) == [media_item]
+ end
+ end
+
describe "list_downloaded_media_items_for/1" do
test "returns only media items with a media_filepath" do
source = source_fixture()
@@ -320,6 +330,18 @@ defmodule Pinchflat.MediaTest do
assert Media.pending_download?(media_item)
end
+
+ test "returns true if the media item is not prevented from downloading" do
+ media_item = media_item_fixture(%{media_filepath: nil, prevent_download: false})
+
+ assert Media.pending_download?(media_item)
+ end
+
+ test "returns false if the media item is prevented from downloading" do
+ media_item = media_item_fixture(%{media_filepath: nil, prevent_download: true})
+
+ refute Media.pending_download?(media_item)
+ end
end
describe "search/1" do
@@ -587,6 +609,62 @@ defmodule Pinchflat.MediaTest do
end
end
+ describe "delete_media_files/2" do
+ test "does not delete the media_item" do
+ media_item = media_item_fixture()
+
+ assert {:ok, %MediaItem{}} = Media.delete_media_files(media_item)
+ assert Repo.reload!(media_item)
+ end
+
+ test "deletes attached tasks" do
+ media_item = media_item_fixture()
+ task = task_fixture(%{media_item_id: media_item.id})
+
+ assert {:ok, %MediaItem{}} = Media.delete_media_files(media_item)
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
+ end
+
+ test "deletes the media_item's files" do
+ media_item = media_item_with_attachments()
+
+ assert {:ok, _} = Media.delete_media_files(media_item)
+ refute File.exists?(media_item.media_filepath)
+ end
+
+ test "does not delete the media item's metadata files" do
+ stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
+ media_item = Repo.preload(media_item_with_attachments(), :metadata)
+
+ update_attrs = %{
+ metadata: %{
+ metadata_filepath: MetadataFileHelpers.compress_and_store_metadata_for(media_item, %{}),
+ thumbnail_filepath:
+ MetadataFileHelpers.download_and_store_thumbnail_for(media_item, %{
+ "thumbnail" => "https://example.com/thumbnail.jpg"
+ })
+ }
+ }
+
+ {:ok, updated_media_item} = Media.update_media_item(media_item, update_attrs)
+ metadata = Repo.preload(updated_media_item, :metadata).metadata
+
+ assert {:ok, _} = Media.delete_media_files(updated_media_item)
+ assert Repo.reload(metadata)
+ assert File.exists?(updated_media_item.metadata.metadata_filepath)
+
+ # cleanup
+ Media.delete_media_item(updated_media_item, delete_files: true)
+ end
+
+ test "can prevent the media item from being downloaded" do
+ media_item = media_item_with_attachments()
+
+ assert {:ok, updated_media_item} = Media.delete_media_files(media_item, prevent_download: true)
+ assert updated_media_item.prevent_download
+ end
+ end
+
describe "change_media_item/1" do
test "change_media_item/1 returns a media_item changeset" do
media_item = media_item_fixture()
diff --git a/test/pinchflat_web/controllers/media_item_controller_test.exs b/test/pinchflat_web/controllers/media_item_controller_test.exs
index 428d337..e8e035a 100644
--- a/test/pinchflat_web/controllers/media_item_controller_test.exs
+++ b/test/pinchflat_web/controllers/media_item_controller_test.exs
@@ -10,27 +10,58 @@ defmodule PinchflatWeb.MediaItemControllerTest do
test "renders the page", %{conn: conn, media_item: media_item} do
conn = get(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item}")
- assert html_response(conn, 200) =~ "Media Item ##{media_item.id}"
+
+ assert html_response(conn, 200) =~ "#{media_item.title}"
end
end
- describe "delete media when just deleting the records" do
+ describe "edit media" do
+ setup [:create_media_item]
+
+ test "renders form for editing chosen media_item", %{conn: conn, media_item: media_item} do
+ conn = get(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item}/edit")
+
+ assert html_response(conn, 200) =~ "Editing"
+ end
+ end
+
+ describe "update media" do
+ setup [:create_media_item]
+
+ test "redirects when data is valid", %{conn: conn, media_item: media_item} do
+ update_attrs = %{title: "New Title"}
+
+ conn = put(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item}", media_item: update_attrs)
+ assert redirected_to(conn) == ~p"/sources/#{media_item.source_id}/media/#{media_item}"
+
+ conn = get(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item}")
+ assert html_response(conn, 200) =~ update_attrs[:title]
+ end
+
+ test "renders errors when data is invalid", %{conn: conn, media_item: media_item} do
+ conn = put(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item}", media_item: %{title: nil})
+
+ assert html_response(conn, 200) =~ "Editing"
+ end
+ end
+
+ describe "delete media" do
setup do
media_item = media_item_with_attachments()
%{media_item: media_item}
end
- test "the media item is deleted", %{conn: conn, media_item: media_item} do
+ test "the media item not is deleted", %{conn: conn, media_item: media_item} do
delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}")
- assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
+ assert Repo.reload!(media_item)
end
- test "the files are not deleted", %{conn: conn, media_item: media_item} do
+ test "the files are deleted", %{conn: conn, media_item: media_item} do
delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}")
- assert File.exists?(media_item.media_filepath)
+ refute File.exists?(media_item.media_filepath)
end
test "redirects to the source page", %{conn: conn, media_item: media_item} do
@@ -38,31 +69,21 @@ defmodule PinchflatWeb.MediaItemControllerTest do
assert redirected_to(conn) == ~p"/sources/#{media_item.source_id}"
end
- end
- describe "delete media when deleting the records and files" do
- setup do
- media_item = media_item_with_attachments()
+ test "doesn't prevent re-download by default", %{conn: conn, media_item: media_item} do
+ delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}")
- %{media_item: media_item}
+ media_item = Repo.reload(media_item)
+
+ refute media_item.prevent_download
end
- test "the media item is deleted", %{conn: conn, media_item: media_item} do
- delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}?delete_files=true")
+ test "can optionally prevent re-download", %{conn: conn, media_item: media_item} do
+ delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}?prevent_download=true")
- assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
- end
+ media_item = Repo.reload(media_item)
- test "the files are deleted", %{conn: conn, media_item: media_item} do
- delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}?delete_files=true")
-
- refute File.exists?(media_item.media_filepath)
- end
-
- test "redirects to the source page", %{conn: conn, media_item: media_item} do
- conn = delete(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item.id}?delete_files=true")
-
- assert redirected_to(conn) == ~p"/sources/#{media_item.source_id}"
+ assert media_item.prevent_download
end
end