diff --git a/lib/pinchflat/boot/nfo_backfill_worker.ex b/lib/pinchflat/boot/nfo_backfill_worker.ex new file mode 100644 index 0000000..3793e7f --- /dev/null +++ b/lib/pinchflat/boot/nfo_backfill_worker.ex @@ -0,0 +1,66 @@ +defmodule Pinchflat.Boot.NfoBackfillWorker do + @moduledoc false + + use Oban.Worker, + queue: :local_metadata, + # This should have it running once _ever_ (until the job is pruned, anyway) + # NOTE: remove within the next month + unique: [period: :infinity, states: Oban.Job.states()], + tags: ["media_item", "media_metadata", "local_metadata", "data_backfill"] + + import Ecto.Query, warn: false + require Logger + + alias Pinchflat.Repo + alias Pinchflat.Media + alias Pinchflat.Media.MediaItem + alias Pinchflat.Metadata.NfoBuilder + alias Pinchflat.Metadata.MetadataFileHelpers + + @doc """ + Runs a one-off backfill job to regenerate NFO files for media items that have + both an NFO file and a metadata file. This is needed because NFO files weren't + escaping characters properly so we need to regenerate them. + + This job will only run once as long as I remove it before the jobs are pruned in a month. + + Returns :ok + """ + @impl Oban.Worker + def perform(%Oban.Job{}) do + Logger.info("Running NFO backfill worker") + + media_items = get_media_items_to_backfill() + + Enum.each(media_items, fn media_item -> + nfo_exists = File.exists?(media_item.nfo_filepath) + metadata_exists = File.exists?(media_item.metadata.metadata_filepath) + + if nfo_exists && metadata_exists do + Logger.info("NFO and metadata exist for media item #{media_item.id} - proceeding") + + regenerate_nfo_for_media_item(media_item) + end + end) + + :ok + end + + defp get_media_items_to_backfill do + from(m in MediaItem, where: not is_nil(m.nfo_filepath)) + |> Repo.all() + |> Repo.preload([:metadata, source: :media_profile]) + end + + defp regenerate_nfo_for_media_item(media_item) do + case MetadataFileHelpers.read_compressed_metadata(media_item.metadata.metadata_filepath) do + {:ok, metadata} -> + Media.update_media_item(media_item, %{ + nfo_filepath: NfoBuilder.build_and_store_for_media_item(media_item.nfo_filepath, metadata) + }) + + _err -> + Logger.error("Failed to read metadata for media item #{media_item.id}") + end + end +end diff --git a/lib/pinchflat/boot/post_job_startup_tasks.ex b/lib/pinchflat/boot/post_job_startup_tasks.ex index 867c300..ab69da7 100644 --- a/lib/pinchflat/boot/post_job_startup_tasks.ex +++ b/lib/pinchflat/boot/post_job_startup_tasks.ex @@ -7,6 +7,9 @@ defmodule Pinchflat.Boot.PostJobStartupTasks do Phoenix supervision tree. """ + alias Pinchflat.Repo + alias Pinchflat.Boot.NfoBackfillWorker + # restart: :temporary means that this process will never be restarted (ie: will run once and then die) use GenServer, restart: :temporary import Ecto.Query, warn: false @@ -26,7 +29,8 @@ defmodule Pinchflat.Boot.PostJobStartupTasks do """ @impl true def init(state) do - # Empty for now, keeping because tasks _will_ be added in future + Repo.insert_unique_job(NfoBackfillWorker.new(%{})) + {:ok, state} end end