[WIP] got all tests passing

This commit is contained in:
Kieran Eglin 2024-03-12 09:52:03 -07:00
parent 6c19f096fb
commit a202652c00
No known key found for this signature in database
GPG key ID: 193984967FCF432D
43 changed files with 219 additions and 321 deletions

View file

@ -4,7 +4,6 @@ alias Pinchflat.Repo
alias Pinchflat.Tasks.Task alias Pinchflat.Tasks.Task
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Metadata.MediaMetadata alias Pinchflat.Metadata.MediaMetadata
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
@ -14,14 +13,14 @@ alias Pinchflat.Profiles
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.Settings alias Pinchflat.Settings
alias Pinchflat.MediaClient.MediaDownloader alias Pinchflat.Downloading.MediaDownloader
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia alias Pinchflat.YtDlp.Media, as: YtDlpMedia
alias Pinchflat.YtDlp.Backend.MediaCollection, as: YtDlpCollection alias Pinchflat.YtDlp.MediaCollection, as: YtDlpCollection
alias Pinchflat.FastIndexing.YoutubeRss alias Pinchflat.FastIndexing.YoutubeRss
alias Pinchflat.Metadata.MetadataFileHelpers alias Pinchflat.Metadata.MetadataFileHelpers
alias Pinchflat.Utils.FilesystemUtils.FileFollowerServer alias Pinchflat.SlowIndexing.FileFollowerServer
defmodule IexHelpers do defmodule IexHelpers do
def playlist_url do def playlist_url do

View file

@ -12,7 +12,7 @@ config :pinchflat,
generators: [timestamp_type: :utc_datetime], generators: [timestamp_type: :utc_datetime],
# Specifying backend data here makes mocking and local testing SUPER easy # Specifying backend data here makes mocking and local testing SUPER easy
yt_dlp_executable: System.find_executable("yt-dlp"), yt_dlp_executable: System.find_executable("yt-dlp"),
yt_dlp_runner: Pinchflat.YtDlp.Backend.CommandRunner, yt_dlp_runner: Pinchflat.YtDlp.CommandRunner,
media_directory: "/downloads", media_directory: "/downloads",
# The user may or may not store metadata for their needs, but the app will always store its copy # The user may or may not store metadata for their needs, but the app will always store its copy
metadata_directory: "/config/metadata", metadata_directory: "/config/metadata",

View file

@ -4,7 +4,7 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilder do
""" """
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Profiles.OutputPathBuilder alias Pinchflat.Downloading.OutputPathBuilder
@doc """ @doc """
Builds the options for yt-dlp to download media based on the given media's profile. Builds the options for yt-dlp to download media based on the given media's profile.

View file

@ -3,18 +3,8 @@ defmodule Pinchflat.Downloading.DownloadingHelpers do
alias Pinchflat.Media alias Pinchflat.Media
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Sources
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.FastIndexing.YoutubeRss
alias Pinchflat.Media.MediaItem
alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.Downloading.MediaDownloadWorker alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.MediaIndexingWorker
alias Pinchflat.YtDlp.Backend.MediaCollection
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
alias Pinchflat.Utils.FilesystemUtils.FileFollowerServer
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia
@doc """ @doc """
Starts tasks for downloading media for any of a sources _pending_ media items. Starts tasks for downloading media for any of a sources _pending_ media items.

View file

