diff --git a/lib/pinchflat/media.ex b/lib/pinchflat/media.ex index 05931a9..f2547de 100644 --- a/lib/pinchflat/media.ex +++ b/lib/pinchflat/media.ex @@ -9,6 +9,7 @@ defmodule Pinchflat.Media do alias Pinchflat.Tasks alias Pinchflat.Media.MediaItem alias Pinchflat.Sources.Source + alias Pinchflat.Media.MediaMetadata @doc """ Returns the list of media_items. Returns [%MediaItem{}, ...]. @@ -103,6 +104,21 @@ defmodule Pinchflat.Media do |> Enum.filter(&is_binary/1) end + @doc """ + Produces a flat list of the filesystem paths for a media_item's metadata files. + Returns an empty list if the media_item has no metadata. + + Returns [binary()] | [] + """ + def metadata_filepaths(media_item) do + metadata = Repo.preload(media_item, :metadata).metadata || %MediaMetadata{} + mapped_struct = Map.from_struct(metadata) + + MediaMetadata.filepath_attributes() + |> Enum.map(fn field -> mapped_struct[field] end) + |> Enum.filter(&is_binary/1) + end + @doc """ Creates a media_item. Returns {:ok, %MediaItem{}} | {:error, %Ecto.Changeset{}}. """ @@ -137,15 +153,23 @@ defmodule Pinchflat.Media do Returns {:ok, %MediaItem{}} """ def delete_attachments(media_item) do + media_item = Repo.preload(media_item, :metadata) + media_item |> media_filepaths() + |> Enum.concat(metadata_filepaths(media_item)) |> Enum.each(&File.rm/1) - # Fails if the directory is not empty - case File.rmdir(Path.dirname(media_item.media_filepath)) do - :ok -> {:ok, media_item} - {:error, :eexist} -> {:ok, media_item} + # rmdir will attempt to delete the directory, but only if it is empty + if media_item.media_filepath do + File.rmdir(Path.dirname(media_item.media_filepath)) end + + if media_item.metadata && media_item.metadata.metadata_filepath do + File.rmdir(Path.dirname(media_item.metadata.metadata_filepath)) + end + + {:ok, media_item} end @doc """ diff --git a/lib/pinchflat/media/media_metadata.ex b/lib/pinchflat/media/media_metadata.ex index daac6d0..18c3ba2 100644 --- a/lib/pinchflat/media/media_metadata.ex +++ b/lib/pinchflat/media/media_metadata.ex @@ -29,4 +29,9 @@ defmodule Pinchflat.Media.MediaMetadata do |> validate_required(@required_fields) |> unique_constraint([:media_item_id]) end + + @doc false + def filepath_attributes do + ~w(metadata_filepath thumbnail_filepath)a + end end diff --git a/lib/pinchflat/media_client/backends/yt_dlp/metadata_file_helpers.ex b/lib/pinchflat/media_client/backends/yt_dlp/metadata_file_helpers.ex index 1863df0..d737e69 100644 --- a/lib/pinchflat/media_client/backends/yt_dlp/metadata_file_helpers.ex +++ b/lib/pinchflat/media_client/backends/yt_dlp/metadata_file_helpers.ex @@ -9,8 +9,6 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.MetadataFileHelpers do needed """ - # TODO: ensure media metadata is deleted when the media item is deleted - @doc """ Compresses and stores metadata for a media item, returning the filepath. diff --git a/test/pinchflat/media_test.exs b/test/pinchflat/media_test.exs index fe92375..a02fe29 100644 --- a/test/pinchflat/media_test.exs +++ b/test/pinchflat/media_test.exs @@ -1,6 +1,7 @@ defmodule Pinchflat.MediaTest do use Pinchflat.DataCase + import Mox import Pinchflat.TasksFixtures import Pinchflat.MediaFixtures import Pinchflat.ProfilesFixtures @@ -8,6 +9,9 @@ defmodule Pinchflat.MediaTest do alias Pinchflat.Media alias Pinchflat.Media.MediaItem + alias Pinchflat.MediaClient.Backends.YtDlp.MetadataFileHelpers + + setup :verify_on_exit! @invalid_attrs %{title: nil, media_id: nil, media_filepath: nil} @@ -261,6 +265,27 @@ defmodule Pinchflat.MediaTest do end end + describe "metadata_filepaths" do + test "returns filepaths in a flat list" do + filepaths = %{ + metadata_filepath: "/metadata.json.gz", + thumbnail_filepath: "/thumbnail.jpg" + } + + media_item = media_item_fixture(%{metadata: filepaths}) + + assert Media.metadata_filepaths(media_item) == [ + "/metadata.json.gz", + "/thumbnail.jpg" + ] + end + + test "returns an empty list when there is no metadata" do + media_item = media_item_fixture() + assert Media.metadata_filepaths(media_item) == [] + end + end + describe "create_media_item/1" do test "creating with valid data creates a media_item" do valid_attrs = %{ @@ -330,6 +355,26 @@ defmodule Pinchflat.MediaTest do refute File.exists?(media_item.media_filepath) end + test "deletes 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) + + assert {:ok, _} = Media.delete_attachments(updated_media_item) + refute File.exists?(updated_media_item.metadata.metadata_filepath) + end + test "does not delete the media item" do media_item = media_item_with_attachments()