<.flash kind={:info} title="Success!" flash={@flash} />
<.flash kind={:error} title="Error!" flash={@flash} />
<.flash
@@ -632,9 +637,11 @@ defmodule PinchflatWeb.CoreComponents do
def show(js \\ %JS{}, selector) do
JS.show(js,
to: selector,
- transition:
- {"transition-all transform ease-out duration-300", "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
- "opacity-100 translate-y-0 sm:scale-100"}
+ transition: {
+ "transition-all transform ease-out duration-300",
+ "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
+ "opacity-100 translate-y-0 sm:scale-100"
+ }
)
end
@@ -642,9 +649,11 @@ defmodule PinchflatWeb.CoreComponents do
JS.hide(js,
to: selector,
time: 200,
- transition:
- {"transition-all transform ease-in duration-200", "opacity-100 translate-y-0 sm:scale-100",
- "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"}
+ transition: {
+ "transition-all transform ease-in duration-200",
+ "opacity-100 translate-y-0 sm:scale-100",
+ "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
+ }
)
end
diff --git a/lib/pinchflat_web/controllers/media/media_item_controller.ex b/lib/pinchflat_web/controllers/media/media_item_controller.ex
index 3db81d4..398bd32 100644
--- a/lib/pinchflat_web/controllers/media/media_item_controller.ex
+++ b/lib/pinchflat_web/controllers/media/media_item_controller.ex
@@ -8,4 +8,23 @@ defmodule PinchflatWeb.Media.MediaItemController do
render(conn, :show, media_item: media_item)
end
+
+ def delete(conn, %{"id" => id} = params) do
+ delete_files = Map.get(params, "delete_files", false)
+ media_item = Media.get_media_item!(id)
+
+ if delete_files do
+ {:ok, _} = Media.delete_media_item_and_attachments(media_item)
+
+ conn
+ |> put_flash(:info, "Record and files deleted successfully.")
+ |> redirect(to: ~p"/sources/#{media_item.source_id}")
+ else
+ {:ok, _} = Media.delete_media_item(media_item)
+
+ conn
+ |> put_flash(:info, "Record deleted successfully. Files were not deleted.")
+ |> redirect(to: ~p"/sources/#{media_item.source_id}")
+ end
+ end
end
diff --git a/lib/pinchflat_web/controllers/media/media_item_html/show.html.heex b/lib/pinchflat_web/controllers/media/media_item_html/show.html.heex
index ac65a28..f23de2c 100644
--- a/lib/pinchflat_web/controllers/media/media_item_html/show.html.heex
+++ b/lib/pinchflat_web/controllers/media/media_item_html/show.html.heex
@@ -1,12 +1,23 @@
diff --git a/lib/pinchflat_web/controllers/searches/search_html/show.html.heex b/lib/pinchflat_web/controllers/searches/search_html/show.html.heex
index 996701e..4d2bfd5 100644
--- a/lib/pinchflat_web/controllers/searches/search_html/show.html.heex
+++ b/lib/pinchflat_web/controllers/searches/search_html/show.html.heex
@@ -16,7 +16,10 @@
<.highlight_search_terms text={result.matching_search_term} />
<:col :let={result} label="" class="flex place-content-evenly">
- <.link navigate={~p"/media/#{result.id}"} class="hover:text-secondary duration-200 ease-in-out mx-0.5">
+ <.link
+ navigate={~p"/sources/#{result.source_id}/media/#{result.id}"}
+ class="hover:text-secondary duration-200 ease-in-out mx-0.5"
+ >
<.icon name="hero-eye" />
diff --git a/lib/pinchflat_web/router.ex b/lib/pinchflat_web/router.ex
index 55c9fcb..0470029 100644
--- a/lib/pinchflat_web/router.ex
+++ b/lib/pinchflat_web/router.ex
@@ -20,11 +20,10 @@ defmodule PinchflatWeb.Router do
get "/", PageController, :home
resources "/media_profiles", MediaProfiles.MediaProfileController
- resources "/media", Media.MediaItemController, only: [:show]
resources "/search", Searches.SearchController, only: [:show], singleton: true
resources "/sources", MediaSources.SourceController do
- resources "/media", Media.MediaItemController, only: [:show]
+ resources "/media", Media.MediaItemController, only: [:show, :delete]
end
end
diff --git a/test/pinchflat/media_test.exs b/test/pinchflat/media_test.exs
index 8fbe8d7..77d267d 100644
--- a/test/pinchflat/media_test.exs
+++ b/test/pinchflat/media_test.exs
@@ -221,6 +221,24 @@ defmodule Pinchflat.MediaTest do
end
end
+ describe "media_filepaths/1" do
+ test "returns filepaths in a flat list" do
+ filepaths = %{
+ media_filepath: "/video/test.mp4",
+ thumbnail_filepath: "/video/test.jpg",
+ subtitle_filepaths: [["en", "video/test.srt"]]
+ }
+
+ media_item = media_item_fixture(filepaths)
+
+ assert Media.media_filepaths(media_item) == [
+ "/video/test.mp4",
+ "/video/test.jpg",
+ "video/test.srt"
+ ]
+ end
+ end
+
describe "create_media_item/1" do
test "creating with valid data creates a media_item" do
valid_attrs = %{
@@ -282,6 +300,61 @@ defmodule Pinchflat.MediaTest do
end
end
+ describe "delete_attachments/1" do
+ test "deletes the media item's files" do
+ media_item = media_item_with_attachments()
+
+ assert {:ok, _} = Media.delete_attachments(media_item)
+ refute File.exists?(media_item.media_filepath)
+ end
+
+ test "does not delete the media item" do
+ media_item = media_item_with_attachments()
+
+ assert {:ok, _} = Media.delete_attachments(media_item)
+
+ assert Repo.reload!(media_item)
+ end
+
+ test "deletes the parent folder if it is empty" do
+ media_item = media_item_with_attachments()
+ root_directory = Path.dirname(media_item.media_filepath)
+
+ assert {:ok, _} = Media.delete_attachments(media_item)
+ refute File.exists?(root_directory)
+ end
+
+ test "does not delete the parent folder if it is not empty" do
+ media_item = media_item_with_attachments()
+ root_directory = Path.dirname(media_item.media_filepath)
+ File.touch(Path.join([root_directory, "test.txt"]))
+
+ assert {:ok, _} = Media.delete_attachments(media_item)
+ assert File.exists?(root_directory)
+
+ :ok = File.rm(Path.join([root_directory, "test.txt"]))
+ :ok = File.rmdir(root_directory)
+ end
+ end
+
+ describe "delete_media_item_and_attachments/1" do
+ setup do
+ media_item = media_item_with_attachments()
+ {:ok, media_item: media_item}
+ end
+
+ test "deletes the media item", %{media_item: media_item} do
+ assert {:ok, _} = Media.delete_media_item_and_attachments(media_item)
+ assert_raise Ecto.NoResultsError, fn -> Media.get_media_item!(media_item.id) end
+ end
+
+ test "deletes associated files", %{media_item: media_item} do
+ assert File.exists?(media_item.media_filepath)
+ assert {:ok, _} = Media.delete_media_item_and_attachments(media_item)
+ refute File.exists?(media_item.media_filepath)
+ 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/profiles/options/yt_dlp/download_option_builder_test.exs b/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs
index 32d3ffa..56942ce 100644
--- a/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs
+++ b/test/pinchflat/profiles/options/yt_dlp/download_option_builder_test.exs
@@ -12,7 +12,7 @@ defmodule Pinchflat.Profiles.Options.YtDlp.DownloadOptionBuilderTest do
test "it generates an expanded output path based on the given template" do
assert {:ok, res} = DownloadOptionBuilder.build(@media_profile)
- assert {:output, "/tmp/videos/%(title)S.%(ext)s"} in res
+ assert {:output, "/tmp/test/videos/%(title)S.%(ext)s"} in res
end
end
diff --git a/test/pinchflat_web/controllers/media_item_controller_test.exs b/test/pinchflat_web/controllers/media_item_controller_test.exs
index eb0eab2..002f3bd 100644
--- a/test/pinchflat_web/controllers/media_item_controller_test.exs
+++ b/test/pinchflat_web/controllers/media_item_controller_test.exs
@@ -3,15 +3,74 @@ defmodule PinchflatWeb.MediaItemControllerTest do
import Pinchflat.MediaFixtures
+ alias Pinchflat.Repo
+ alias Pinchflat.Media
+
describe "show media" do
setup [:create_media_item]
test "renders the page", %{conn: conn, media_item: media_item} do
- conn = get(conn, ~p"/media/#{media_item}")
+ conn = get(conn, ~p"/sources/#{media_item.source_id}/media/#{media_item}")
assert html_response(conn, 200) =~ "Media Item ##{media_item.id}"
end
end
+ describe "delete media when just deleting the records" do
+ setup do
+ media_item = media_item_with_attachments()
+
+ on_exit(fn ->
+ Media.delete_attachments(media_item)
+ end)
+
+ %{media_item: media_item}
+ 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}")
+
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
+ end
+
+ test "the files are not 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)
+ 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}")
+
+ 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()
+
+ %{media_item: media_item}
+ 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")
+
+ assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
+ end
+
+ 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}"
+ end
+ end
+
defp create_media_item(_) do
media_item = media_item_fixture()
%{media_item: media_item}
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index ff478a0..3aa5fce 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -34,6 +34,7 @@ defmodule PinchflatWeb.ConnCase do
setup tags do
Pinchflat.DataCase.setup_sandbox(tags)
+ Pinchflat.DataCase.setup_temp_filepaths()
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end
diff --git a/test/support/data_case.ex b/test/support/data_case.ex
index d217749..5bf7f10 100644
--- a/test/support/data_case.ex
+++ b/test/support/data_case.ex
@@ -32,6 +32,20 @@ defmodule Pinchflat.DataCase do
setup tags do
Pinchflat.DataCase.setup_sandbox(tags)
+ Pinchflat.DataCase.setup_temp_filepaths()
+ :ok
+ end
+
+ @doc """
+ Sets up the temp filepaths for the media and metadata directories.
+ """
+ def setup_temp_filepaths do
+ File.rm_rf!(Application.get_env(:pinchflat, :media_directory))
+ File.rm_rf!(Application.get_env(:pinchflat, :metadata_directory))
+
+ File.mkdir_p!(Application.get_env(:pinchflat, :media_directory))
+ File.mkdir_p!(Application.get_env(:pinchflat, :metadata_directory))
+
:ok
end
diff --git a/test/support/files/media.mkv b/test/support/files/media.mkv
new file mode 100644
index 0000000..b0b02c1
Binary files /dev/null and b/test/support/files/media.mkv differ
diff --git a/test/support/fixtures/media_fixtures.ex b/test/support/fixtures/media_fixtures.ex
index 66bb1da..f998824 100644
--- a/test/support/fixtures/media_fixtures.ex
+++ b/test/support/fixtures/media_fixtures.ex
@@ -31,9 +31,9 @@ defmodule Pinchflat.MediaFixtures do
def media_item_with_metadata(attrs \\ %{}) do
json_filepath =
Path.join([
- Path.dirname(__ENV__.file),
+ File.cwd!(),
+ "test",
"support",
- "fixtures",
"files",
"media_metadata.json"
])
@@ -44,4 +44,28 @@ defmodule Pinchflat.MediaFixtures do
media_item_fixture(merged_attrs)
end
+
+ def media_item_with_attachments(attrs \\ %{}) do
+ stored_media_filepath =
+ Path.join([
+ Application.get_env(:pinchflat, :media_directory),
+ "#{:rand.uniform(1_000_000)}",
+ "#{:rand.uniform(1_000_000)}_media.mkv"
+ ])
+
+ fixture_media_filepath =
+ Path.join([
+ File.cwd!(),
+ "test",
+ "support",
+ "files",
+ "media.mkv"
+ ])
+
+ :ok = File.mkdir_p(Path.dirname(stored_media_filepath))
+ :ok = File.cp(fixture_media_filepath, stored_media_filepath)
+
+ merged_attrs = Map.merge(attrs, %{media_filepath: stored_media_filepath})
+ media_item_fixture(merged_attrs)
+ end
end
diff --git a/test/test_helper.exs b/test/test_helper.exs
index 40d1202..b0bcf23 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -4,3 +4,11 @@ Application.put_env(:pinchflat, :yt_dlp_runner, YtDlpRunnerMock)
ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(Pinchflat.Repo, :manual)
Faker.start()
+
+ExUnit.after_suite(fn _ ->
+ File.rm_rf!(Application.get_env(:pinchflat, :media_directory))
+ File.rm_rf!(Application.get_env(:pinchflat, :metadata_directory))
+
+ File.mkdir_p!(Application.get_env(:pinchflat, :media_directory))
+ File.mkdir_p!(Application.get_env(:pinchflat, :metadata_directory))
+end)