@ -9,8 +9,8 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
alias Pinchflat.Repo alias Pinchflat.Repo
alias Pinchflat.Media alias Pinchflat.Media
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.MediaClient.MediaDownloader alias Pinchflat.Downloading.MediaDownloader
alias Pinchflat.Workers.FilesystemDataWorker alias Pinchflat.Filesystem.FilesystemDataWorker
@impl Oban.Worker @impl Oban.Worker
@doc """ @doc """

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.MediaClient.MediaDownloader do defmodule Pinchflat.Downloading.MediaDownloader do
@moduledoc """ @moduledoc """
This is the integration layer for actually downloading media. This is the integration layer for actually downloading media.
It takes into account the media profile's settings in order It takes into account the media profile's settings in order
@ -9,7 +9,7 @@ defmodule Pinchflat.MediaClient.MediaDownloader do
alias Pinchflat.Media alias Pinchflat.Media
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia alias Pinchflat.YtDlp.Media, as: YtDlpMedia
alias Pinchflat.Downloading.DownloadOptionBuilder, as: YtDlpDownloadOptionBuilder alias Pinchflat.Downloading.DownloadOptionBuilder, as: YtDlpDownloadOptionBuilder
alias Pinchflat.Metadata.MetadataParser, as: YtDlpMetadataParser alias Pinchflat.Metadata.MetadataParser, as: YtDlpMetadataParser
alias Pinchflat.Metadata.MetadataFileHelpers, as: YtDlpMetadataHelpers alias Pinchflat.Metadata.MetadataFileHelpers, as: YtDlpMetadataHelpers

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.RenderedString.Base do defmodule Pinchflat.Downloading.OutputPath.Base do
@moduledoc """ @moduledoc """
A base module for parsing rendered strings, designed as a macro to be used A base module for parsing rendered strings, designed as a macro to be used
in other modules. See https://elixirforum.com/t/help-to-parse-a-template-with-nimbleparsec/47980 in other modules. See https://elixirforum.com/t/help-to-parse-a-template-with-nimbleparsec/47980
@ -6,7 +6,7 @@ defmodule Pinchflat.RenderedString.Base do
NOTE: if the needs here get any more complicated, look into using a Liquid NOTE: if the needs here get any more complicated, look into using a Liquid
template parser. No need to reinvent the wheel any more than I already have. template parser. No need to reinvent the wheel any more than I already have.
NOTE: this is effectively tested by the `Pinchflat.RenderedString.Parser`'s tests NOTE: this is effectively tested by the `Pinchflat.Downloading.OutputPath.Parser`'s tests
""" """
defmacro __using__(_opts) do defmacro __using__(_opts) do

View file

@ -1,11 +1,11 @@
defmodule Pinchflat.RenderedString.Parser do defmodule Pinchflat.Downloading.OutputPath.Parser do
@moduledoc """ @moduledoc """
Parses liquid-ish-style strings into a rendered string Parses liquid-ish-style strings into a rendered string
Used for turning filepath templates into real filepaths Used for turning filepath templates into real filepaths
""" """
use Pinchflat.RenderedString.Base use Pinchflat.Downloading.OutputPath.Base
@doc """ @doc """
Parses a string into a rendered string, using the provided variables. Optionally Parses a string into a rendered string, using the provided variables. Optionally

View file

@ -1,9 +1,9 @@
defmodule Pinchflat.Profiles.OutputPathBuilder do defmodule Pinchflat.Downloading.OutputPathBuilder do
@moduledoc """ @moduledoc """
Builds yt-dlp-friendly output paths for downloaded media Builds yt-dlp-friendly output paths for downloaded media
""" """
alias Pinchflat.RenderedString.Parser, as: TemplateParser alias Pinchflat.Downloading.OutputPath.Parser, as: TemplateParser
@doc """ @doc """
Builds the actual final filepath from a given template. Optionally, you can pass in Builds the actual final filepath from a given template. Optionally, you can pass in

View file

@ -1,16 +1,13 @@
defmodule Pinchflat.FastIndexing.FastIndexingHelpers do defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
alias Pinchflat.Media alias Pinchflat.Media
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Sources
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.FastIndexing.YoutubeRss alias Pinchflat.FastIndexing.YoutubeRss
alias Pinchflat.Media.MediaItem
alias Pinchflat.FastIndexing.FastIndexingWorker alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.Downloading.MediaDownloadWorker alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.MediaIndexingWorker alias Pinchflat.FastIndexing.MediaIndexingWorker
alias Pinchflat.YtDlp.Backend.MediaCollection
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker alias Pinchflat.YtDlp.Media, as: YtDlpMedia
alias Pinchflat.Utils.FilesystemUtils.FileFollowerServer
@doc """ @doc """
Starts tasks for running a fast indexing task for a source's media Starts tasks for running a fast indexing task for a source's media
@ -56,4 +53,36 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
|> Tasks.create_job_with_task(source) |> Tasks.create_job_with_task(source)
end) end)
end end
@doc """
Indexes a single media item for a source and enqueues a download job if the
media should be downloaded. This method creates the media item record so it's
the one-stop-shop for adding a media item (and possibly downloading it) just
by a URL and source.
Returns {:ok, media_item} | {:error, any()}
"""
def index_and_enqueue_download_for_media_item(%Source{} = source, url) do
maybe_media_item = create_media_item_from_url(source, url)
case maybe_media_item do
{:ok, media_item} ->
if source.download_media && Media.pending_download?(media_item) do
%{id: media_item.id}
|> MediaDownloadWorker.new()
|> Tasks.create_job_with_task(media_item)
end
{:ok, media_item}
err ->
err
end
end
defp create_media_item_from_url(source, url) do
{:ok, media_attrs} = YtDlpMedia.get_media_attributes(url)
Media.create_media_item_from_backend_attrs(source, media_attrs)
end
end end

View file

@ -11,7 +11,7 @@ defmodule Pinchflat.FastIndexing.MediaIndexingWorker do
require Logger require Logger
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.Tasks.MediaItemTasks alias Pinchflat.FastIndexing.FastIndexingHelpers
@impl Oban.Worker @impl Oban.Worker
@doc """ @doc """
@ -42,7 +42,7 @@ defmodule Pinchflat.FastIndexing.MediaIndexingWorker do
def perform(%Oban.Job{args: %{"id" => source_id, "media_url" => media_url}}) do def perform(%Oban.Job{args: %{"id" => source_id, "media_url" => media_url}}) do
source = Sources.get_source!(source_id) source = Sources.get_source!(source_id)
case MediaItemTasks.index_and_enqueue_download_for_media_item(source, media_url) do case FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, media_url) do
{:ok, media_item} -> {:ok, media_item} ->
Logger.debug("Indexed and enqueued download for url: #{media_url} (media item: #{media_item.id})") Logger.debug("Indexed and enqueued download for url: #{media_url} (media item: #{media_item.id})")

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.Workers.FilesystemDataWorker do defmodule Pinchflat.Filesystem.FilesystemDataWorker do
@moduledoc false @moduledoc false
use Oban.Worker, use Oban.Worker,
@ -7,7 +7,7 @@ defmodule Pinchflat.Workers.FilesystemDataWorker do
max_attempts: 1 max_attempts: 1
alias Pinchflat.Media alias Pinchflat.Media
alias Pinchflat.Tasks.MediaItemTasks alias Pinchflat.Filesystem.FilesystemHelpers
@impl Oban.Worker @impl Oban.Worker
@doc """ @doc """
@ -18,7 +18,7 @@ defmodule Pinchflat.Workers.FilesystemDataWorker do
def perform(%Oban.Job{args: %{"id" => media_item_id}}) do def perform(%Oban.Job{args: %{"id" => media_item_id}}) do
media_item = Media.get_media_item!(media_item_id) media_item = Media.get_media_item!(media_item_id)
MediaItemTasks.compute_and_save_media_filesize(media_item) FilesystemHelpers.compute_and_save_media_filesize(media_item)
# Don't retry on failure - if it didn't work immediately there's no # Don't retry on failure - if it didn't work immediately there's no
# reason to believe it will work later. # reason to believe it will work later.

View file

@ -1,8 +1,8 @@
defmodule Pinchflat.Utils.FilesystemUtils do defmodule Pinchflat.Filesystem.FilesystemHelpers do
@moduledoc """ @moduledoc """
Utility methods for working with the filesystem Utility methods for working with the filesystem
""" """
alias Pinchflat.Media
alias Pinchflat.Utils.StringUtils alias Pinchflat.Utils.StringUtils
@doc """ @doc """
@ -20,4 +20,19 @@ defmodule Pinchflat.Utils.FilesystemUtils do
filepath filepath
end end
@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 end

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.Utils.FilesystemUtils.FileFollowerServer do defmodule Pinchflat.SlowIndexing.FileFollowerServer do
@moduledoc """ @moduledoc """
A GenServer that watches a file for new lines and processes them as they come in. A GenServer that watches a file for new lines and processes them as they come in.
This is useful for tailing log files and other similar tasks. If there's no activity This is useful for tailing log files and other similar tasks. If there's no activity

View file

@ -10,7 +10,6 @@ defmodule Pinchflat.SlowIndexing.MediaCollectionIndexingWorker do
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.FastIndexing.FastIndexingWorker alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.SlowIndexing.SlowIndexingHelpers alias Pinchflat.SlowIndexing.SlowIndexingHelpers

View file

@ -5,17 +5,14 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.FastIndexing.YoutubeRss
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.FastIndexing.FastIndexingWorker alias Pinchflat.YtDlp.MediaCollection
alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.MediaIndexingWorker
alias Pinchflat.YtDlp.Backend.MediaCollection
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
alias Pinchflat.Utils.FilesystemUtils.FileFollowerServer
alias Pinchflat.Downloading.DownloadingHelpers alias Pinchflat.Downloading.DownloadingHelpers
alias Pinchflat.SlowIndexing.FileFollowerServer
alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia alias Pinchflat.YtDlp.Media, as: YtDlpMedia
@doc """ @doc """
Starts tasks for indexing a source's media regardless of the source's indexing Starts tasks for indexing a source's media regardless of the source's indexing

View file

@ -9,9 +9,8 @@ defmodule Pinchflat.Sources do
alias Pinchflat.Media alias Pinchflat.Media
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
alias Pinchflat.YtDlp.Backend.MediaCollection alias Pinchflat.YtDlp.MediaCollection
alias Pinchflat.Downloading.DownloadingHelpers alias Pinchflat.Downloading.DownloadingHelpers
alias Pinchflat.FastIndexing.FastIndexingHelpers alias Pinchflat.FastIndexing.FastIndexingHelpers
alias Pinchflat.SlowIndexing.SlowIndexingHelpers alias Pinchflat.SlowIndexing.SlowIndexingHelpers

View file

@ -1,61 +0,0 @@
defmodule Pinchflat.Tasks.MediaItemTasks do
@moduledoc """
Contains methods used by OR used to create/manage tasks for media items.
Tasks/workers are meant to be thin wrappers so most of the actual work they
do is also defined here. Essentially, a one-stop-shop for media-related tasks/workers.
"""
alias Pinchflat.Media
alias Pinchflat.Tasks
alias Pinchflat.Sources.Source
alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia
@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
@doc """
Indexes a single media item for a source and enqueues a download job if the
media should be downloaded. This method creates the media item record so it's
the one-stop-shop for adding a media item (and possibly downloading it) just
by a URL and source.
Returns {:ok, media_item} | {:error, any()}
"""
def index_and_enqueue_download_for_media_item(%Source{} = source, url) do
maybe_media_item = create_media_item_from_url(source, url)
case maybe_media_item do
{:ok, media_item} ->
if source.download_media && Media.pending_download?(media_item) do
%{id: media_item.id}
|> MediaDownloadWorker.new()
|> Tasks.create_job_with_task(media_item)
end
{:ok, media_item}
err ->
err
end
end
defp create_media_item_from_url(source, url) do
{:ok, media_attrs} = YtDlpMedia.get_media_attributes(url)
Media.create_media_item_from_backend_attrs(source, media_attrs)
end
end

View file

@ -1,25 +0,0 @@
defmodule Pinchflat.Tasks.SourceTasks do
@moduledoc """
Contains methods used by OR used to create/manage tasks for sources.
Tasks/workers are meant to be thin wrappers so most of the actual work they
do is also defined here. Essentially, a one-stop-shop for source-related tasks/workers.
"""
require Logger
# alias Pinchflat.Media
# alias Pinchflat.Tasks
# alias Pinchflat.Sources
# alias Pinchflat.Sources.Source
# alias Pinchflat.FastIndexing.YoutubeRss
# alias Pinchflat.Media.MediaItem
# alias Pinchflat.FastIndexing.FastIndexingWorker
# alias Pinchflat.Downloading.MediaDownloadWorker
# alias Pinchflat.FastIndexing.MediaIndexingWorker
# alias Pinchflat.YtDlp.Backend.MediaCollection
# alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
# alias Pinchflat.Utils.FilesystemUtils.FileFollowerServer
# alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia
end

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.YtDlp.Backend.BackendCommandRunner do defmodule Pinchflat.YtDlp.BackendCommandRunner do
@moduledoc """ @moduledoc """
A behaviour for running CLI commands against a downloader backend (yt-dlp). A behaviour for running CLI commands against a downloader backend (yt-dlp).

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.YtDlp.Backend.CommandRunner do defmodule Pinchflat.YtDlp.CommandRunner do
@moduledoc """ @moduledoc """
Runs yt-dlp commands using the `System.cmd/3` function Runs yt-dlp commands using the `System.cmd/3` function
""" """
@ -6,8 +6,8 @@ defmodule Pinchflat.YtDlp.Backend.CommandRunner do
require Logger require Logger
alias Pinchflat.Utils.StringUtils alias Pinchflat.Utils.StringUtils
alias Pinchflat.Utils.FilesystemUtils, as: FSUtils alias Pinchflat.Filesystem.FilesystemHelpers, as: FSUtils
alias Pinchflat.YtDlp.Backend.BackendCommandRunner alias Pinchflat.YtDlp.BackendCommandRunner
@behaviour BackendCommandRunner @behaviour BackendCommandRunner

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.YtDlp.Backend.Media do defmodule Pinchflat.YtDlp.Media do
@moduledoc """ @moduledoc """
Contains utilities for working with singular pieces of media Contains utilities for working with singular pieces of media
""" """

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.YtDlp.Backend.MediaCollection do defmodule Pinchflat.YtDlp.MediaCollection do
@moduledoc """ @moduledoc """
Contains utilities for working with collections of Contains utilities for working with collections of
media (aka: a source [ie: channels, playlists]). media (aka: a source [ie: channels, playlists]).
@ -7,8 +7,8 @@ defmodule Pinchflat.YtDlp.Backend.MediaCollection do
require Logger require Logger
alias Pinchflat.Utils.FunctionUtils alias Pinchflat.Utils.FunctionUtils
alias Pinchflat.Utils.FilesystemUtils alias Pinchflat.Filesystem.FilesystemHelpers
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia alias Pinchflat.YtDlp.Media, as: YtDlpMedia
@doc """ @doc """
Returns a list of maps representing the media in the collection. Returns a list of maps representing the media in the collection.
@ -24,7 +24,7 @@ defmodule Pinchflat.YtDlp.Backend.MediaCollection do
runner = Application.get_env(:pinchflat, :yt_dlp_runner) runner = Application.get_env(:pinchflat, :yt_dlp_runner)
command_opts = [:simulate, :skip_download] command_opts = [:simulate, :skip_download]
output_template = YtDlpMedia.indexing_output_template() output_template = YtDlpMedia.indexing_output_template()
output_filepath = FilesystemUtils.generate_metadata_tmpfile(:json) output_filepath = FilesystemHelpers.generate_metadata_tmpfile(:json)
file_listener_handler = Keyword.get(addl_opts, :file_listener_handler, false) file_listener_handler = Keyword.get(addl_opts, :file_listener_handler, false)
if file_listener_handler do if file_listener_handler do

View file

@ -4,7 +4,7 @@ defmodule Pinchflat.Boot.DataBackfillWorkerTest do
import Pinchflat.MediaFixtures import Pinchflat.MediaFixtures
alias Pinchflat.Boot.DataBackfillWorker alias Pinchflat.Boot.DataBackfillWorker
alias Pinchflat.Workers.FilesystemDataWorker alias Pinchflat.Filesystem.FilesystemDataWorker
describe "cancel_pending_backfill_jobs/0" do describe "cancel_pending_backfill_jobs/0" do
test "cancels all pending backfill jobs" do test "cancels all pending backfill jobs" do

View file

@ -2,20 +2,12 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
use Pinchflat.DataCase use Pinchflat.DataCase
import Mox import Mox
import Pinchflat.TasksFixtures
import Pinchflat.MediaFixtures import Pinchflat.MediaFixtures
import Pinchflat.SourcesFixtures import Pinchflat.SourcesFixtures
import Pinchflat.ProfilesFixtures
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Tasks.Task
alias Pinchflat.Media.MediaItem
alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.MediaIndexingWorker
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
alias Pinchflat.Downloading.DownloadingHelpers alias Pinchflat.Downloading.DownloadingHelpers
alias Pinchflat.Downloading.MediaDownloadWorker
setup :verify_on_exit! setup :verify_on_exit!

View file

@ -6,7 +6,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.Downloading.MediaDownloadWorker alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.Workers.FilesystemDataWorker alias Pinchflat.Filesystem.FilesystemDataWorker
setup :verify_on_exit! setup :verify_on_exit!

View file

@ -1,9 +1,9 @@
defmodule Pinchflat.MediaClient.MediaDownloaderTest do defmodule Pinchflat.Downloading.MediaDownloaderTest do
use Pinchflat.DataCase use Pinchflat.DataCase
import Mox import Mox
import Pinchflat.MediaFixtures import Pinchflat.MediaFixtures
alias Pinchflat.MediaClient.MediaDownloader alias Pinchflat.Downloading.MediaDownloader
setup :verify_on_exit! setup :verify_on_exit!

View file

@ -1,7 +1,7 @@
defmodule Pinchflat.RenderedString.ParserTest do defmodule Pinchflat.Downloading.OutputPath.ParserTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
alias Pinchflat.RenderedString.Parser alias Pinchflat.Downloading.OutputPath.Parser
describe "parse/3" do describe "parse/3" do
test "it returns the rendered string when the string is valid" do test "it returns the rendered string when the string is valid" do

