Adds method to improve cleanup of empty directories
This commit is contained in:
parent
0f3329e97d
commit
0de5ea901d
5 changed files with 106 additions and 13 deletions
|
|
@ -14,6 +14,7 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
|||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Settings
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
def start_link(opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, %{}, opts)
|
||||
|
|
@ -31,6 +32,7 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
|||
@impl true
|
||||
def init(state) do
|
||||
apply_default_settings()
|
||||
ensure_directories_are_writeable()
|
||||
rename_old_job_workers()
|
||||
|
||||
{:ok, state}
|
||||
|
|
@ -41,6 +43,21 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
|||
Settings.fetch!(:pro_enabled, false)
|
||||
end
|
||||
|
||||
defp ensure_directories_are_writeable do
|
||||
directories = [
|
||||
Application.get_env(:pinchflat, :media_directory),
|
||||
Application.get_env(:pinchflat, :tmpfile_directory),
|
||||
Application.get_env(:pinchflat, :metadata_directory)
|
||||
]
|
||||
|
||||
Enum.each(directories, fn dir ->
|
||||
file = Path.join([dir, ".keep"])
|
||||
|
||||
# This will fail if the directory is not writeable, stopping boot
|
||||
FilesystemHelpers.write_p!(file, "")
|
||||
end)
|
||||
end
|
||||
|
||||
# As part of a large refactor, I ended up moving a bunch of workers around. This
|
||||
# is a problem because the workers are stored in the database and the runner
|
||||
# will try to run the OLD jobs. This is also why these tasks run before the job
|
||||
|
|
|
|||
|
|
@ -48,4 +48,36 @@ defmodule Pinchflat.Filesystem.FilesystemHelpers do
|
|||
err
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a file and removes any empty directories in the path.
|
||||
Does NOT remove any directories that are not empty.
|
||||
|
||||
Returns :ok | {:error, any()}
|
||||
"""
|
||||
def delete_file_and_remove_empty_directories(filepath) do
|
||||
case File.rm(filepath) do
|
||||
:ok ->
|
||||
filepath
|
||||
|> Path.dirname()
|
||||
|> recursively_delete_empty_directories()
|
||||
|
||||
err ->
|
||||
err
|
||||
end
|
||||
end
|
||||
|
||||
defp recursively_delete_empty_directories(directory) do
|
||||
case File.rmdir(directory) do
|
||||
:ok ->
|
||||
directory
|
||||
|> Path.dirname()
|
||||
|> recursively_delete_empty_directories()
|
||||
|
||||
err ->
|
||||
err
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ defmodule Pinchflat.Media do
|
|||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Tasks
|
||||
alias Pinchflat.Media.MediaItem
|
||||
alias Pinchflat.Sources.Source
|
||||
alias Pinchflat.Media.MediaItem
|
||||
alias Pinchflat.Metadata.MediaMetadata
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
@doc """
|
||||
Returns the list of media_items.
|
||||
|
|
@ -254,16 +255,7 @@ defmodule Pinchflat.Media do
|
|||
media_item
|
||||
|> media_filepaths()
|
||||
|> Enum.concat(metadata_filepaths(media_item))
|
||||
|> Enum.each(&File.rm/1)
|
||||
|
||||
# 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
|
||||
|> Enum.each(&FilesystemHelpers.delete_file_and_remove_empty_directories/1)
|
||||
|
||||
{:ok, media_item}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ defmodule Pinchflat.Sources do
|
|||
alias Pinchflat.Profiles.MediaProfile
|
||||
alias Pinchflat.YtDlp.MediaCollection
|
||||
alias Pinchflat.Metadata.SourceMetadata
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
alias Pinchflat.Downloading.DownloadingHelpers
|
||||
alias Pinchflat.FastIndexing.FastIndexingHelpers
|
||||
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
||||
|
|
@ -108,8 +109,8 @@ defmodule Pinchflat.Sources do
|
|||
Media.delete_media_item(media_item, delete_files: delete_files)
|
||||
end)
|
||||
|
||||
Tasks.delete_tasks_for(source)
|
||||
delete_source_metadata_files(source)
|
||||
Tasks.delete_tasks_for(source)
|
||||
Repo.delete(source)
|
||||
end
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ defmodule Pinchflat.Sources do
|
|||
|> Enum.map(fn field -> mapped_struct[field] end)
|
||||
|> Enum.filter(&is_binary/1)
|
||||
|
||||
Enum.each(filepaths, &File.rm/1)
|
||||
Enum.each(filepaths, &FilesystemHelpers.delete_file_and_remove_empty_directories/1)
|
||||
end
|
||||
|
||||
defp add_source_details_to_changeset(source, changeset) do
|
||||
|
|
|
|||
|
|
@ -56,4 +56,55 @@ defmodule Pinchflat.Filesystem.FilesystemHelpersTest do
|
|||
File.rm!(filepath)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_file_and_remove_empty_directories/1" do
|
||||
test "deletes file at the provided filepath" do
|
||||
filepath = FilesystemHelpers.generate_metadata_tmpfile(:json)
|
||||
|
||||
assert File.exists?(filepath)
|
||||
|
||||
assert :ok = FilesystemHelpers.delete_file_and_remove_empty_directories(filepath)
|
||||
|
||||
refute File.exists?(filepath)
|
||||
end
|
||||
|
||||
test "deletes empty directories" do
|
||||
tmpfile_directory = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||
filepath = Path.join([tmpfile_directory, "foo", "bar", "baz", "qux.json"])
|
||||
FilesystemHelpers.write_p!(filepath, "")
|
||||
|
||||
assert :ok = FilesystemHelpers.delete_file_and_remove_empty_directories(filepath)
|
||||
|
||||
refute File.exists?(filepath)
|
||||
refute File.exists?(Path.join([tmpfile_directory, "foo", "bar", "baz"]))
|
||||
refute File.exists?(Path.join([tmpfile_directory, "foo", "bar"]))
|
||||
refute File.exists?(Path.join([tmpfile_directory, "foo"]))
|
||||
end
|
||||
|
||||
test "does not delete directories with other files in them" do
|
||||
tmpfile_directory = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||
filepath_1 = Path.join([tmpfile_directory, "foo", "bar", "baz", "qux.json"])
|
||||
filepath_2 = Path.join([tmpfile_directory, "foo", "baz.json"])
|
||||
FilesystemHelpers.write_p!(filepath_1, "")
|
||||
FilesystemHelpers.write_p!(filepath_2, "")
|
||||
|
||||
assert :ok = FilesystemHelpers.delete_file_and_remove_empty_directories(filepath_1)
|
||||
|
||||
refute File.exists?(filepath_1)
|
||||
refute File.exists?(Path.join([tmpfile_directory, "foo", "bar", "baz"]))
|
||||
refute File.exists?(Path.join([tmpfile_directory, "foo", "bar"]))
|
||||
|
||||
assert File.exists?(filepath_2)
|
||||
assert File.exists?(Path.join([tmpfile_directory, "foo"]))
|
||||
|
||||
# cleanup
|
||||
FilesystemHelpers.delete_file_and_remove_empty_directories(filepath_2)
|
||||
end
|
||||
|
||||
test "returns an error if file could not be deleted" do
|
||||
filepath = "/nonexistent/file.json"
|
||||
|
||||
assert {:error, _} = FilesystemHelpers.delete_file_and_remove_empty_directories(filepath)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue