From 2993c33e360a32a93887bf205b35674ce75f330e Mon Sep 17 00:00:00 2001 From: Kieran Eglin Date: Tue, 2 Apr 2024 14:22:36 -0700 Subject: [PATCH] Added methods for ignoring media items from future download --- lib/pinchflat/media/media.ex | 29 +++++++++-- lib/pinchflat/media/media_item.ex | 10 ++++ lib/pinchflat/media/media_query.ex | 4 ++ test/pinchflat/media_test.exs | 78 ++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 4 deletions(-) diff --git a/lib/pinchflat/media/media.ex b/lib/pinchflat/media/media.ex index 346029b..4ab09d8 100644 --- a/lib/pinchflat/media/media.ex +++ b/lib/pinchflat/media/media.ex @@ -27,8 +27,8 @@ defmodule Pinchflat.Media do pending means the `media_filepath` is `nil` AND the media_item matches the format selection rules of the parent media_profile. - See `build_format_clauses` but tl;dr is it _may_ filter based - on shorts or livestreams depending on the media_profile settings. + See `matching_download_criteria_for` but tl;dr is it _may_ filter based + on shorts livestreams depending on the media_profile settings. Returns [%MediaItem{}, ...]. """ @@ -161,7 +161,7 @@ defmodule Pinchflat.Media do Tasks.delete_tasks_for(media_item) if delete_files do - {:ok, _} = delete_media_files(media_item) + {:ok, _} = do_delete_media_files(media_item) end # Should delete these no matter what @@ -169,6 +169,26 @@ defmodule Pinchflat.Media do Repo.delete(media_item) end + @doc """ + Deletes the tasks and media files associated with a media_item but leaves the + media_item in the database. Does not delete anything to do with associated metadata. + + ## Options: + - `:prevent_download` - If `true`, the media_item will be marked to prevent being redownloaded + + Returns {:ok, %MediaItem{}} | {:error, %Ecto.Changeset{}} + """ + def delete_media_files(%MediaItem{} = media_item, opts \\ []) do + prevent_download = Keyword.get(opts, :prevent_download, false) + filepath_attrs = MediaItem.filepath_attribute_defaults() + opt_attrs = %{prevent_download: prevent_download} + + Tasks.delete_tasks_for(media_item) + {:ok, _} = do_delete_media_files(media_item) + + update_media_item(media_item, Map.merge(filepath_attrs, opt_attrs)) + end + @doc """ Returns an `%Ecto.Changeset{}` for tracking media_item changes. """ @@ -176,7 +196,7 @@ defmodule Pinchflat.Media do MediaItem.changeset(media_item, attrs) end - defp delete_media_files(media_item) do + defp do_delete_media_files(media_item) do mapped_struct = Map.from_struct(media_item) MediaItem.filepath_attributes() @@ -203,6 +223,7 @@ defmodule Pinchflat.Media do defp matching_download_criteria_for(query, source_with_preloads) do query + |> MediaQuery.with_no_prevented_download() |> MediaQuery.with_no_media_filepath() |> MediaQuery.with_upload_date_after(source_with_preloads.download_cutoff_date) |> MediaQuery.with_format_preference(source_with_preloads.media_profile) diff --git a/lib/pinchflat/media/media_item.ex b/lib/pinchflat/media/media_item.ex index d5f1e75..04e7038 100644 --- a/lib/pinchflat/media/media_item.ex +++ b/lib/pinchflat/media/media_item.ex @@ -100,4 +100,14 @@ defmodule Pinchflat.Media.MediaItem do def filepath_attributes do ~w(media_filepath thumbnail_filepath metadata_filepath subtitle_filepaths nfo_filepath)a end + + @doc false + def filepath_attribute_defaults do + filepath_attributes() + |> Enum.map(fn + :subtitle_filepaths -> {:subtitle_filepaths, []} + field -> {field, nil} + end) + |> Enum.into(%{}) + end end diff --git a/lib/pinchflat/media/media_query.ex b/lib/pinchflat/media/media_query.ex index c43b5eb..30960d6 100644 --- a/lib/pinchflat/media/media_query.ex +++ b/lib/pinchflat/media/media_query.ex @@ -53,6 +53,10 @@ defmodule Pinchflat.Media.MediaQuery do where(query, [mi], mi.upload_date >= ^date) end + def with_no_prevented_download(query) do + where(query, [mi], mi.prevent_download == false) + end + def matching_title_regex(query, nil), do: query def matching_title_regex(query, regex) do 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()