View file

@ -1,7 +1,7 @@
defmodule Pinchflat.Profiles.OutputPathBuilderTest do defmodule Pinchflat.Downloading.OutputPathBuilderTest do
use Pinchflat.DataCase use Pinchflat.DataCase
alias Pinchflat.Profiles.OutputPathBuilder alias Pinchflat.Downloading.OutputPathBuilder
describe "build/2" do describe "build/2" do
test "it expands 'standard' curly brace variables in the template" do test "it expands 'standard' curly brace variables in the template" do

View file

@ -9,16 +9,16 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
alias Pinchflat.Tasks alias Pinchflat.Tasks
alias Pinchflat.Tasks.Task alias Pinchflat.Tasks.Task
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Downloading.MediaDownloadWorker alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.MediaIndexingWorker alias Pinchflat.FastIndexing.MediaIndexingWorker
alias Pinchflat.FastIndexing.FastIndexingHelpers alias Pinchflat.FastIndexing.FastIndexingHelpers
alias Pinchflat.FastIndexing.FastIndexingWorker alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
setup :verify_on_exit! setup :verify_on_exit!
@media_url "https://www.youtube.com/watch?v=test_1"
describe "kickoff_fast_indexing_task/1" do describe "kickoff_fast_indexing_task/1" do
test "it schedules a job" do test "it schedules a job" do
source = source_fixture() source = source_fixture()
@ -71,4 +71,72 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
refute_enqueued(worker: MediaIndexingWorker) refute_enqueued(worker: MediaIndexingWorker)
end end
end end
describe "index_and_enqueue_download_for_media_item/2" do
setup do
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
{:ok, media_attributes_return_fixture()}
end)
{:ok, [source: source_fixture()]}
end
test "creates a new media item based on the URL", %{source: source} do
assert Repo.aggregate(MediaItem, :count) == 0
assert {:ok, _} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
assert Repo.aggregate(MediaItem, :count) == 1
end
test "won't duplicate media_items based on media_id and source", %{source: source} do
assert {:ok, mi_1} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
assert {:ok, mi_2} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
assert Repo.aggregate(MediaItem, :count) == 1
assert mi_1.id == mi_2.id
end
test "enqueues a download job", %{source: source} do
assert {:ok, media_item} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
end
test "creates a download task record", %{source: source} do
assert {:ok, media_item} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
assert [_] = Tasks.list_tasks_for(:media_item_id, media_item.id, "MediaDownloadWorker")
end
test "does not enqueue a download job if the source does not allow it" do
source = source_fixture(%{download_media: false})
assert {:ok, _} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
refute_enqueued(worker: MediaDownloadWorker)
end
test "does not enqueue a download job if the media item does not match the format rules" do
profile = media_profile_fixture(%{shorts_behaviour: :exclude})
source = source_fixture(%{media_profile_id: profile.id})
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
output =
Phoenix.json_library().encode!(%{
id: "video2",
title: "Video 2",
webpage_url: "https://example.com/shorts/video2",
was_live: true,
description: "desc2",
aspect_ratio: 1.67,
duration: 345.67,
upload_date: "20210101"
})
{:ok, output}
end)
assert {:ok, _media_item} = FastIndexingHelpers.index_and_enqueue_download_for_media_item(source, @media_url)
refute_enqueued(worker: MediaDownloadWorker)
end
end
end end

View file

@ -1,9 +1,9 @@
defmodule Pinchflat.Workers.FilesystemDataWorkerTest do defmodule Pinchflat.Filesystem.FilesystemDataWorkerTest do
use Pinchflat.DataCase use Pinchflat.DataCase
import Pinchflat.MediaFixtures import Pinchflat.MediaFixtures
alias Pinchflat.Workers.FilesystemDataWorker alias Pinchflat.Filesystem.FilesystemDataWorker
describe "perform/1" do describe "perform/1" do
test "Computes and stores the media file size" do test "Computes and stores the media file size" do

View file

@ -0,0 +1,36 @@
defmodule Pinchflat.Filesystem.FilesystemHelpersTest do
use Pinchflat.DataCase
import Pinchflat.MediaFixtures
alias Pinchflat.Filesystem.FilesystemHelpers
describe "generate_metadata_tmpfile/1" do
test "creates a tmpfile and returns its path" do
res = FilesystemHelpers.generate_metadata_tmpfile(:json)
assert String.ends_with?(res, ".json")
assert File.exists?(res)
File.rm!(res)
end
end
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} = FilesystemHelpers.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, _} = FilesystemHelpers.compute_and_save_media_filesize(media_item)
end
end
end

View file

@ -11,7 +11,7 @@ defmodule Pinchflat.MediaTest do
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Metadata.MetadataFileHelpers alias Pinchflat.Metadata.MetadataFileHelpers
alias Pinchflat.YtDlp.Backend.Media, as: YtDlpMedia alias Pinchflat.YtDlp.Media, as: YtDlpMedia
setup :verify_on_exit! setup :verify_on_exit!

View file

@ -1,4 +1,4 @@
defmodule Pinchflat.YtDlp.Backend.MediaParserTest do defmodule Pinchflat.YtDlp.MediaParserTest do
use Pinchflat.DataCase use Pinchflat.DataCase
alias Pinchflat.Metadata.MetadataParser, as: Parser alias Pinchflat.Metadata.MetadataParser, as: Parser

View file

@ -1,12 +1,12 @@
defmodule Pinchflat.Utils.FilesystemUtils.FileFollowerServerTest do defmodule Pinchflat.SlowIndexing.FileFollowerServerTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
alias alias Pinchflat.Utils.FilesystemUtils alias alias Pinchflat.Filesystem.FilesystemHelpers
alias Pinchflat.Utils.FilesystemUtils.FileFollowerServer alias Pinchflat.SlowIndexing.FileFollowerServer
setup do setup do
{:ok, pid} = FileFollowerServer.start_link() {:ok, pid} = FileFollowerServer.start_link()
tmpfile = FilesystemUtils.generate_metadata_tmpfile(:txt) tmpfile = FilesystemHelpers.generate_metadata_tmpfile(:txt)
{:ok, %{pid: pid, tmpfile: tmpfile}} {:ok, %{pid: pid, tmpfile: tmpfile}}
end end

View file

@ -7,7 +7,6 @@ defmodule Pinchflat.SourcesTest do
import Pinchflat.SourcesFixtures import Pinchflat.SourcesFixtures
alias Pinchflat.Sources alias Pinchflat.Sources
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Sources.Source alias Pinchflat.Sources.Source
alias Pinchflat.Downloading.DownloadingHelpers alias Pinchflat.Downloading.DownloadingHelpers
alias Pinchflat.FastIndexing.FastIndexingWorker alias Pinchflat.FastIndexing.FastIndexingWorker

View file

@ -1,103 +0,0 @@
defmodule Pinchflat.Tasks.MediaItemTasksTest do
use Pinchflat.DataCase
import Mox
import Pinchflat.MediaFixtures
import Pinchflat.SourcesFixtures
import Pinchflat.ProfilesFixtures
alias Pinchflat.Tasks
alias Pinchflat.Media.MediaItem
alias Pinchflat.Tasks.MediaItemTasks
alias Pinchflat.Downloading.MediaDownloadWorker
setup :verify_on_exit!
@media_url "https://www.youtube.com/watch?v=1234"
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
describe "index_and_enqueue_download_for_media_item/2" do
setup do
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
{:ok, media_attributes_return_fixture()}
end)
{:ok, [source: source_fixture()]}
end
test "creates a new media item based on the URL", %{source: source} do
assert Repo.aggregate(MediaItem, :count) == 0
assert {:ok, _} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
assert Repo.aggregate(MediaItem, :count) == 1
end
test "won't duplicate media_items based on media_id and source", %{source: source} do
assert {:ok, mi_1} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
assert {:ok, mi_2} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
assert Repo.aggregate(MediaItem, :count) == 1
assert mi_1.id == mi_2.id
end
test "enqueues a download job", %{source: source} do
assert {:ok, media_item} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
end
test "creates a download task record", %{source: source} do
assert {:ok, media_item} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
assert [_] = Tasks.list_tasks_for(:media_item_id, media_item.id, "MediaDownloadWorker")
end
test "does not enqueue a download job if the source does not allow it" do
source = source_fixture(%{download_media: false})
assert {:ok, _} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
refute_enqueued(worker: MediaDownloadWorker)
end
test "does not enqueue a download job if the media item does not match the format rules" do
profile = media_profile_fixture(%{shorts_behaviour: :exclude})
source = source_fixture(%{media_profile_id: profile.id})
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
output =
Phoenix.json_library().encode!(%{
id: "video2",
title: "Video 2",
webpage_url: "https://example.com/shorts/video2",
was_live: true,
description: "desc2",
aspect_ratio: 1.67,
duration: 345.67,
upload_date: "20210101"
})
{:ok, output}
end)
assert {:ok, _media_item} = MediaItemTasks.index_and_enqueue_download_for_media_item(source, @media_url)
refute_enqueued(worker: MediaDownloadWorker)
end
end
end

