Added function for updating a media item's filepaths on-disk
This commit is contained in:
parent
0163e85e76
commit
a3d3ff91ba
3 changed files with 85 additions and 12 deletions
|
|
@ -12,7 +12,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
alias Pinchflat.Tasks
|
alias Pinchflat.Tasks
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Media.FileDeletion
|
alias Pinchflat.Media.FileSyncing
|
||||||
alias Pinchflat.Downloading.MediaDownloader
|
alias Pinchflat.Downloading.MediaDownloader
|
||||||
|
|
||||||
alias Pinchflat.Lifecycle.UserScripts.CommandRunner, as: UserScriptRunner
|
alias Pinchflat.Lifecycle.UserScripts.CommandRunner, as: UserScriptRunner
|
||||||
|
|
@ -86,7 +86,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
media_redownloaded_at: get_redownloaded_at(is_quality_upgrade)
|
media_redownloaded_at: get_redownloaded_at(is_quality_upgrade)
|
||||||
})
|
})
|
||||||
|
|
||||||
:ok = FileDeletion.delete_outdated_files(media_item, updated_media_item)
|
:ok = FileSyncing.delete_outdated_files(media_item, updated_media_item)
|
||||||
run_user_script(:media_downloaded, updated_media_item)
|
run_user_script(:media_downloaded, updated_media_item)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
defmodule Pinchflat.Media.FileDeletion do
|
defmodule Pinchflat.Media.FileSyncing do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Functions for deleting files that are no longer needed by media items.
|
Functions for ensuring file state is accurately reflected in the database.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Utils.MapUtils
|
alias Pinchflat.Utils.MapUtils
|
||||||
alias Pinchflat.Media.MediaItem
|
alias Pinchflat.Media.MediaItem
|
||||||
alias Pinchflat.Utils.FilesystemUtils, as: FSUtils
|
alias Pinchflat.Utils.FilesystemUtils, as: FSUtils
|
||||||
|
|
@ -34,6 +35,22 @@ defmodule Pinchflat.Media.FileDeletion do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Nillifies any media item filepaths that don't exist on disk for a list of media items
|
||||||
|
|
||||||
|
returns [%MediaItem{}]
|
||||||
|
"""
|
||||||
|
def sync_file_presence_on_disk(media_items) do
|
||||||
|
Enum.map(media_items, fn media_item ->
|
||||||
|
new_attributes = sync_media_item_files(media_item)
|
||||||
|
# Doing this one-by-one instead of batching since this process
|
||||||
|
# can take time and a batch could let MediaItem state get out of sync
|
||||||
|
{:ok, updated_media_item} = Media.update_media_item(media_item, new_attributes)
|
||||||
|
|
||||||
|
updated_media_item
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp handle_file_deletion(old_attributes, new_attributes) do
|
defp handle_file_deletion(old_attributes, new_attributes) do
|
||||||
# The logic:
|
# The logic:
|
||||||
# - A file should only be deleted if it exists and the new file is different
|
# - A file should only be deleted if it exists and the new file is different
|
||||||
|
|
@ -51,4 +68,26 @@ defmodule Pinchflat.Media.FileDeletion do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp sync_media_item_files(media_item) do
|
||||||
|
non_subtitle_keys = MediaItem.filepath_attributes() -- [:subtitle_filepaths]
|
||||||
|
subtitle_keys = MapUtils.from_nested_list(media_item.subtitle_filepaths)
|
||||||
|
non_subtitles = Map.take(media_item, non_subtitle_keys)
|
||||||
|
|
||||||
|
# This one is checking for the negative (ie: only update if the file doesn't exist)
|
||||||
|
new_non_subtitle_attrs =
|
||||||
|
Enum.reduce(non_subtitles, %{}, fn {key, filepath}, acc ->
|
||||||
|
if filepath && File.exists?(filepath), do: acc, else: Map.put(acc, key, nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
# This one is checking for the positive (ie: only update if the file exists)
|
||||||
|
# This is because subtitles, being an array type in the DB, are most easily updated
|
||||||
|
# by a full replacement rather than finding the actual diff
|
||||||
|
new_subtitle_attrs =
|
||||||
|
Enum.reduce(subtitle_keys, [], fn {key, filepath}, acc ->
|
||||||
|
if filepath && File.exists?(filepath), do: acc ++ [[key, filepath]], else: acc
|
||||||
|
end)
|
||||||
|
|
||||||
|
Map.put(new_non_subtitle_attrs, :subtitle_filepaths, new_subtitle_attrs)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
defmodule Pinchflat.Media.FileDeletionTest do
|
defmodule Pinchflat.Media.FileSyncingTest do
|
||||||
use Pinchflat.DataCase
|
use Pinchflat.DataCase
|
||||||
|
|
||||||
import Pinchflat.MediaFixtures
|
import Pinchflat.MediaFixtures
|
||||||
|
|
||||||
alias Pinchflat.Media.FileDeletion
|
alias Pinchflat.Media.FileSyncing
|
||||||
|
|
||||||
describe "delete_outdated_files/2" do
|
describe "delete_outdated_files/2" do
|
||||||
test "deletes outdated non-subtitle files" do
|
test "deletes outdated non-subtitle files" do
|
||||||
new_media_item = media_item_with_attachments()
|
new_media_item = media_item_with_attachments()
|
||||||
old_media_item = media_item_with_attachments()
|
old_media_item = media_item_with_attachments()
|
||||||
|
|
||||||
assert :ok = FileDeletion.delete_outdated_files(old_media_item, new_media_item)
|
assert :ok = FileSyncing.delete_outdated_files(old_media_item, new_media_item)
|
||||||
|
|
||||||
assert File.exists?(new_media_item.media_filepath)
|
assert File.exists?(new_media_item.media_filepath)
|
||||||
refute File.exists?(old_media_item.media_filepath)
|
refute File.exists?(old_media_item.media_filepath)
|
||||||
|
|
@ -20,7 +20,7 @@ defmodule Pinchflat.Media.FileDeletionTest do
|
||||||
new_media_item = media_item_with_attachments()
|
new_media_item = media_item_with_attachments()
|
||||||
old_media_item = media_item_fixture(%{media_filepath: new_media_item.media_filepath})
|
old_media_item = media_item_fixture(%{media_filepath: new_media_item.media_filepath})
|
||||||
|
|
||||||
assert :ok = FileDeletion.delete_outdated_files(old_media_item, new_media_item)
|
assert :ok = FileSyncing.delete_outdated_files(old_media_item, new_media_item)
|
||||||
|
|
||||||
assert File.exists?(new_media_item.media_filepath)
|
assert File.exists?(new_media_item.media_filepath)
|
||||||
assert File.exists?(old_media_item.media_filepath)
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
|
|
@ -30,7 +30,7 @@ defmodule Pinchflat.Media.FileDeletionTest do
|
||||||
new_media_item = media_item_fixture(%{media_filepath: nil})
|
new_media_item = media_item_fixture(%{media_filepath: nil})
|
||||||
old_media_item = media_item_with_attachments()
|
old_media_item = media_item_with_attachments()
|
||||||
|
|
||||||
assert :ok = FileDeletion.delete_outdated_files(old_media_item, new_media_item)
|
assert :ok = FileSyncing.delete_outdated_files(old_media_item, new_media_item)
|
||||||
|
|
||||||
assert File.exists?(old_media_item.media_filepath)
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
end
|
end
|
||||||
|
|
@ -39,7 +39,7 @@ defmodule Pinchflat.Media.FileDeletionTest do
|
||||||
new_media_item = media_item_with_attachments()
|
new_media_item = media_item_with_attachments()
|
||||||
old_media_item = media_item_with_attachments()
|
old_media_item = media_item_with_attachments()
|
||||||
|
|
||||||
assert :ok = FileDeletion.delete_outdated_files(old_media_item, new_media_item)
|
assert :ok = FileSyncing.delete_outdated_files(old_media_item, new_media_item)
|
||||||
|
|
||||||
assert File.exists?(get_subtitle_filepath(new_media_item, "en"))
|
assert File.exists?(get_subtitle_filepath(new_media_item, "en"))
|
||||||
refute File.exists?(get_subtitle_filepath(old_media_item, "en"))
|
refute File.exists?(get_subtitle_filepath(old_media_item, "en"))
|
||||||
|
|
@ -49,7 +49,7 @@ defmodule Pinchflat.Media.FileDeletionTest do
|
||||||
new_media_item = media_item_with_attachments()
|
new_media_item = media_item_with_attachments()
|
||||||
old_media_item = media_item_fixture(%{subtitle_filepaths: new_media_item.subtitle_filepaths})
|
old_media_item = media_item_fixture(%{subtitle_filepaths: new_media_item.subtitle_filepaths})
|
||||||
|
|
||||||
assert :ok = FileDeletion.delete_outdated_files(old_media_item, new_media_item)
|
assert :ok = FileSyncing.delete_outdated_files(old_media_item, new_media_item)
|
||||||
|
|
||||||
assert File.exists?(get_subtitle_filepath(new_media_item, "en"))
|
assert File.exists?(get_subtitle_filepath(new_media_item, "en"))
|
||||||
assert File.exists?(get_subtitle_filepath(old_media_item, "en"))
|
assert File.exists?(get_subtitle_filepath(old_media_item, "en"))
|
||||||
|
|
@ -59,12 +59,46 @@ defmodule Pinchflat.Media.FileDeletionTest do
|
||||||
new_media_item = media_item_fixture(%{subtitle_filepaths: []})
|
new_media_item = media_item_fixture(%{subtitle_filepaths: []})
|
||||||
old_media_item = media_item_with_attachments()
|
old_media_item = media_item_with_attachments()
|
||||||
|
|
||||||
assert :ok = FileDeletion.delete_outdated_files(old_media_item, new_media_item)
|
assert :ok = FileSyncing.delete_outdated_files(old_media_item, new_media_item)
|
||||||
|
|
||||||
assert File.exists?(get_subtitle_filepath(old_media_item, "en"))
|
assert File.exists?(get_subtitle_filepath(old_media_item, "en"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "sync_file_presence_on_disk/1" do
|
||||||
|
test "removes attributes whose files are missing" do
|
||||||
|
media_item = media_item_fixture(%{media_filepath: "/tmp/missing_file.mp4"})
|
||||||
|
|
||||||
|
assert media_item.media_filepath
|
||||||
|
assert [updated_media_item] = FileSyncing.sync_file_presence_on_disk([media_item])
|
||||||
|
refute updated_media_item.media_filepath
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't remove attributes where the files still exist" do
|
||||||
|
media_item = media_item_with_attachments()
|
||||||
|
|
||||||
|
assert media_item.media_filepath
|
||||||
|
assert [updated_media_item] = FileSyncing.sync_file_presence_on_disk([media_item])
|
||||||
|
assert updated_media_item.media_filepath
|
||||||
|
end
|
||||||
|
|
||||||
|
test "removes subtitle files that are missing" do
|
||||||
|
media_item = media_item_fixture(%{subtitle_filepaths: [["en", "/tmp/missing_file.srt"]]})
|
||||||
|
|
||||||
|
assert get_subtitle_filepath(media_item, "en")
|
||||||
|
assert [updated_media_item] = FileSyncing.sync_file_presence_on_disk([media_item])
|
||||||
|
refute get_subtitle_filepath(updated_media_item, "en")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't remove subtitle files that still exist" do
|
||||||
|
media_item = media_item_with_attachments()
|
||||||
|
|
||||||
|
assert get_subtitle_filepath(media_item, "en")
|
||||||
|
assert [updated_media_item] = FileSyncing.sync_file_presence_on_disk([media_item])
|
||||||
|
assert get_subtitle_filepath(updated_media_item, "en")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp get_subtitle_filepath(media_item, language) do
|
defp get_subtitle_filepath(media_item, language) do
|
||||||
Enum.reduce_while(media_item.subtitle_filepaths, nil, fn [lang, filepath], acc ->
|
Enum.reduce_while(media_item.subtitle_filepaths, nil, fn [lang, filepath], acc ->
|
||||||
if lang == language do
|
if lang == language do
|
||||||
Loading…
Reference in a new issue