Added method for deleting media files and their content
This commit is contained in:
parent
060e340558
commit
a2b2d9e150
7 changed files with 122 additions and 6 deletions
|
|
@ -3,8 +3,8 @@ import Config
|
|||
config :pinchflat,
|
||||
# Specifying backend data here makes mocking and local testing SUPER easy
|
||||
yt_dlp_executable: Path.join([File.cwd!(), "/test/support/scripts/yt-dlp-mocks/repeater.sh"]),
|
||||
media_directory: Path.join([System.tmp_dir!(), "videos"]),
|
||||
metadata_directory: Path.join([System.tmp_dir!(), "metadata"])
|
||||
media_directory: Path.join([File.cwd!(), "tmp", "test", "videos"]),
|
||||
metadata_directory: Path.join([File.cwd!(), "tmp", "test", "metadata"])
|
||||
|
||||
config :pinchflat, Oban, testing: :manual
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,23 @@ defmodule Pinchflat.Media do
|
|||
"""
|
||||
def get_media_item!(id), do: Repo.get!(MediaItem, id)
|
||||
|
||||
@doc """
|
||||
Produces a flat list of the filesystem paths for a media_item's downloaded files
|
||||
|
||||
Returns [binary()]
|
||||
"""
|
||||
def media_filepaths(media_item) do
|
||||
filesystem_fields = ~w(media_filepath thumbnail_filepath metadata_filepath subtitle_filepaths)a
|
||||
mapped_struct = Map.from_struct(media_item)
|
||||
|
||||
filesystem_fields
|
||||
|> Enum.map(fn
|
||||
:subtitle_filepaths = field -> Enum.map(mapped_struct[field], fn [_, filepath] -> filepath end)
|
||||
field -> List.wrap(mapped_struct[field])
|
||||
end)
|
||||
|> List.flatten()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a media_item. Returns {:ok, %MediaItem{}} | {:error, %Ecto.Changeset{}}.
|
||||
"""
|
||||
|
|
@ -116,6 +133,28 @@ defmodule Pinchflat.Media do
|
|||
Repo.delete(media_item)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes the media_item and all associated files. Attempts to delete the root directory
|
||||
but only if it is empty.
|
||||
|
||||
Returns {:ok, %MediaItem{}}
|
||||
"""
|
||||
def delete_media_item_and_attachments(media_item) do
|
||||
root_directory = Path.dirname(media_item.media_filepath)
|
||||
|
||||
media_item
|
||||
|> media_filepaths()
|
||||
|> Enum.each(&File.rm/1)
|
||||
|
||||
delete_media_item(media_item)
|
||||
|
||||
# Fails if the directory is not empty
|
||||
case File.rmdir(root_directory) do
|
||||
:ok -> {:ok, media_item}
|
||||
{:error, :eexist} -> {:ok, media_item}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking media_item changes.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.CommandRunnerTest do
|
|||
test "it automatically includes the --print-to-file flag" do
|
||||
assert {:ok, output} = Runner.run(@video_url, [], "%(id)s")
|
||||
|
||||
assert String.contains?(output, "--print-to-file %(id)s /tmp/")
|
||||
assert String.contains?(output, "--print-to-file %(id)s /app/tmp/")
|
||||
end
|
||||
|
||||
test "it returns the output and status when the command fails" do
|
||||
|
|
|
|||
|
|
@ -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,41 @@ defmodule Pinchflat.MediaTest do
|
|||
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
|
||||
|
||||
test "deletes the parent folder if it is empty", %{media_item: media_item} do
|
||||
root_directory = Path.dirname(media_item.media_filepath)
|
||||
assert File.exists?(root_directory)
|
||||
assert {:ok, _} = Media.delete_media_item_and_attachments(media_item)
|
||||
refute File.exists?(root_directory)
|
||||
end
|
||||
|
||||
test "does not delete the parent folder if it is not empty", %{media_item: media_item} do
|
||||
root_directory = Path.dirname(media_item.media_filepath)
|
||||
File.touch(Path.join([root_directory, "test.txt"]))
|
||||
assert {:ok, _} = Media.delete_media_item_and_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 "change_media_item/1" do
|
||||
test "change_media_item/1 returns a media_item changeset" do
|
||||
media_item = media_item_fixture()
|
||||
|
|
|
|||
|
|
@ -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, "/app/tmp/test/videos/%(title)S.%(ext)s"} in res
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
BIN
test/support/files/media.mkv
Normal file
BIN
test/support/files/media.mkv
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue