[Enhancement] Ignore media based on user scripts (#330)

* Integrated pre-download callback for media items

* Refactored existing tests

* Docs, etc
This commit is contained in:
Kieran
2024-07-22 10:47:49 -07:00
committed by GitHub
parent d392fc3818
commit 7a01db05dd
12 changed files with 108 additions and 48 deletions
@@ -39,17 +39,14 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
- `quality_upgrade?`: re-downloads media, including the video. Does not force download
if the source is set to not download media
Returns :ok | {:ok, %MediaItem{}} | {:error, any, ...any}
Returns :ok | {:error, any, ...any}
"""
@impl Oban.Worker
def perform(%Oban.Job{args: %{"id" => media_item_id} = args}) do
should_force = Map.get(args, "force", false)
is_quality_upgrade = Map.get(args, "quality_upgrade?", false)
media_item =
media_item_id
|> Media.get_media_item!()
|> Repo.preload(:source)
media_item = fetch_and_run_prevent_download_user_script(media_item_id)
# If the source or media item is set to not download media, perform a no-op unless forced
if (media_item.source.download_media && !media_item.prevent_download) || should_force do
@@ -62,6 +59,20 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
Ecto.StaleEntryError -> Logger.info("#{__MODULE__} discarded: media item #{media_item_id} stale")
end
# If a user script exists and, when run, returns a non-zero exit code, prevent this and all future downloads
# of the media item.
defp fetch_and_run_prevent_download_user_script(media_item_id) do
media_item = Media.get_media_item!(media_item_id)
{:ok, media_item} =
case run_user_script(:media_pre_download, media_item) do
{:ok, _, exit_code} when exit_code != 0 -> Media.update_media_item(media_item, %{prevent_download: true})
_ -> {:ok, media_item}
end
Repo.preload(media_item, :source)
end
defp download_media_and_schedule_jobs(media_item, is_quality_upgrade, should_force) do
overwrite_behaviour = if should_force || is_quality_upgrade, do: :force_overwrites, else: :no_force_overwrites
override_opts = [overwrite_behaviour: overwrite_behaviour]
@@ -74,9 +85,9 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
media_redownloaded_at: get_redownloaded_at(is_quality_upgrade)
})
:ok = run_user_script(updated_media_item)
run_user_script(:media_downloaded, updated_media_item)
{:ok, updated_media_item}
:ok
{:recovered, _} ->
{:error, :retry}
@@ -112,9 +123,9 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
# NOTE: I like this pattern of using the default value so that I don't have to
# define it in config.exs (and friends). Consider using this elsewhere.
defp run_user_script(media_item) do
defp run_user_script(event, media_item) do
runner = Application.get_env(:pinchflat, :user_script_runner, UserScriptRunner)
runner.run(:media_downloaded, media_item)
runner.run(event, media_item)
end
end
@@ -12,6 +12,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
@behaviour UserScriptCommandRunner
@event_types [
:media_pre_download,
:media_downloaded,
:media_deleted
]
@@ -22,24 +23,25 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
This function will succeed in almost all cases, even if the user script command
failed - this is because I don't want bad scripts to stop the whole process.
If something fails, it'll be logged.
If something fails, it'll be logged and returned BUT the tuple will always
start with {:ok, ...}.
The only things that can cause a true failure are passing in an invalid event
type or if the passed data cannot be encoded into JSON - both indicative of
failures in the development process.
Returns :ok
Returns {:ok, :no_executable} | {:ok, output, exit_code}
"""
@impl UserScriptCommandRunner
def run(event_type, encodable_data) when event_type in @event_types do
case backend_executable() do
{:ok, :no_executable} ->
:ok
{:ok, :no_executable}
{:ok, executable_path} ->
{:ok, encoded_data} = Phoenix.json_library().encode(encodable_data)
{_output, _exit_code} =
{output, exit_code} =
CliUtils.wrap_cmd(
executable_path,
[to_string(event_type), encoded_data],
@@ -47,7 +49,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
logging_arg_override: "[suppressed]"
)
:ok
{:ok, output, exit_code}
end
end
@@ -62,7 +64,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
if FilesystemUtils.exists_and_nonempty?(filepath) do
{:ok, filepath}
else
Logger.warning("User scripts lifecyle file either not present or is empty. Skipping.")
Logger.info("User scripts lifecyle file either not present or is empty. Skipping.")
{:ok, :no_executable}
end
+2 -2
View File
@@ -171,7 +171,7 @@ defmodule Pinchflat.Media do
if delete_files do
{:ok, _} = do_delete_media_files(media_item)
:ok = run_user_script(:media_deleted, media_item)
run_user_script(:media_deleted, media_item)
end
# Should delete these no matter what
@@ -194,7 +194,7 @@ defmodule Pinchflat.Media do
Tasks.delete_tasks_for(media_item)
{:ok, _} = do_delete_media_files(media_item)
:ok = run_user_script(:media_deleted, media_item)
run_user_script(:media_deleted, media_item)
update_media_item(media_item, Map.merge(filepath_attrs, addl_attrs))
end