Added way to track media filesize (#48)
This commit is contained in:
parent
0f40dcf5dd
commit
b370c97bfb
10 changed files with 141 additions and 6 deletions
|
|
@ -39,7 +39,7 @@ config :pinchflat, Oban,
|
||||||
# Keep old jobs for 30 days for display in the UI
|
# Keep old jobs for 30 days for display in the UI
|
||||||
plugins: [{Oban.Plugins.Pruner, max_age: 30 * 24 * 60 * 60}],
|
plugins: [{Oban.Plugins.Pruner, max_age: 30 * 24 * 60 * 60}],
|
||||||
# TODO: consider making this an env var or something?
|
# 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
|
# Configures the mailer
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ defmodule Pinchflat.Media.MediaItem do
|
||||||
# these fields are captured on download
|
# these fields are captured on download
|
||||||
:media_downloaded_at,
|
:media_downloaded_at,
|
||||||
:media_filepath,
|
:media_filepath,
|
||||||
|
:media_size_bytes,
|
||||||
:subtitle_filepaths,
|
:subtitle_filepaths,
|
||||||
:thumbnail_filepath,
|
:thumbnail_filepath,
|
||||||
:metadata_filepath
|
:metadata_filepath
|
||||||
|
|
@ -37,6 +38,7 @@ defmodule Pinchflat.Media.MediaItem do
|
||||||
field :media_downloaded_at, :utc_datetime
|
field :media_downloaded_at, :utc_datetime
|
||||||
|
|
||||||
field :media_filepath, :string
|
field :media_filepath, :string
|
||||||
|
field :media_size_bytes, :integer
|
||||||
field :thumbnail_filepath, :string
|
field :thumbnail_filepath, :string
|
||||||
field :metadata_filepath, :string
|
field :metadata_filepath, :string
|
||||||
# This is an array of [iso-2 language, filepath] pairs. Probably could
|
# This is an array of [iso-2 language, filepath] pairs. Probably could
|
||||||
|
|
|
||||||
22
lib/pinchflat/tasks/media_item_tasks.ex
Normal file
22
lib/pinchflat/tasks/media_item_tasks.ex
Normal 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
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule Pinchflat.Tasks.SourceTasks do
|
defmodule Pinchflat.Tasks.SourceTasks do
|
||||||
@moduledoc """
|
@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
|
alias Pinchflat.Media
|
||||||
|
|
|
||||||
27
lib/pinchflat/workers/filesystem_data_worker.ex
Normal file
27
lib/pinchflat/workers/filesystem_data_worker.ex
Normal 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
|
||||||
|
|
@ -8,7 +8,9 @@ defmodule Pinchflat.Workers.VideoDownloadWorker do
|
||||||
|
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
|
alias Pinchflat.Tasks
|
||||||
alias Pinchflat.MediaClient.VideoDownloader
|
alias Pinchflat.MediaClient.VideoDownloader
|
||||||
|
alias Pinchflat.Workers.FilesystemDataWorker
|
||||||
|
|
||||||
@impl Oban.Worker
|
@impl Oban.Worker
|
||||||
@doc """
|
@doc """
|
||||||
|
|
@ -25,16 +27,31 @@ defmodule Pinchflat.Workers.VideoDownloadWorker do
|
||||||
|
|
||||||
# If the source is set to not download media, perform a no-op
|
# If the source is set to not download media, perform a no-op
|
||||||
if media_item.source.download_media do
|
if media_item.source.download_media do
|
||||||
download_media(media_item)
|
download_media_and_schedule_jobs(media_item)
|
||||||
else
|
else
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
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
|
case VideoDownloader.download_for_media_item(media_item) do
|
||||||
{:ok, _} -> {:ok, media_item}
|
{:ok, _} ->
|
||||||
err -> err
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
25
test/pinchflat/tasks/media_items_tasks_test.exs
Normal file
25
test/pinchflat/tasks/media_items_tasks_test.exs
Normal 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
|
||||||
19
test/pinchflat/workers/filesystem_data_worker_test.exs
Normal file
19
test/pinchflat/workers/filesystem_data_worker_test.exs
Normal 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
|
||||||
|
|
@ -6,6 +6,7 @@ defmodule Pinchflat.Workers.VideoDownloadWorkerTest do
|
||||||
|
|
||||||
alias Pinchflat.Sources
|
alias Pinchflat.Sources
|
||||||
alias Pinchflat.Workers.VideoDownloadWorker
|
alias Pinchflat.Workers.VideoDownloadWorker
|
||||||
|
alias Pinchflat.Workers.FilesystemDataWorker
|
||||||
|
|
||||||
setup :verify_on_exit!
|
setup :verify_on_exit!
|
||||||
|
|
||||||
|
|
@ -68,5 +69,17 @@ defmodule Pinchflat.Workers.VideoDownloadWorkerTest do
|
||||||
|
|
||||||
perform_job(VideoDownloadWorker, %{id: media_item.id})
|
perform_job(VideoDownloadWorker, %{id: media_item.id})
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue