From eef5d771c9dc2dfc8dd5cdf03878301b09c9b406 Mon Sep 17 00:00:00 2001 From: Kieran Eglin Date: Thu, 26 Sep 2024 12:31:58 -0700 Subject: [PATCH] Turned the file sync into a job and properly hooked it up to the controller --- lib/pinchflat/media/file_syncing_worker.ex | 38 +++++++++++++++++++ .../controllers/sources/source_controller.ex | 5 +-- test/pinchflat/media/file_syncing_test.exs | 11 ++++++ .../media/file_syncing_worker_test.exs | 37 ++++++++++++++++++ .../controllers/source_controller_test.exs | 18 +++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 lib/pinchflat/media/file_syncing_worker.ex create mode 100644 test/pinchflat/media/file_syncing_worker_test.exs diff --git a/lib/pinchflat/media/file_syncing_worker.ex b/lib/pinchflat/media/file_syncing_worker.ex new file mode 100644 index 0000000..fde7e73 --- /dev/null +++ b/lib/pinchflat/media/file_syncing_worker.ex @@ -0,0 +1,38 @@ +defmodule Pinchflat.Media.FileSyncingWorker do + @moduledoc false + + use Oban.Worker, + queue: :local_data, + tags: ["sources", "local_data"] + + alias __MODULE__ + alias Pinchflat.Repo + alias Pinchflat.Tasks + alias Pinchflat.Sources + alias Pinchflat.Media.FileSyncing + + @doc """ + Starts the source file syncing worker. + + Returns {:ok, %Task{}} | {:error, %Ecto.Changeset{}} + """ + def kickoff_with_task(source, opts \\ []) do + %{id: source.id} + |> FileSyncingWorker.new(opts) + |> Tasks.create_job_with_task(source) + end + + @doc """ + Deletes a profile and optionally deletes its files + + Returns :ok + """ + @impl Oban.Worker + def perform(%Oban.Job{args: %{"id" => source_id}}) do + source = Repo.preload(Sources.get_source!(source_id), :media_items) + + FileSyncing.sync_file_presence_on_disk(source.media_items) + + :ok + end +end diff --git a/lib/pinchflat_web/controllers/sources/source_controller.ex b/lib/pinchflat_web/controllers/sources/source_controller.ex index 26d47f7..9c48cec 100644 --- a/lib/pinchflat_web/controllers/sources/source_controller.ex +++ b/lib/pinchflat_web/controllers/sources/source_controller.ex @@ -8,6 +8,7 @@ defmodule PinchflatWeb.Sources.SourceController do alias Pinchflat.Sources.Source alias Pinchflat.Media.MediaItem alias Pinchflat.Profiles.MediaProfile + alias Pinchflat.Media.FileSyncingWorker alias Pinchflat.Sources.SourceDeletionWorker alias Pinchflat.Downloading.DownloadingHelpers alias Pinchflat.SlowIndexing.SlowIndexingHelpers @@ -175,14 +176,12 @@ defmodule PinchflatWeb.Sources.SourceController do ) end - # TODO: test - # TODO: update the job that's running def sync_files_on_disk(conn, %{"source_id" => id}) do wrap_forced_action( conn, id, "File sync enqueued.", - &SourceMetadataStorageWorker.kickoff_with_task/1 + &FileSyncingWorker.kickoff_with_task/1 ) end diff --git a/test/pinchflat/media/file_syncing_test.exs b/test/pinchflat/media/file_syncing_test.exs index da354bd..c69c795 100644 --- a/test/pinchflat/media/file_syncing_test.exs +++ b/test/pinchflat/media/file_syncing_test.exs @@ -82,6 +82,17 @@ defmodule Pinchflat.Media.FileSyncingTest do assert updated_media_item.media_filepath end + test "doesn't touch other attributes if some are missing and some aren't" do + media_item = media_item_with_attachments() + File.rm(media_item.media_filepath) + + assert media_item.thumbnail_filepath + assert media_item.media_filepath + assert [updated_media_item] = FileSyncing.sync_file_presence_on_disk([media_item]) + assert updated_media_item.thumbnail_filepath + refute 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"]]}) diff --git a/test/pinchflat/media/file_syncing_worker_test.exs b/test/pinchflat/media/file_syncing_worker_test.exs new file mode 100644 index 0000000..37ebafa --- /dev/null +++ b/test/pinchflat/media/file_syncing_worker_test.exs @@ -0,0 +1,37 @@ +defmodule Pinchflat.Media.FileSyncingWorkerTest do + use Pinchflat.DataCase + + import Pinchflat.MediaFixtures + import Pinchflat.SourcesFixtures + + alias Pinchflat.Media.FileSyncingWorker + + describe "kickoff_with_task/3" do + test "starts the worker" do + source = source_fixture() + + assert [] = all_enqueued(worker: FileSyncingWorker) + assert {:ok, _} = FileSyncingWorker.kickoff_with_task(source) + assert [_] = all_enqueued(worker: FileSyncingWorker) + end + + test "attaches a task" do + source = source_fixture() + + assert {:ok, task} = FileSyncingWorker.kickoff_with_task(source) + assert task.source_id == source.id + end + end + + describe "perform/1" do + test "syncs file presence on disk" do + source = source_fixture() + media_item = media_item_fixture(%{media_filepath: "/tmp/missing.mp4", source_id: source.id}) + + perform_job(FileSyncingWorker, %{"id" => source.id}) + updated_media_item = Repo.reload!(media_item) + + refute updated_media_item.media_filepath + end + end +end diff --git a/test/pinchflat_web/controllers/source_controller_test.exs b/test/pinchflat_web/controllers/source_controller_test.exs index 5d532da..5a6e0e7 100644 --- a/test/pinchflat_web/controllers/source_controller_test.exs +++ b/test/pinchflat_web/controllers/source_controller_test.exs @@ -7,6 +7,7 @@ defmodule PinchflatWeb.SourceControllerTest do alias Pinchflat.Repo alias Pinchflat.Settings + alias Pinchflat.Media.FileSyncingWorker alias Pinchflat.Sources.SourceDeletionWorker alias Pinchflat.Downloading.MediaDownloadWorker alias Pinchflat.Metadata.SourceMetadataStorageWorker @@ -268,6 +269,23 @@ defmodule PinchflatWeb.SourceControllerTest do end end + describe "sync_files_on_disk" do + test "forces a file sync", %{conn: conn} do + source = source_fixture() + + assert [] = all_enqueued(worker: FileSyncingWorker) + post(conn, ~p"/sources/#{source.id}/sync_files_on_disk") + assert [_] = all_enqueued(worker: FileSyncingWorker) + end + + test "redirects to the source page", %{conn: conn} do + source = source_fixture() + + conn = post(conn, ~p"/sources/#{source.id}/sync_files_on_disk") + assert redirected_to(conn) == ~p"/sources/#{source.id}" + end + end + defp create_source(_) do source = source_fixture() media_item = media_item_with_attachments(%{source_id: source.id})