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.Repo
|
||||||
alias Pinchflat.Settings
|
alias Pinchflat.Settings
|
||||||
|
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||||
|
|
||||||
def start_link(opts \\ []) do
|
def start_link(opts \\ []) do
|
||||||
GenServer.start_link(__MODULE__, %{}, opts)
|
GenServer.start_link(__MODULE__, %{}, opts)
|
||||||
|
|
@ -31,6 +32,7 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
||||||
@impl true
|
@impl true
|
||||||
def init(state) do
|
def init(state) do
|
||||||
apply_default_settings()
|
apply_default_settings()
|
||||||
|
ensure_directories_are_writeable()
|
||||||
rename_old_job_workers()
|
rename_old_job_workers()
|
||||||
|
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
|
|
@ -41,6 +43,21 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
||||||
Settings.fetch!(:pro_enabled, false)
|
Settings.fetch!(:pro_enabled, false)
|
||||||
end
|
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
|
# 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
|
# 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
|
# 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
|
err
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@ defmodule Pinchflat.Media do
|
||||||
|
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Tasks
|
alias Pinchflat.Tasks
|
||||||
alias Pinchflat.Media.MediaItem
|
|
||||||
alias Pinchflat.Sources.Source
|
alias Pinchflat.Sources.Source
|
||||||
|
alias Pinchflat.Media.MediaItem
|
||||||
alias Pinchflat.Metadata.MediaMetadata
|
alias Pinchflat.Metadata.MediaMetadata
|
||||||
|
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of media_items.
|
Returns the list of media_items.
|
||||||
|
|
@ -254,16 +255,7 @@ defmodule Pinchflat.Media do
|
||||||
media_item
|
media_item
|
||||||
|> media_filepaths()
|
|> media_filepaths()
|
||||||
|> Enum.concat(metadata_filepaths(media_item))
|
|> Enum.concat(metadata_filepaths(media_item))
|
||||||
|> Enum.each(&File.rm/1)
|
|> Enum.each(&FilesystemHelpers.delete_file_and_remove_empty_directories/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
|
|
||||||
|
|
||||||
{:ok, media_item}
|
{:ok, media_item}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ defmodule Pinchflat.Sources do
|
||||||
alias Pinchflat.Profiles.MediaProfile
|
alias Pinchflat.Profiles.MediaProfile
|
||||||
alias Pinchflat.YtDlp.MediaCollection
|
alias Pinchflat.YtDlp.MediaCollection
|
||||||
alias Pinchflat.Metadata.SourceMetadata
|
alias Pinchflat.Metadata.SourceMetadata
|
||||||
|
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||||
alias Pinchflat.Downloading.DownloadingHelpers
|
alias Pinchflat.Downloading.DownloadingHelpers
|
||||||
alias Pinchflat.FastIndexing.FastIndexingHelpers
|
alias Pinchflat.FastIndexing.FastIndexingHelpers
|
||||||
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
||||||
|
|
@ -108,8 +109,8 @@ defmodule Pinchflat.Sources do
|
||||||
Media.delete_media_item(media_item, delete_files: delete_files)
|
Media.delete_media_item(media_item, delete_files: delete_files)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Tasks.delete_tasks_for(source)
|
|
||||||
delete_source_metadata_files(source)
|
delete_source_metadata_files(source)
|
||||||
|
Tasks.delete_tasks_for(source)
|
||||||
Repo.delete(source)
|
Repo.delete(source)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -149,7 +150,7 @@ defmodule Pinchflat.Sources do
|
||||||
|> Enum.map(fn field -> mapped_struct[field] end)
|
|> Enum.map(fn field -> mapped_struct[field] end)
|
||||||
|> Enum.filter(&is_binary/1)
|
|> Enum.filter(&is_binary/1)
|
||||||
|
|
||||||
Enum.each(filepaths, &File.rm/1)
|
Enum.each(filepaths, &FilesystemHelpers.delete_file_and_remove_empty_directories/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_source_details_to_changeset(source, changeset) do
|
defp add_source_details_to_changeset(source, changeset) do
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,55 @@ defmodule Pinchflat.Filesystem.FilesystemHelpersTest do
|
||||||
File.rm!(filepath)
|
File.rm!(filepath)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue