Added way to track media filesize (#48)

This commit is contained in:
Kieran 2024-03-02 19:19:10 -08:00 committed by GitHub
parent 0f40dcf5dd
commit b370c97bfb
10 changed files with 141 additions and 6 deletions

View file

@ -39,7 +39,7 @@ config :pinchflat, Oban,
# Keep old jobs for 30 days for display in the UI
plugins: [{Oban.Plugins.Pruner, max_age: 30 * 24 * 60 * 60}],
# TODO: consider making this an env var or something?
queues: [default: 10, media_indexing: 2, media_fetching: 2]
queues: [default: 10, media_indexing: 2, media_fetching: 2, media_local_metadata: 8]
# Configures the mailer
#

View file

@ -22,6 +22,7 @@ defmodule Pinchflat.Media.MediaItem do
# these fields are captured on download
:media_downloaded_at,
:media_filepath,
:media_size_bytes,
:subtitle_filepaths,
:thumbnail_filepath,
:metadata_filepath
@ -37,6 +38,7 @@ defmodule Pinchflat.Media.MediaItem do
field :media_downloaded_at, :utc_datetime
field :media_filepath, :string
field :media_size_bytes, :integer
field :thumbnail_filepath, :string
field :metadata_filepath, :string
# This is an array of [iso-2 language, filepath] pairs. Probably could

View file

@ -0,0 +1,22 @@
defmodule Pinchflat.Tasks.MediaItemTasks do
@moduledoc """
This module contains methods used by or used to control tasks (aka workers)
related to media items.
"""
alias Pinchflat.Media
@doc """
Fetches the file size of a media item and saves it to the database.
Returns {:ok, media_item} | {:error, any()}
"""
def compute_and_save_media_filesize(media_item) do
case File.stat(media_item.media_filepath) do
{:ok, %{size: size}} ->
Media.update_media_item(media_item, %{media_size_bytes: size})
err ->
err
end
end
end

View file

@ -1,6 +1,7 @@
defmodule Pinchflat.Tasks.SourceTasks do
@moduledoc """
This module contains methods for managing tasks (workers) related to sources.
This module contains methods used by or used to control tasks (aka workers)
related to sources.
"""
alias Pinchflat.Media

View file

@ -0,0 +1,27 @@
defmodule Pinchflat.Workers.FilesystemDataWorker do
@moduledoc false
use Oban.Worker,
queue: :media_local_metadata,
tags: ["media_item", "media_metadata", "local_metadata"],
max_attempts: 1
alias Pinchflat.Media
alias Pinchflat.Tasks.MediaItemTasks
@impl Oban.Worker
@doc """
For a given media item, compute and save metadata about the file on-disk.
Returns :ok
"""
def perform(%Oban.Job{args: %{"id" => media_item_id}}) do
media_item = Media.get_media_item!(media_item_id)
MediaItemTasks.compute_and_save_media_filesize(media_item)
# Don't retry on failure - if it didn't work immediately there's no
# reason to believe it will work later.
:ok
end
end

View file

@ -8,7 +8,9 @@ defmodule Pinchflat.Workers.VideoDownloadWorker do
alias Pinchflat.Repo
alias Pinchflat.Media
alias Pinchflat.Tasks
alias Pinchflat.MediaClient.VideoDownloader
alias Pinchflat.Workers.FilesystemDataWorker
@impl Oban.Worker
@doc """
@ -25,16 +27,31 @@ defmodule Pinchflat.Workers.VideoDownloadWorker do
# If the source is set to not download media, perform a no-op
if media_item.source.download_media do
download_media(media_item)
download_media_and_schedule_jobs(media_item)
else
:ok
end
end
defp download_media(media_item) do
defp download_media_and_schedule_jobs(media_item) do
case VideoDownloader.download_for_media_item(media_item) do
{:ok, _} -> {:ok, media_item}
err -> err
{:ok, _} ->
schedule_filesystem_data_worker(media_item)
{:ok, media_item}
err ->
err
end
end
defp schedule_filesystem_data_worker(media_item) do
media_item
|> Map.take([:id])
|> FilesystemDataWorker.new()
|> Tasks.create_job_with_task(media_item)
|> case do
{:ok, task} -> {:ok, task}
{:error, :duplicate_job} -> {:ok, :job_exists}
end
end
end

View file

@ -0,0 +1,9 @@
defmodule Pinchflat.Repo.Migrations.AddMediaSizeToMediaItem do
use Ecto.Migration
def change do
alter table(:media_items) do
add :media_size_bytes, :integer
end
end
end

View file

@ -0,0 +1,25 @@
defmodule Pinchflat.Tasks.MediaItemTasksTest do
use Pinchflat.DataCase
import Pinchflat.MediaFixtures
alias Pinchflat.Tasks.MediaItemTasks
describe "compute_and_save_media_filesize/1" do
test "updates the media item with the file size" do
media_item = media_item_with_attachments()
refute media_item.media_size_bytes
assert {:ok, media_item} = MediaItemTasks.compute_and_save_media_filesize(media_item)
assert Repo.reload!(media_item).media_size_bytes
end
test "returns the error if operation fails" do
media_item = media_item_fixture(%{media_filepath: "/nonexistent/file.mkv"})
assert {:error, _} = MediaItemTasks.compute_and_save_media_filesize(media_item)
end
end
end

View file

@ -0,0 +1,19 @@
defmodule Pinchflat.Workers.FilesystemDataWorkerTest do
use Pinchflat.DataCase
import Pinchflat.MediaFixtures
alias Pinchflat.Workers.FilesystemDataWorker
describe "perform/1" do
test "Computes and stores the media file size" do
media_item = media_item_with_attachments()
refute media_item.media_size_bytes
perform_job(FilesystemDataWorker, %{id: media_item.id})
assert Repo.reload!(media_item).media_size_bytes
end
end
end

View file

@ -6,6 +6,7 @@ defmodule Pinchflat.Workers.VideoDownloadWorkerTest do
alias Pinchflat.Sources
alias Pinchflat.Workers.VideoDownloadWorker
alias Pinchflat.Workers.FilesystemDataWorker
setup :verify_on_exit!
@ -68,5 +69,17 @@ defmodule Pinchflat.Workers.VideoDownloadWorkerTest do
perform_job(VideoDownloadWorker, %{id: media_item.id})
end
test "it schedules a filesystem data worker", %{media_item: media_item} do
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
{:ok, render_metadata(:media_metadata)}
end)
assert [] = all_enqueued(worker: FilesystemDataWorker)
perform_job(VideoDownloadWorker, %{id: media_item.id})
assert [_] = all_enqueued(worker: FilesystemDataWorker)
end
end
end