View file

@ -1,20 +0,0 @@
defmodule Pinchflat.Tasks.SourceTasksTest do
use Pinchflat.DataCase
import Mox
import Pinchflat.TasksFixtures
import Pinchflat.MediaFixtures
import Pinchflat.SourcesFixtures
import Pinchflat.ProfilesFixtures
alias Pinchflat.Tasks
alias Pinchflat.Tasks.Task
alias Pinchflat.Tasks.SourceTasks
alias Pinchflat.Media.MediaItem
alias Pinchflat.FastIndexing.FastIndexingWorker
alias Pinchflat.Downloading.MediaDownloadWorker
alias Pinchflat.FastIndexing.MediaIndexingWorker
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
setup :verify_on_exit!
end

View file

@ -1,16 +0,0 @@
defmodule Pinchflat.Utils.FilesystemUtilsTest do
use ExUnit.Case, async: true
alias Pinchflat.Utils.FilesystemUtils
describe "generate_metadata_tmpfile/1" do
test "creates a tmpfile and returns its path" do
res = FilesystemUtils.generate_metadata_tmpfile(:json)
assert String.ends_with?(res, ".json")
assert File.exists?(res)
File.rm!(res)
end
end
end

View file

@ -1,7 +1,7 @@
defmodule Pinchflat.YtDlp.Backend.CommandRunnerTest do defmodule Pinchflat.YtDlp.CommandRunnerTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
alias Pinchflat.YtDlp.Backend.CommandRunner, as: Runner alias Pinchflat.YtDlp.CommandRunner, as: Runner
@original_executable Application.compile_env(:pinchflat, :yt_dlp_executable) @original_executable Application.compile_env(:pinchflat, :yt_dlp_executable)
@media_url "https://www.youtube.com/watch?v=-LHXuyzpex0" @media_url "https://www.youtube.com/watch?v=-LHXuyzpex0"

View file

@ -1,10 +1,10 @@
defmodule Pinchflat.YtDlp.Backend.MediaCollectionTest do defmodule Pinchflat.YtDlp.MediaCollectionTest do
use Pinchflat.DataCase use Pinchflat.DataCase
import Mox import Mox
import Pinchflat.SourcesFixtures import Pinchflat.SourcesFixtures
alias Pinchflat.YtDlp.Backend.Media alias Pinchflat.YtDlp.Media
alias Pinchflat.YtDlp.Backend.MediaCollection alias Pinchflat.YtDlp.MediaCollection
@channel_url "https://www.youtube.com/c/TheUselessTrials" @channel_url "https://www.youtube.com/c/TheUselessTrials"

View file

@ -1,9 +1,9 @@
defmodule Pinchflat.YtDlp.Backend.MediaTest do defmodule Pinchflat.YtDlp.MediaTest do
use Pinchflat.DataCase use Pinchflat.DataCase
import Mox import Mox
import Pinchflat.MediaFixtures import Pinchflat.MediaFixtures
alias Pinchflat.YtDlp.Backend.Media alias Pinchflat.YtDlp.Media
@media_url "https://www.youtube.com/watch?v=TiZPUDkDYbk" @media_url "https://www.youtube.com/watch?v=TiZPUDkDYbk"

View file

@ -1,4 +1,4 @@
Mox.defmock(YtDlpRunnerMock, for: Pinchflat.YtDlp.Backend.BackendCommandRunner) Mox.defmock(YtDlpRunnerMock, for: Pinchflat.YtDlp.BackendCommandRunner)
Application.put_env(:pinchflat, :yt_dlp_runner, YtDlpRunnerMock) Application.put_env(:pinchflat, :yt_dlp_runner, YtDlpRunnerMock)
Mox.defmock(HTTPClientMock, for: Pinchflat.HTTP.HTTPBehaviour) Mox.defmock(HTTPClientMock, for: Pinchflat.HTTP.HTTPBehaviour)