Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d5b62ccf9 | |||
| af8235c6b3 | |||
| 8e9f02c807 | |||
| fd20ac5d84 | |||
| 23492961d5 | |||
| 2326775d78 | |||
| e0a84a521b | |||
| d217ad5744 | |||
| d5ae41cdab | |||
| 7a01db05dd | |||
| d392fc3818 | |||
| 47596d5f72 | |||
| dbc25e5730 |
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "Pinchflat Dev",
|
||||||
|
"dockerComposeFile": "../docker-compose.yml",
|
||||||
|
"service": "phx",
|
||||||
|
"workspaceFolder": "/app",
|
||||||
|
"shutdownAction": "stopCompose",
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": ["phoenixframework.phoenix", "JakeBecker.elixir-ls", "esbenp.prettier-vscode"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,3 +23,11 @@ alias Pinchflat.Metadata.MetadataFileHelpers
|
|||||||
alias Pinchflat.SlowIndexing.FileFollowerServer
|
alias Pinchflat.SlowIndexing.FileFollowerServer
|
||||||
|
|
||||||
Pinchflat.Release.check_file_permissions()
|
Pinchflat.Release.check_file_permissions()
|
||||||
|
|
||||||
|
defmodule IexHelpers do
|
||||||
|
def restart do
|
||||||
|
:init.restart()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
import IexHelpers
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
assets/vendor/
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/kieraneglin/pinchflat/releases)
|
[](https://github.com/kieraneglin/pinchflat/releases)
|
||||||
[](#)
|
[](#)
|
||||||
|
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/kieraneglin/pinchflat)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -145,15 +146,16 @@ If you change this setting and it works well for you, please leave a comment on
|
|||||||
### Environment variables
|
### Environment variables
|
||||||
|
|
||||||
| Name | Required? | Default | Notes |
|
| Name | Required? | Default | Notes |
|
||||||
| --------------------- | --------- | ------------------------- | ---------------------------------------------------------------------------------------------- |
|
| --------------------------- | --------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| TZ | No | `UTC` | Must follow IANA TZ format |
|
| `TZ` | No | `UTC` | Must follow IANA TZ format |
|
||||||
| LOG_LEVEL | No | `debug` | Can be set to `info` |
|
| `LOG_LEVEL` | No | `debug` | Can be set to `info` but `debug` is strongly recommended |
|
||||||
| BASIC_AUTH_USERNAME | No | | See [authentication docs](https://github.com/kieraneglin/pinchflat/wiki/Username-and-Password) |
|
| `BASIC_AUTH_USERNAME` | No | | See [authentication docs](https://github.com/kieraneglin/pinchflat/wiki/Username-and-Password) |
|
||||||
| BASIC_AUTH_PASSWORD | No | | See [authentication docs](https://github.com/kieraneglin/pinchflat/wiki/Username-and-Password) |
|
| `BASIC_AUTH_PASSWORD` | No | | See [authentication docs](https://github.com/kieraneglin/pinchflat/wiki/Username-and-Password) |
|
||||||
| EXPOSE_FEED_ENDPOINTS | No | | See [RSS feed docs](https://github.com/kieraneglin/pinchflat/wiki/Podcast-RSS-Feeds) |
|
| `EXPOSE_FEED_ENDPOINTS` | No | | See [RSS feed docs](https://github.com/kieraneglin/pinchflat/wiki/Podcast-RSS-Feeds) |
|
||||||
| JOURNAL_MODE | No | `wal` | Set to `delete` if your config directory is stored on a network share (not recommended) |
|
| `JOURNAL_MODE` | No | `wal` | Set to `delete` if your config directory is stored on a network share (not recommended) |
|
||||||
| TZ_DATA_DIR | No | `/etc/elixir_tzdata_data` | The container path where the timezone database is stored |
|
| `TZ_DATA_DIR` | No | `/etc/elixir_tzdata_data` | The container path where the timezone database is stored |
|
||||||
| BASE_ROUTE_PATH | No | `/` | The base path for route generation. Useful when running behind certain reverse proxies |
|
| `BASE_ROUTE_PATH` | No | `/` | The base path for route generation. Useful when running behind certain reverse proxies |
|
||||||
|
| `YT_DLP_WORKER_CONCURRENCY` | No | `2` | The number of concurrent workers that use `yt-dlp` _per queue_. Set to 1 if you're getting IP limited, otherwise don't touch it |
|
||||||
|
|
||||||
## EFF donations
|
## EFF donations
|
||||||
|
|
||||||
|
|||||||
@@ -57,16 +57,6 @@ config :pinchflat, Oban,
|
|||||||
{"0 1 * * *", Pinchflat.Downloading.MediaRetentionWorker},
|
{"0 1 * * *", Pinchflat.Downloading.MediaRetentionWorker},
|
||||||
{"0 2 * * *", Pinchflat.Downloading.MediaQualityUpgradeWorker}
|
{"0 2 * * *", Pinchflat.Downloading.MediaQualityUpgradeWorker}
|
||||||
]}
|
]}
|
||||||
],
|
|
||||||
# TODO: consider making this an env var or something?
|
|
||||||
queues: [
|
|
||||||
default: 10,
|
|
||||||
fast_indexing: 6,
|
|
||||||
media_indexing: 2,
|
|
||||||
media_collection_indexing: 2,
|
|
||||||
media_fetching: 2,
|
|
||||||
local_metadata: 8,
|
|
||||||
remote_metadata: 4
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Configures the mailer
|
# Configures the mailer
|
||||||
|
|||||||
@@ -40,6 +40,20 @@ config :pinchflat, Pinchflat.Repo,
|
|||||||
Path.join([:code.priv_dir(:pinchflat), "repo", "extensions", "sqlean-linux-#{system_arch}", "sqlean"])
|
Path.join([:code.priv_dir(:pinchflat), "repo", "extensions", "sqlean-linux-#{system_arch}", "sqlean"])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Some users may want to increase the number of workers that use yt-dlp to improve speeds
|
||||||
|
# Others may want to decrease the number of these workers to lessen the chance of an IP ban
|
||||||
|
{yt_dlp_worker_count, _} = Integer.parse(System.get_env("YT_DLP_WORKER_CONCURRENCY", "2"))
|
||||||
|
|
||||||
|
config :pinchflat, Oban,
|
||||||
|
queues: [
|
||||||
|
default: 10,
|
||||||
|
fast_indexing: 6,
|
||||||
|
media_collection_indexing: yt_dlp_worker_count,
|
||||||
|
media_fetching: yt_dlp_worker_count,
|
||||||
|
remote_metadata: yt_dlp_worker_count,
|
||||||
|
local_data: 8
|
||||||
|
]
|
||||||
|
|
||||||
if config_env() == :prod do
|
if config_env() == :prod do
|
||||||
config_path = "/config"
|
config_path = "/config"
|
||||||
db_path = System.get_env("DATABASE_PATH", Path.join([config_path, "db", "pinchflat.db"]))
|
db_path = System.get_env("DATABASE_PATH", Path.join([config_path, "db", "pinchflat.db"]))
|
||||||
|
|||||||
@@ -10,5 +10,3 @@ services:
|
|||||||
command: bash -c "chmod +x docker/docker-run.dev.sh && docker/docker-run.dev.sh"
|
command: bash -c "chmod +x docker/docker-run.dev.sh && docker/docker-run.dev.sh"
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: true
|
tty: true
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ RUN echo "Building for ${TARGETPLATFORM:?}"
|
|||||||
RUN apt-get update -qq && \
|
RUN apt-get update -qq && \
|
||||||
apt-get install -y inotify-tools curl git openssh-client jq \
|
apt-get install -y inotify-tools curl git openssh-client jq \
|
||||||
python3 python3-setuptools python3-wheel python3-dev pipx \
|
python3 python3-setuptools python3-wheel python3-dev pipx \
|
||||||
python3-mutagen locales procps build-essential graphviz
|
python3-mutagen locales procps build-essential graphviz zsh
|
||||||
|
|
||||||
# Install ffmpeg
|
# Install ffmpeg
|
||||||
RUN export FFMPEG_DOWNLOAD=$(case ${TARGETPLATFORM:-linux/amd64} in \
|
RUN export FFMPEG_DOWNLOAD=$(case ${TARGETPLATFORM:-linux/amd64} in \
|
||||||
@@ -38,7 +38,10 @@ RUN curl -sL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh && \
|
|||||||
# Install Apprise
|
# Install Apprise
|
||||||
export PIPX_HOME=/opt/pipx && \
|
export PIPX_HOME=/opt/pipx && \
|
||||||
export PIPX_BIN_DIR=/usr/local/bin && \
|
export PIPX_BIN_DIR=/usr/local/bin && \
|
||||||
pipx install apprise
|
pipx install apprise && \
|
||||||
|
# Set up ZSH tools
|
||||||
|
chsh -s $(which zsh) && \
|
||||||
|
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
|
||||||
|
|
||||||
# Set the locale
|
# Set the locale
|
||||||
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ defmodule Pinchflat.Downloading.DownloadingHelpers do
|
|||||||
[m, s, mp],
|
[m, s, mp],
|
||||||
^MediaQuery.for_source(source) and
|
^MediaQuery.for_source(source) and
|
||||||
^MediaQuery.downloaded() and
|
^MediaQuery.downloaded() and
|
||||||
not (^MediaQuery.download_prevented()) and
|
not (^MediaQuery.download_prevented())
|
||||||
not (^MediaQuery.culled())
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|||||||
@@ -39,17 +39,14 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
|||||||
- `quality_upgrade?`: re-downloads media, including the video. Does not force download
|
- `quality_upgrade?`: re-downloads media, including the video. Does not force download
|
||||||
if the source is set to not download media
|
if the source is set to not download media
|
||||||
|
|
||||||
Returns :ok | {:ok, %MediaItem{}} | {:error, any, ...any}
|
Returns :ok | {:error, any, ...any}
|
||||||
"""
|
"""
|
||||||
@impl Oban.Worker
|
@impl Oban.Worker
|
||||||
def perform(%Oban.Job{args: %{"id" => media_item_id} = args}) do
|
def perform(%Oban.Job{args: %{"id" => media_item_id} = args}) do
|
||||||
should_force = Map.get(args, "force", false)
|
should_force = Map.get(args, "force", false)
|
||||||
is_quality_upgrade = Map.get(args, "quality_upgrade?", false)
|
is_quality_upgrade = Map.get(args, "quality_upgrade?", false)
|
||||||
|
|
||||||
media_item =
|
media_item = fetch_and_run_prevent_download_user_script(media_item_id)
|
||||||
media_item_id
|
|
||||||
|> Media.get_media_item!()
|
|
||||||
|> Repo.preload(:source)
|
|
||||||
|
|
||||||
# If the source or media item is set to not download media, perform a no-op unless forced
|
# If the source or media item is set to not download media, perform a no-op unless forced
|
||||||
if (media_item.source.download_media && !media_item.prevent_download) || should_force do
|
if (media_item.source.download_media && !media_item.prevent_download) || should_force do
|
||||||
@@ -62,6 +59,20 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
|||||||
Ecto.StaleEntryError -> Logger.info("#{__MODULE__} discarded: media item #{media_item_id} stale")
|
Ecto.StaleEntryError -> Logger.info("#{__MODULE__} discarded: media item #{media_item_id} stale")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If a user script exists and, when run, returns a non-zero exit code, prevent this and all future downloads
|
||||||
|
# of the media item.
|
||||||
|
defp fetch_and_run_prevent_download_user_script(media_item_id) do
|
||||||
|
media_item = Media.get_media_item!(media_item_id)
|
||||||
|
|
||||||
|
{:ok, media_item} =
|
||||||
|
case run_user_script(:media_pre_download, media_item) do
|
||||||
|
{:ok, _, exit_code} when exit_code != 0 -> Media.update_media_item(media_item, %{prevent_download: true})
|
||||||
|
_ -> {:ok, media_item}
|
||||||
|
end
|
||||||
|
|
||||||
|
Repo.preload(media_item, :source)
|
||||||
|
end
|
||||||
|
|
||||||
defp download_media_and_schedule_jobs(media_item, is_quality_upgrade, should_force) do
|
defp download_media_and_schedule_jobs(media_item, is_quality_upgrade, should_force) do
|
||||||
overwrite_behaviour = if should_force || is_quality_upgrade, do: :force_overwrites, else: :no_force_overwrites
|
overwrite_behaviour = if should_force || is_quality_upgrade, do: :force_overwrites, else: :no_force_overwrites
|
||||||
override_opts = [overwrite_behaviour: overwrite_behaviour]
|
override_opts = [overwrite_behaviour: overwrite_behaviour]
|
||||||
@@ -74,9 +85,9 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
|||||||
media_redownloaded_at: get_redownloaded_at(is_quality_upgrade)
|
media_redownloaded_at: get_redownloaded_at(is_quality_upgrade)
|
||||||
})
|
})
|
||||||
|
|
||||||
:ok = run_user_script(updated_media_item)
|
run_user_script(:media_downloaded, updated_media_item)
|
||||||
|
|
||||||
{:ok, updated_media_item}
|
:ok
|
||||||
|
|
||||||
{:recovered, _} ->
|
{:recovered, _} ->
|
||||||
{:error, :retry}
|
{:error, :retry}
|
||||||
@@ -112,9 +123,9 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
|||||||
|
|
||||||
# NOTE: I like this pattern of using the default value so that I don't have to
|
# NOTE: I like this pattern of using the default value so that I don't have to
|
||||||
# define it in config.exs (and friends). Consider using this elsewhere.
|
# define it in config.exs (and friends). Consider using this elsewhere.
|
||||||
defp run_user_script(media_item) do
|
defp run_user_script(event, media_item) do
|
||||||
runner = Application.get_env(:pinchflat, :user_script_runner, UserScriptRunner)
|
runner = Application.get_env(:pinchflat, :user_script_runner, UserScriptRunner)
|
||||||
|
|
||||||
runner.run(:media_downloaded, media_item)
|
runner.run(event, media_item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ defmodule Pinchflat.Downloading.MediaDownloader do
|
|||||||
|> MetadataParser.parse_for_media_item()
|
|> MetadataParser.parse_for_media_item()
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
media_downloaded_at: DateTime.utc_now(),
|
media_downloaded_at: DateTime.utc_now(),
|
||||||
|
culled_at: nil,
|
||||||
nfo_filepath: determine_nfo_filepath(media_with_preloads, parsed_json),
|
nfo_filepath: determine_nfo_filepath(media_with_preloads, parsed_json),
|
||||||
metadata: %{
|
metadata: %{
|
||||||
# IDEA: might be worth kicking off a job for this since thumbnail fetching
|
# IDEA: might be worth kicking off a job for this since thumbnail fetching
|
||||||
@@ -105,8 +106,9 @@ defmodule Pinchflat.Downloading.MediaDownloader do
|
|||||||
|
|
||||||
defp download_with_options(url, item_with_preloads, output_filepath, override_opts) do
|
defp download_with_options(url, item_with_preloads, output_filepath, override_opts) do
|
||||||
{:ok, options} = DownloadOptionBuilder.build(item_with_preloads, override_opts)
|
{:ok, options} = DownloadOptionBuilder.build(item_with_preloads, override_opts)
|
||||||
|
runner_opts = [output_filepath: output_filepath, use_cookies: item_with_preloads.source.use_cookies]
|
||||||
|
|
||||||
YtDlpMedia.download(url, options, output_filepath: output_filepath)
|
YtDlpMedia.download(url, options, runner_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp recoverable_errors do
|
defp recoverable_errors do
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ defmodule Pinchflat.Downloading.MediaQualityUpgradeWorker do
|
|||||||
"""
|
"""
|
||||||
@impl Oban.Worker
|
@impl Oban.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def perform(%Oban.Job{}) do
|
||||||
redownloadable_media = Media.list_redownloadable_media_items()
|
upgradable_media = Media.list_upgradeable_media_items()
|
||||||
Logger.info("Redownloading #{length(redownloadable_media)} media items")
|
Logger.info("Redownloading #{length(upgradable_media)} media items")
|
||||||
|
|
||||||
Enum.each(redownloadable_media, fn media_item ->
|
Enum.each(upgradable_media, fn media_item ->
|
||||||
MediaDownloadWorker.kickoff_with_task(media_item, %{quality_upgrade?: true})
|
MediaDownloadWorker.kickoff_with_task(media_item, %{quality_upgrade?: true})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ defmodule Pinchflat.Downloading.MediaRetentionWorker do
|
|||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
use Oban.Worker,
|
use Oban.Worker,
|
||||||
queue: :local_metadata,
|
queue: :local_data,
|
||||||
unique: [period: :infinity, states: [:available, :scheduled, :retryable, :executing]],
|
unique: [period: :infinity, states: [:available, :scheduled, :retryable, :executing]],
|
||||||
tags: ["media_item", "local_metadata"]
|
tags: ["media_item", "local_data"]
|
||||||
|
|
||||||
|
use Pinchflat.Media.MediaQuery
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@@ -20,14 +23,51 @@ defmodule Pinchflat.Downloading.MediaRetentionWorker do
|
|||||||
"""
|
"""
|
||||||
@impl Oban.Worker
|
@impl Oban.Worker
|
||||||
def perform(%Oban.Job{}) do
|
def perform(%Oban.Job{}) do
|
||||||
cullable_media = Media.list_cullable_media_items()
|
cull_cullable_media_items()
|
||||||
|
delete_media_items_from_before_cutoff()
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cull_cullable_media_items do
|
||||||
|
cullable_media =
|
||||||
|
MediaQuery.new()
|
||||||
|
|> MediaQuery.require_assoc(:source)
|
||||||
|
|> where(^MediaQuery.cullable())
|
||||||
|
|> Repo.all()
|
||||||
|
|
||||||
Logger.info("Culling #{length(cullable_media)} media items past their retention date")
|
Logger.info("Culling #{length(cullable_media)} media items past their retention date")
|
||||||
|
|
||||||
Enum.each(cullable_media, fn media_item ->
|
Enum.each(cullable_media, fn media_item ->
|
||||||
|
# Setting `prevent_download` does what it says on the tin, but `culled_at` is purely informational.
|
||||||
|
# We don't actually do anything with that in terms of queries and it gets set to nil if the media item
|
||||||
|
# gets re-downloaded.
|
||||||
Media.delete_media_files(media_item, %{
|
Media.delete_media_files(media_item, %{
|
||||||
prevent_download: true,
|
prevent_download: true,
|
||||||
culled_at: DateTime.utc_now()
|
culled_at: DateTime.utc_now()
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp delete_media_items_from_before_cutoff do
|
||||||
|
deletable_media =
|
||||||
|
MediaQuery.new()
|
||||||
|
|> MediaQuery.require_assoc(:source)
|
||||||
|
|> where(^MediaQuery.deletable_based_on_source_cutoff())
|
||||||
|
|> Repo.all()
|
||||||
|
|
||||||
|
Logger.info("Deleting #{length(deletable_media)} media items that are from before the source cutoff")
|
||||||
|
|
||||||
|
Enum.each(deletable_media, fn media_item ->
|
||||||
|
# Note that I'm not setting `prevent_download` on the media_item here.
|
||||||
|
# That's because cutoff_date can easily change and it's a valid behavior to re-download older
|
||||||
|
# media items if the cutoff_date changes.
|
||||||
|
# Download is ultimately prevented because `MediaQuery.pending()` only returns media items
|
||||||
|
# from after the cutoff date (among other things), so it's not like the media will just immediately
|
||||||
|
# be re-downloaded.
|
||||||
|
Media.delete_media_files(media_item, %{
|
||||||
|
culled_at: DateTime.utc_now()
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
|
|||||||
defp create_media_item_from_media_id(source, media_id) do
|
defp create_media_item_from_media_id(source, media_id) do
|
||||||
url = "https://www.youtube.com/watch?v=#{media_id}"
|
url = "https://www.youtube.com/watch?v=#{media_id}"
|
||||||
|
|
||||||
case YtDlpMedia.get_media_attributes(url) do
|
case YtDlpMedia.get_media_attributes(url, use_cookies: source.use_cookies) do
|
||||||
{:ok, media_attrs} ->
|
{:ok, media_attrs} ->
|
||||||
Media.create_media_item_from_backend_attrs(source, media_attrs)
|
Media.create_media_item_from_backend_attrs(source, media_attrs)
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
|
|||||||
@behaviour UserScriptCommandRunner
|
@behaviour UserScriptCommandRunner
|
||||||
|
|
||||||
@event_types [
|
@event_types [
|
||||||
|
:media_pre_download,
|
||||||
:media_downloaded,
|
:media_downloaded,
|
||||||
:media_deleted
|
:media_deleted
|
||||||
]
|
]
|
||||||
@@ -22,24 +23,25 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
|
|||||||
|
|
||||||
This function will succeed in almost all cases, even if the user script command
|
This function will succeed in almost all cases, even if the user script command
|
||||||
failed - this is because I don't want bad scripts to stop the whole process.
|
failed - this is because I don't want bad scripts to stop the whole process.
|
||||||
If something fails, it'll be logged.
|
If something fails, it'll be logged and returned BUT the tuple will always
|
||||||
|
start with {:ok, ...}.
|
||||||
|
|
||||||
The only things that can cause a true failure are passing in an invalid event
|
The only things that can cause a true failure are passing in an invalid event
|
||||||
type or if the passed data cannot be encoded into JSON - both indicative of
|
type or if the passed data cannot be encoded into JSON - both indicative of
|
||||||
failures in the development process.
|
failures in the development process.
|
||||||
|
|
||||||
Returns :ok
|
Returns {:ok, :no_executable} | {:ok, output, exit_code}
|
||||||
"""
|
"""
|
||||||
@impl UserScriptCommandRunner
|
@impl UserScriptCommandRunner
|
||||||
def run(event_type, encodable_data) when event_type in @event_types do
|
def run(event_type, encodable_data) when event_type in @event_types do
|
||||||
case backend_executable() do
|
case backend_executable() do
|
||||||
{:ok, :no_executable} ->
|
{:ok, :no_executable} ->
|
||||||
:ok
|
{:ok, :no_executable}
|
||||||
|
|
||||||
{:ok, executable_path} ->
|
{:ok, executable_path} ->
|
||||||
{:ok, encoded_data} = Phoenix.json_library().encode(encodable_data)
|
{:ok, encoded_data} = Phoenix.json_library().encode(encodable_data)
|
||||||
|
|
||||||
{_output, _exit_code} =
|
{output, exit_code} =
|
||||||
CliUtils.wrap_cmd(
|
CliUtils.wrap_cmd(
|
||||||
executable_path,
|
executable_path,
|
||||||
[to_string(event_type), encoded_data],
|
[to_string(event_type), encoded_data],
|
||||||
@@ -47,7 +49,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
|
|||||||
logging_arg_override: "[suppressed]"
|
logging_arg_override: "[suppressed]"
|
||||||
)
|
)
|
||||||
|
|
||||||
:ok
|
{:ok, output, exit_code}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -62,7 +64,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
|
|||||||
if FilesystemUtils.exists_and_nonempty?(filepath) do
|
if FilesystemUtils.exists_and_nonempty?(filepath) do
|
||||||
{:ok, filepath}
|
{:ok, filepath}
|
||||||
else
|
else
|
||||||
Logger.warning("User scripts lifecyle file either not present or is empty. Skipping.")
|
Logger.info("User scripts lifecyle file either not present or is empty. Skipping.")
|
||||||
|
|
||||||
{:ok, :no_executable}
|
{:ok, :no_executable}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -25,21 +25,10 @@ defmodule Pinchflat.Media do
|
|||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns a list of media_items that are cullable based on the retention period
|
Returns a list of media_items that are upgradeable based on the redownload delay
|
||||||
of the source they belong to.
|
of the media_profile their source belongs to. In this context, upgradeable means
|
||||||
|
that it's been long enough since upload that the video may be in a higher quality
|
||||||
Returns [%MediaItem{}, ...]
|
or have better sponsorblock segments (or similar).
|
||||||
"""
|
|
||||||
def list_cullable_media_items do
|
|
||||||
MediaQuery.new()
|
|
||||||
|> MediaQuery.require_assoc(:source)
|
|
||||||
|> where(^MediaQuery.cullable())
|
|
||||||
|> Repo.all()
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns a list of media_items that are redownloadable based on the redownload delay
|
|
||||||
of the media_profile their source belongs to.
|
|
||||||
|
|
||||||
The logic is that a media_item is past_redownload_delay if the media_item's uploaded_at is
|
The logic is that a media_item is past_redownload_delay if the media_item's uploaded_at is
|
||||||
at least redownload_delay_days ago AND `media_downloaded_at` - `redownload_delay_days`
|
at least redownload_delay_days ago AND `media_downloaded_at` - `redownload_delay_days`
|
||||||
@@ -52,10 +41,10 @@ defmodule Pinchflat.Media do
|
|||||||
|
|
||||||
Returns [%MediaItem{}, ...]
|
Returns [%MediaItem{}, ...]
|
||||||
"""
|
"""
|
||||||
def list_redownloadable_media_items do
|
def list_upgradeable_media_items do
|
||||||
MediaQuery.new()
|
MediaQuery.new()
|
||||||
|> MediaQuery.require_assoc(:media_profile)
|
|> MediaQuery.require_assoc(:media_profile)
|
||||||
|> where(^MediaQuery.redownloadable())
|
|> where(^MediaQuery.upgradeable())
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -182,7 +171,7 @@ defmodule Pinchflat.Media do
|
|||||||
|
|
||||||
if delete_files do
|
if delete_files do
|
||||||
{:ok, _} = do_delete_media_files(media_item)
|
{:ok, _} = do_delete_media_files(media_item)
|
||||||
:ok = run_user_script(:media_deleted, media_item)
|
run_user_script(:media_deleted, media_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Should delete these no matter what
|
# Should delete these no matter what
|
||||||
@@ -205,7 +194,7 @@ defmodule Pinchflat.Media do
|
|||||||
|
|
||||||
Tasks.delete_tasks_for(media_item)
|
Tasks.delete_tasks_for(media_item)
|
||||||
{:ok, _} = do_delete_media_files(media_item)
|
{:ok, _} = do_delete_media_files(media_item)
|
||||||
:ok = run_user_script(:media_deleted, media_item)
|
run_user_script(:media_deleted, media_item)
|
||||||
|
|
||||||
update_media_item(media_item, Map.merge(filepath_attrs, addl_attrs))
|
update_media_item(media_item, Map.merge(filepath_attrs, addl_attrs))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ defmodule Pinchflat.Media.MediaQuery do
|
|||||||
def downloaded, do: dynamic([mi], not is_nil(mi.media_filepath))
|
def downloaded, do: dynamic([mi], not is_nil(mi.media_filepath))
|
||||||
def download_prevented, do: dynamic([mi], mi.prevent_download == true)
|
def download_prevented, do: dynamic([mi], mi.prevent_download == true)
|
||||||
def culling_prevented, do: dynamic([mi], mi.prevent_culling == true)
|
def culling_prevented, do: dynamic([mi], mi.prevent_culling == true)
|
||||||
def culled, do: dynamic([mi], not is_nil(mi.culled_at))
|
|
||||||
def redownloaded, do: dynamic([mi], not is_nil(mi.media_redownloaded_at))
|
def redownloaded, do: dynamic([mi], not is_nil(mi.media_redownloaded_at))
|
||||||
def upload_date_matches(other_date), do: dynamic([mi], fragment("date(?) = date(?)", mi.uploaded_at, ^other_date))
|
def upload_date_matches(other_date), do: dynamic([mi], fragment("date(?) = date(?)", mi.uploaded_at, ^other_date))
|
||||||
|
|
||||||
@@ -76,6 +75,14 @@ defmodule Pinchflat.Media.MediaQuery do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def meets_min_and_max_duration do
|
||||||
|
dynamic(
|
||||||
|
[mi, source],
|
||||||
|
(is_nil(source.min_duration_seconds) or fragment("duration_seconds >= ?", source.min_duration_seconds)) and
|
||||||
|
(is_nil(source.max_duration_seconds) or fragment("duration_seconds <= ?", source.max_duration_seconds))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def past_retention_period do
|
def past_retention_period do
|
||||||
dynamic(
|
dynamic(
|
||||||
[mi, source],
|
[mi, source],
|
||||||
@@ -108,6 +115,15 @@ defmodule Pinchflat.Media.MediaQuery do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def deletable_based_on_source_cutoff do
|
||||||
|
dynamic(
|
||||||
|
[mi, source],
|
||||||
|
^downloaded() and
|
||||||
|
not (^upload_date_after_source_cutoff()) and
|
||||||
|
not (^culling_prevented())
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def pending do
|
def pending do
|
||||||
dynamic(
|
dynamic(
|
||||||
[mi],
|
[mi],
|
||||||
@@ -115,16 +131,16 @@ defmodule Pinchflat.Media.MediaQuery do
|
|||||||
not (^download_prevented()) and
|
not (^download_prevented()) and
|
||||||
^upload_date_after_source_cutoff() and
|
^upload_date_after_source_cutoff() and
|
||||||
^format_matching_profile_preference() and
|
^format_matching_profile_preference() and
|
||||||
^matches_source_title_regex()
|
^matches_source_title_regex() and
|
||||||
|
^meets_min_and_max_duration()
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def redownloadable do
|
def upgradeable do
|
||||||
dynamic(
|
dynamic(
|
||||||
[mi, source],
|
[mi, source],
|
||||||
^downloaded() and
|
^downloaded() and
|
||||||
not (^download_prevented()) and
|
not (^download_prevented()) and
|
||||||
not (^culled()) and
|
|
||||||
not (^redownloaded()) and
|
not (^redownloaded()) and
|
||||||
^past_redownload_delay()
|
^past_redownload_delay()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -62,11 +62,13 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
|
|||||||
|
|
||||||
Returns binary() | nil
|
Returns binary() | nil
|
||||||
"""
|
"""
|
||||||
def download_and_store_thumbnail_for(database_record) do
|
def download_and_store_thumbnail_for(media_item_with_preloads) do
|
||||||
yt_dlp_filepath = generate_filepath_for(database_record, "thumbnail.%(ext)s")
|
yt_dlp_filepath = generate_filepath_for(media_item_with_preloads, "thumbnail.%(ext)s")
|
||||||
real_filepath = generate_filepath_for(database_record, "thumbnail.jpg")
|
real_filepath = generate_filepath_for(media_item_with_preloads, "thumbnail.jpg")
|
||||||
|
command_opts = [output: yt_dlp_filepath]
|
||||||
|
addl_opts = [use_cookies: media_item_with_preloads.source.use_cookies]
|
||||||
|
|
||||||
case YtDlpMedia.download_thumbnail(database_record.original_url, output: yt_dlp_filepath) do
|
case YtDlpMedia.download_thumbnail(media_item_with_preloads.original_url, command_opts, addl_opts) do
|
||||||
{:ok, _} -> real_filepath
|
{:ok, _} -> real_filepath
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||||||
|
|
||||||
defp determine_series_directory(source) do
|
defp determine_series_directory(source) do
|
||||||
output_path = DownloadOptionBuilder.build_output_path_for(source)
|
output_path = DownloadOptionBuilder.build_output_path_for(source)
|
||||||
{:ok, %{filepath: filepath}} = MediaCollection.get_source_details(source.original_url, output: output_path)
|
runner_opts = [output: output_path]
|
||||||
|
addl_opts = [use_cookies: source.use_cookies]
|
||||||
|
{:ok, %{filepath: filepath}} = MediaCollection.get_source_details(source.original_url, runner_opts, addl_opts)
|
||||||
|
|
||||||
case MetadataFileHelpers.series_directory_from_media_filepath(filepath) do
|
case MetadataFileHelpers.series_directory_from_media_filepath(filepath) do
|
||||||
{:ok, series_directory} -> series_directory
|
{:ok, series_directory} -> series_directory
|
||||||
@@ -119,7 +121,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
|
|||||||
base_opts ++ [:write_thumbnail, playlist_items: 1]
|
base_opts ++ [:write_thumbnail, playlist_items: 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
MediaCollection.get_source_metadata(source.original_url, opts)
|
MediaCollection.get_source_metadata(source.original_url, opts, use_cookies: source.use_cookies)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp tmp_directory do
|
defp tmp_directory do
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ defmodule Pinchflat.Profiles.MediaProfile do
|
|||||||
livestream_behaviour
|
livestream_behaviour
|
||||||
preferred_resolution
|
preferred_resolution
|
||||||
redownload_delay_days
|
redownload_delay_days
|
||||||
|
marked_for_deletion_at
|
||||||
)a
|
)a
|
||||||
|
|
||||||
@required_fields ~w(name output_path_template)a
|
@required_fields ~w(name output_path_template)a
|
||||||
@@ -65,6 +66,8 @@ defmodule Pinchflat.Profiles.MediaProfile do
|
|||||||
field :livestream_behaviour, Ecto.Enum, values: ~w(include exclude only)a, default: :include
|
field :livestream_behaviour, Ecto.Enum, values: ~w(include exclude only)a, default: :include
|
||||||
field :preferred_resolution, Ecto.Enum, values: ~w(4320p 2160p 1080p 720p 480p 360p audio)a, default: :"1080p"
|
field :preferred_resolution, Ecto.Enum, values: ~w(4320p 2160p 1080p 720p 480p 360p audio)a, default: :"1080p"
|
||||||
|
|
||||||
|
field :marked_for_deletion_at, :utc_datetime
|
||||||
|
|
||||||
has_many :sources, Source
|
has_many :sources, Source
|
||||||
|
|
||||||
timestamps(type: :utc_datetime)
|
timestamps(type: :utc_datetime)
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
defmodule Pinchflat.Profiles.MediaProfileDeletionWorker do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Oban.Worker,
|
||||||
|
queue: :local_data,
|
||||||
|
tags: ["media_profiles", "local_data"]
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias __MODULE__
|
||||||
|
alias Pinchflat.Profiles
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Starts the profile deletion worker. Does not attach it to a task like `kickoff_with_task/2`
|
||||||
|
since deletion also cancels all tasks for the profile
|
||||||
|
|
||||||
|
Returns {:ok, %Task{}} | {:error, %Ecto.Changeset{}}
|
||||||
|
"""
|
||||||
|
def kickoff(profile, job_args \\ %{}, job_opts \\ []) do
|
||||||
|
%{id: profile.id}
|
||||||
|
|> Map.merge(job_args)
|
||||||
|
|> MediaProfileDeletionWorker.new(job_opts)
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a profile and optionally deletes its files
|
||||||
|
|
||||||
|
Returns :ok
|
||||||
|
"""
|
||||||
|
@impl Oban.Worker
|
||||||
|
def perform(%Oban.Job{args: %{"id" => profile_id} = args}) do
|
||||||
|
delete_files = Map.get(args, "delete_files", false)
|
||||||
|
profile = Profiles.get_media_profile!(profile_id)
|
||||||
|
|
||||||
|
Profiles.delete_media_profile(profile, delete_files: delete_files)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -94,7 +94,8 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
|
|||||||
{:ok, pid} = FileFollowerServer.start_link()
|
{:ok, pid} = FileFollowerServer.start_link()
|
||||||
|
|
||||||
handler = fn filepath -> setup_file_follower_watcher(pid, filepath, source) end
|
handler = fn filepath -> setup_file_follower_watcher(pid, filepath, source) end
|
||||||
result = MediaCollection.get_media_attributes_for_collection(source.original_url, file_listener_handler: handler)
|
runner_opts = [file_listener_handler: handler, use_cookies: source.use_cookies]
|
||||||
|
result = MediaCollection.get_media_attributes_for_collection(source.original_url, runner_opts)
|
||||||
|
|
||||||
FileFollowerServer.stop(pid)
|
FileFollowerServer.stop(pid)
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ defmodule Pinchflat.Sources.Source do
|
|||||||
series_directory
|
series_directory
|
||||||
index_frequency_minutes
|
index_frequency_minutes
|
||||||
fast_index
|
fast_index
|
||||||
|
use_cookies
|
||||||
download_media
|
download_media
|
||||||
last_indexed_at
|
last_indexed_at
|
||||||
original_url
|
original_url
|
||||||
@@ -35,6 +36,9 @@ defmodule Pinchflat.Sources.Source do
|
|||||||
title_filter_regex
|
title_filter_regex
|
||||||
media_profile_id
|
media_profile_id
|
||||||
output_path_template_override
|
output_path_template_override
|
||||||
|
marked_for_deletion_at
|
||||||
|
min_duration_seconds
|
||||||
|
max_duration_seconds
|
||||||
)a
|
)a
|
||||||
|
|
||||||
# Expensive API calls are made when a source is inserted/updated so
|
# Expensive API calls are made when a source is inserted/updated so
|
||||||
@@ -72,6 +76,7 @@ defmodule Pinchflat.Sources.Source do
|
|||||||
field :collection_type, Ecto.Enum, values: [:channel, :playlist]
|
field :collection_type, Ecto.Enum, values: [:channel, :playlist]
|
||||||
field :index_frequency_minutes, :integer, default: 60 * 24
|
field :index_frequency_minutes, :integer, default: 60 * 24
|
||||||
field :fast_index, :boolean, default: false
|
field :fast_index, :boolean, default: false
|
||||||
|
field :use_cookies, :boolean, default: false
|
||||||
field :download_media, :boolean, default: true
|
field :download_media, :boolean, default: true
|
||||||
field :last_indexed_at, :utc_datetime
|
field :last_indexed_at, :utc_datetime
|
||||||
# Only download media items that were published after this date
|
# Only download media items that were published after this date
|
||||||
@@ -81,12 +86,17 @@ defmodule Pinchflat.Sources.Source do
|
|||||||
field :title_filter_regex, :string
|
field :title_filter_regex, :string
|
||||||
field :output_path_template_override, :string
|
field :output_path_template_override, :string
|
||||||
|
|
||||||
|
field :min_duration_seconds, :integer
|
||||||
|
field :max_duration_seconds, :integer
|
||||||
|
|
||||||
field :series_directory, :string
|
field :series_directory, :string
|
||||||
field :nfo_filepath, :string
|
field :nfo_filepath, :string
|
||||||
field :poster_filepath, :string
|
field :poster_filepath, :string
|
||||||
field :fanart_filepath, :string
|
field :fanart_filepath, :string
|
||||||
field :banner_filepath, :string
|
field :banner_filepath, :string
|
||||||
|
|
||||||
|
field :marked_for_deletion_at, :utc_datetime
|
||||||
|
|
||||||
belongs_to :media_profile, MediaProfile
|
belongs_to :media_profile, MediaProfile
|
||||||
|
|
||||||
has_one :metadata, SourceMetadata, on_replace: :update
|
has_one :metadata, SourceMetadata, on_replace: :update
|
||||||
@@ -113,6 +123,7 @@ defmodule Pinchflat.Sources.Source do
|
|||||||
|> dynamic_default(:uuid, fn _ -> Ecto.UUID.generate() end)
|
|> dynamic_default(:uuid, fn _ -> Ecto.UUID.generate() end)
|
||||||
|> validate_required(required_fields)
|
|> validate_required(required_fields)
|
||||||
|> validate_title_regex()
|
|> validate_title_regex()
|
||||||
|
|> validate_min_and_max_durations()
|
||||||
|> validate_number(:retention_period_days, greater_than_or_equal_to: 0)
|
|> validate_number(:retention_period_days, greater_than_or_equal_to: 0)
|
||||||
# Ensures it ends with `.{{ ext }}` or `.%(ext)s` or similar (with a little wiggle room)
|
# Ensures it ends with `.{{ ext }}` or `.%(ext)s` or similar (with a little wiggle room)
|
||||||
|> validate_format(:output_path_template_override, MediaProfile.ext_regex(), message: "must end with .{{ ext }}")
|
|> validate_format(:output_path_template_override, MediaProfile.ext_regex(), message: "must end with .{{ ext }}")
|
||||||
@@ -159,6 +170,17 @@ defmodule Pinchflat.Sources.Source do
|
|||||||
|
|
||||||
defp validate_title_regex(changeset), do: changeset
|
defp validate_title_regex(changeset), do: changeset
|
||||||
|
|
||||||
|
defp validate_min_and_max_durations(changeset) do
|
||||||
|
min_duration = get_change(changeset, :min_duration_seconds)
|
||||||
|
max_duration = get_change(changeset, :max_duration_seconds)
|
||||||
|
|
||||||
|
case {min_duration, max_duration} do
|
||||||
|
{min, max} when is_nil(min) or is_nil(max) -> changeset
|
||||||
|
{min, max} when min >= max -> add_error(changeset, :max_duration_seconds, "must be greater than minumum duration")
|
||||||
|
_ -> changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defimpl Jason.Encoder, for: Source do
|
defimpl Jason.Encoder, for: Source do
|
||||||
def encode(value, opts) do
|
def encode(value, opts) do
|
||||||
value
|
value
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
defmodule Pinchflat.Sources.SourceDeletionWorker do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Oban.Worker,
|
||||||
|
queue: :local_data,
|
||||||
|
tags: ["sources", "local_data"]
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias __MODULE__
|
||||||
|
alias Pinchflat.Sources
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Starts the source deletion worker. Does not attach it to a task like `kickoff_with_task/2`
|
||||||
|
since deletion also cancels all tasks for the source
|
||||||
|
|
||||||
|
Returns {:ok, %Task{}} | {:error, %Ecto.Changeset{}}
|
||||||
|
"""
|
||||||
|
def kickoff(source, job_args \\ %{}, job_opts \\ []) do
|
||||||
|
%{id: source.id}
|
||||||
|
|> Map.merge(job_args)
|
||||||
|
|> SourceDeletionWorker.new(job_opts)
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a source and optionally deletes its files
|
||||||
|
|
||||||
|
Returns :ok
|
||||||
|
"""
|
||||||
|
@impl Oban.Worker
|
||||||
|
def perform(%Oban.Job{args: %{"id" => source_id} = args}) do
|
||||||
|
delete_files = Map.get(args, "delete_files", false)
|
||||||
|
source = Sources.get_source!(source_id)
|
||||||
|
|
||||||
|
Sources.delete_source(source, delete_files: delete_files)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -180,7 +180,9 @@ defmodule Pinchflat.Sources do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp add_source_details_to_changeset(source, changeset) do
|
defp add_source_details_to_changeset(source, changeset) do
|
||||||
case MediaCollection.get_source_details(changeset.changes.original_url) do
|
use_cookies = Ecto.Changeset.get_field(changeset, :use_cookies)
|
||||||
|
|
||||||
|
case MediaCollection.get_source_details(changeset.changes.original_url, [], use_cookies: use_cookies) do
|
||||||
{:ok, source_details} ->
|
{:ok, source_details} ->
|
||||||
add_source_details_by_collection_type(source, changeset, source_details)
|
add_source_details_by_collection_type(source, changeset, source_details)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
|||||||
- :output_filepath - the path to save the output to. If not provided, a temporary
|
- :output_filepath - the path to save the output to. If not provided, a temporary
|
||||||
file will be created and used. Useful for if you need a reference to the file
|
file will be created and used. Useful for if you need a reference to the file
|
||||||
for a file watcher.
|
for a file watcher.
|
||||||
|
- :use_cookies - if true, will add a cookie file to the command options. Will not
|
||||||
|
attach a cookie file if the user hasn't set one up.
|
||||||
|
|
||||||
Returns {:ok, binary()} | {:error, output, status}.
|
Returns {:ok, binary()} | {:error, output, status}.
|
||||||
"""
|
"""
|
||||||
@@ -28,7 +30,7 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
|||||||
|
|
||||||
output_filepath = generate_output_filepath(addl_opts)
|
output_filepath = generate_output_filepath(addl_opts)
|
||||||
print_to_file_opts = [{:print_to_file, output_template}, output_filepath]
|
print_to_file_opts = [{:print_to_file, output_template}, output_filepath]
|
||||||
user_configured_opts = cookie_file_options()
|
user_configured_opts = cookie_file_options(addl_opts)
|
||||||
# These must stay in exactly this order, hence why I'm giving it its own variable.
|
# These must stay in exactly this order, hence why I'm giving it its own variable.
|
||||||
all_opts = command_opts ++ print_to_file_opts ++ user_configured_opts ++ global_options()
|
all_opts = command_opts ++ print_to_file_opts ++ user_configured_opts ++ global_options()
|
||||||
formatted_command_opts = [url] ++ CliUtils.parse_options(all_opts)
|
formatted_command_opts = [url] ++ CliUtils.parse_options(all_opts)
|
||||||
@@ -78,7 +80,14 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp cookie_file_options do
|
defp cookie_file_options(addl_opts) do
|
||||||
|
case Keyword.get(addl_opts, :use_cookies) do
|
||||||
|
true -> add_cookie_file()
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_cookie_file do
|
||||||
base_dir = Application.get_env(:pinchflat, :extras_directory)
|
base_dir = Application.get_env(:pinchflat, :extras_directory)
|
||||||
filename_options_map = %{cookies: "cookies.txt"}
|
filename_options_map = %{cookies: "cookies.txt"}
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ defmodule Pinchflat.YtDlp.Media do
|
|||||||
Returns {:ok, map()} | {:error, any, ...}.
|
Returns {:ok, map()} | {:error, any, ...}.
|
||||||
"""
|
"""
|
||||||
def download(url, command_opts \\ [], addl_opts \\ []) do
|
def download(url, command_opts \\ [], addl_opts \\ []) do
|
||||||
opts = [:no_simulate] ++ command_opts
|
all_command_opts = [:no_simulate] ++ command_opts
|
||||||
|
|
||||||
with {:ok, output} <- backend_runner().run(url, opts, "after_move:%()j", addl_opts),
|
with {:ok, output} <- backend_runner().run(url, all_command_opts, "after_move:%()j", addl_opts),
|
||||||
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
||||||
{:ok, parsed_json}
|
{:ok, parsed_json}
|
||||||
else
|
else
|
||||||
@@ -53,12 +53,12 @@ defmodule Pinchflat.YtDlp.Media do
|
|||||||
|
|
||||||
Returns {:ok, ""} | {:error, any, ...}.
|
Returns {:ok, ""} | {:error, any, ...}.
|
||||||
"""
|
"""
|
||||||
def download_thumbnail(url, command_opts \\ []) do
|
def download_thumbnail(url, command_opts \\ [], addl_opts \\ []) do
|
||||||
opts = [:no_simulate, :skip_download, :write_thumbnail, convert_thumbnail: "jpg"] ++ command_opts
|
all_command_opts = [:no_simulate, :skip_download, :write_thumbnail, convert_thumbnail: "jpg"] ++ command_opts
|
||||||
|
|
||||||
# NOTE: it doesn't seem like this command actually returns anything in `after_move` since
|
# NOTE: it doesn't seem like this command actually returns anything in `after_move` since
|
||||||
# we aren't downloading the main media file
|
# we aren't downloading the main media file
|
||||||
backend_runner().run(url, opts, "after_move:%()j")
|
backend_runner().run(url, all_command_opts, "after_move:%()j", addl_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@@ -66,12 +66,12 @@ defmodule Pinchflat.YtDlp.Media do
|
|||||||
|
|
||||||
Returns {:ok, %Media{}} | {:error, any, ...}.
|
Returns {:ok, %Media{}} | {:error, any, ...}.
|
||||||
"""
|
"""
|
||||||
def get_media_attributes(url) do
|
def get_media_attributes(url, addl_opts \\ []) 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 = indexing_output_template()
|
output_template = indexing_output_template()
|
||||||
|
|
||||||
case runner.run(url, command_opts, output_template) do
|
case runner.run(url, command_opts, output_template, addl_opts) do
|
||||||
{:ok, output} ->
|
{:ok, output} ->
|
||||||
output
|
output
|
||||||
|> Phoenix.json_library().decode!()
|
|> Phoenix.json_library().decode!()
|
||||||
|
|||||||
@@ -25,15 +25,17 @@ defmodule Pinchflat.YtDlp.MediaCollection do
|
|||||||
# the first video has not released yet (ie: is a premier). We don't care about
|
# the first video has not released yet (ie: is a premier). We don't care about
|
||||||
# available formats since we're just getting the media details
|
# available formats since we're just getting the media details
|
||||||
command_opts = [:simulate, :skip_download, :ignore_no_formats_error, :no_warnings]
|
command_opts = [:simulate, :skip_download, :ignore_no_formats_error, :no_warnings]
|
||||||
|
use_cookies = Keyword.get(addl_opts, :use_cookies, false)
|
||||||
output_template = YtDlpMedia.indexing_output_template()
|
output_template = YtDlpMedia.indexing_output_template()
|
||||||
output_filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
output_filepath = FilesystemUtils.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)
|
||||||
|
runner_opts = [output_filepath: output_filepath, use_cookies: use_cookies]
|
||||||
|
|
||||||
if file_listener_handler do
|
if file_listener_handler do
|
||||||
file_listener_handler.(output_filepath)
|
file_listener_handler.(output_filepath)
|
||||||
end
|
end
|
||||||
|
|
||||||
case runner.run(url, command_opts, output_template, output_filepath: output_filepath) do
|
case runner.run(url, command_opts, output_template, runner_opts) do
|
||||||
{:ok, output} ->
|
{:ok, output} ->
|
||||||
parsed_lines =
|
parsed_lines =
|
||||||
output
|
output
|
||||||
@@ -64,7 +66,7 @@ defmodule Pinchflat.YtDlp.MediaCollection do
|
|||||||
|
|
||||||
Returns {:ok, map()} | {:error, any, ...}.
|
Returns {:ok, map()} | {:error, any, ...}.
|
||||||
"""
|
"""
|
||||||
def get_source_details(source_url, addl_opts \\ []) do
|
def get_source_details(source_url, command_opts \\ [], addl_opts \\ []) do
|
||||||
# `ignore_no_formats_error` is necessary because yt-dlp will error out if
|
# `ignore_no_formats_error` is necessary because yt-dlp will error out if
|
||||||
# the first video has not released yet (ie: is a premier). We don't care about
|
# the first video has not released yet (ie: is a premier). We don't care about
|
||||||
# available formats since we're just getting the source details
|
# available formats since we're just getting the source details
|
||||||
@@ -75,10 +77,10 @@ defmodule Pinchflat.YtDlp.MediaCollection do
|
|||||||
playlist_end: 1
|
playlist_end: 1
|
||||||
]
|
]
|
||||||
|
|
||||||
command_opts = default_opts ++ addl_opts
|
all_command_opts = default_opts ++ command_opts
|
||||||
output_template = "%(.{channel,channel_id,playlist_id,playlist_title,filename})j"
|
output_template = "%(.{channel,channel_id,playlist_id,playlist_title,filename})j"
|
||||||
|
|
||||||
with {:ok, output} <- backend_runner().run(source_url, command_opts, output_template),
|
with {:ok, output} <- backend_runner().run(source_url, all_command_opts, output_template, addl_opts),
|
||||||
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
||||||
{:ok, format_source_details(parsed_json)}
|
{:ok, format_source_details(parsed_json)}
|
||||||
else
|
else
|
||||||
@@ -109,14 +111,14 @@ defmodule Pinchflat.YtDlp.MediaCollection do
|
|||||||
|
|
||||||
Returns {:ok, map()} | {:error, any, ...}.
|
Returns {:ok, map()} | {:error, any, ...}.
|
||||||
"""
|
"""
|
||||||
def get_source_metadata(source_url, addl_opts \\ [playlist_items: 0]) do
|
def get_source_metadata(source_url, command_opts, addl_opts \\ []) do
|
||||||
# This only validates that the `playlist_items` key is present. It's otherwise unused
|
# This only validates that the `playlist_items` key is present. It's otherwise unused
|
||||||
_playlist_items = Keyword.fetch!(addl_opts, :playlist_items)
|
_playlist_items = Keyword.fetch!(command_opts, :playlist_items)
|
||||||
|
|
||||||
opts = [:skip_download] ++ addl_opts
|
all_command_opts = [:skip_download] ++ command_opts
|
||||||
output_template = "playlist:%()j"
|
output_template = "playlist:%()j"
|
||||||
|
|
||||||
with {:ok, output} <- backend_runner().run(source_url, opts, output_template),
|
with {:ok, output} <- backend_runner().run(source_url, all_command_opts, output_template, addl_opts),
|
||||||
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
{:ok, parsed_json} <- Phoenix.json_library().decode(output) do
|
||||||
{:ok, parsed_json}
|
{:ok, parsed_json}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileController do
|
|||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Profiles
|
alias Pinchflat.Profiles
|
||||||
alias Pinchflat.Profiles.MediaProfile
|
alias Pinchflat.Profiles.MediaProfile
|
||||||
|
alias Pinchflat.Profiles.MediaProfileDeletionWorker
|
||||||
|
|
||||||
def index(conn, _params) do
|
def index(conn, _params) do
|
||||||
media_profiles =
|
media_profiles =
|
||||||
MediaProfile
|
MediaProfile
|
||||||
|
|> where([mp], is_nil(mp.marked_for_deletion_at))
|
||||||
|> order_by(asc: :name)
|
|> order_by(asc: :name)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
@@ -70,19 +72,15 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, %{"id" => id} = params) do
|
def delete(conn, %{"id" => id} = params) do
|
||||||
delete_files = Map.get(params, "delete_files", false)
|
# This awkward comparison converts the string to a boolean
|
||||||
|
delete_files = Map.get(params, "delete_files", "") == "true"
|
||||||
media_profile = Profiles.get_media_profile!(id)
|
media_profile = Profiles.get_media_profile!(id)
|
||||||
{:ok, _media_profile} = Profiles.delete_media_profile(media_profile, delete_files: delete_files)
|
|
||||||
|
|
||||||
flash_message =
|
{:ok, _} = Profiles.update_media_profile(media_profile, %{marked_for_deletion_at: DateTime.utc_now()})
|
||||||
if delete_files do
|
MediaProfileDeletionWorker.kickoff(media_profile, %{delete_files: delete_files})
|
||||||
"Media profile, its sources, and its files deleted successfully."
|
|
||||||
else
|
|
||||||
"Media profile and its sources deleted successfully. Files were not deleted."
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, flash_message)
|
|> put_flash(:info, "Media Profile deletion started. This may take a while to complete.")
|
||||||
|> redirect(to: ~p"/media_profiles")
|
|> redirect(to: ~p"/media_profiles")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ defmodule PinchflatWeb.Sources.SourceController do
|
|||||||
alias Pinchflat.Sources.Source
|
alias Pinchflat.Sources.Source
|
||||||
alias Pinchflat.Media.MediaItem
|
alias Pinchflat.Media.MediaItem
|
||||||
alias Pinchflat.Profiles.MediaProfile
|
alias Pinchflat.Profiles.MediaProfile
|
||||||
|
alias Pinchflat.Sources.SourceDeletionWorker
|
||||||
alias Pinchflat.Downloading.DownloadingHelpers
|
alias Pinchflat.Downloading.DownloadingHelpers
|
||||||
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
||||||
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
||||||
@@ -17,6 +18,7 @@ defmodule PinchflatWeb.Sources.SourceController do
|
|||||||
from s in Source,
|
from s in Source,
|
||||||
as: :source,
|
as: :source,
|
||||||
inner_join: mp in assoc(s, :media_profile),
|
inner_join: mp in assoc(s, :media_profile),
|
||||||
|
where: is_nil(s.marked_for_deletion_at) and is_nil(mp.marked_for_deletion_at),
|
||||||
preload: [media_profile: mp],
|
preload: [media_profile: mp],
|
||||||
order_by: [asc: s.custom_name],
|
order_by: [asc: s.custom_name],
|
||||||
select: map(s, ^Source.__schema__(:fields)),
|
select: map(s, ^Source.__schema__(:fields)),
|
||||||
@@ -60,6 +62,7 @@ defmodule PinchflatWeb.Sources.SourceController do
|
|||||||
| id: nil,
|
| id: nil,
|
||||||
uuid: nil,
|
uuid: nil,
|
||||||
custom_name: nil,
|
custom_name: nil,
|
||||||
|
description: nil,
|
||||||
collection_name: nil,
|
collection_name: nil,
|
||||||
collection_id: nil,
|
collection_id: nil,
|
||||||
collection_type: nil,
|
collection_type: nil,
|
||||||
@@ -124,19 +127,15 @@ defmodule PinchflatWeb.Sources.SourceController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, %{"id" => id} = params) do
|
def delete(conn, %{"id" => id} = params) do
|
||||||
delete_files = Map.get(params, "delete_files", false)
|
# This awkward comparison converts the string to a boolean
|
||||||
|
delete_files = Map.get(params, "delete_files", "") == "true"
|
||||||
source = Sources.get_source!(id)
|
source = Sources.get_source!(id)
|
||||||
{:ok, _source} = Sources.delete_source(source, delete_files: delete_files)
|
|
||||||
|
|
||||||
flash_message =
|
{:ok, _} = Sources.update_source(source, %{marked_for_deletion_at: DateTime.utc_now()})
|
||||||
if delete_files do
|
SourceDeletionWorker.kickoff(source, %{delete_files: delete_files})
|
||||||
"Source and files deleted successfully."
|
|
||||||
else
|
|
||||||
"Source deleted successfully. Files were not deleted."
|
|
||||||
end
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, flash_message)
|
|> put_flash(:info, "Source deletion started. This may take a while to complete.")
|
||||||
|> redirect(to: ~p"/sources")
|
|> redirect(to: ~p"/sources")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,31 @@
|
|||||||
help="Unchecking still indexes media but it won't be downloaded until you enable this option"
|
help="Unchecking still indexes media but it won't be downloaded until you enable this option"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<.input
|
||||||
|
field={f[:use_cookies]}
|
||||||
|
type="toggle"
|
||||||
|
label="Use Cookies for Downloading"
|
||||||
|
help="Uses your YouTube cookies for this source (if configured). Used for downloading private playlists and videos. See docs for important details"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section x-show="advancedMode">
|
||||||
|
<.input
|
||||||
|
field={f[:min_duration_seconds]}
|
||||||
|
type="number"
|
||||||
|
label="Minimum Duration (seconds)"
|
||||||
|
min="0"
|
||||||
|
help="Minimum duration of the media to be downloaded. Can be blank"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<.input
|
||||||
|
field={f[:max_duration_seconds]}
|
||||||
|
type="number"
|
||||||
|
label="Maximum Duration (seconds)"
|
||||||
|
min="0"
|
||||||
|
help="Maximum duration of the media to be downloaded. Can be blank"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
<.input
|
<.input
|
||||||
field={f[:download_cutoff_date]}
|
field={f[:download_cutoff_date]}
|
||||||
type="text"
|
type="text"
|
||||||
@@ -94,7 +119,7 @@
|
|||||||
maxlength="10"
|
maxlength="10"
|
||||||
pattern="((?:19|20)[0-9][0-9])-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"
|
pattern="((?:19|20)[0-9][0-9])-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"
|
||||||
title="YYYY-MM-DD"
|
title="YYYY-MM-DD"
|
||||||
help="Only download media uploaded after this date. Leave blank to download all media. Must be in YYYY-MM-DD format"
|
help="Only download media uploaded after this date. Leave blank to download all media. Must be in YYYY-MM-DD format. Old media may be deleted or downloaded if you change this date"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<.input
|
<.input
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ defmodule Pinchflat.MixProject do
|
|||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :pinchflat,
|
app: :pinchflat,
|
||||||
version: "2024.7.19-1",
|
version: "2024.8.14",
|
||||||
elixir: "~> 1.17",
|
elixir: "~> 1.17",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
@@ -103,7 +103,8 @@ defmodule Pinchflat.MixProject do
|
|||||||
"ecto.rollback": [
|
"ecto.rollback": [
|
||||||
"ecto.rollback",
|
"ecto.rollback",
|
||||||
~s(cmd [ -z "$MIX_ENV" ] && yarn run create-erd || echo "No ERD generated")
|
~s(cmd [ -z "$MIX_ENV" ] && yarn run create-erd || echo "No ERD generated")
|
||||||
]
|
],
|
||||||
|
"version.bump": "cmd ./tooling/version_bump.sh"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+2
-2
@@ -6,8 +6,8 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"create-erd": "sqleton -o priv/repo/erd.png priv/repo/pinchflat_dev.db",
|
"create-erd": "sqleton -o priv/repo/erd.png priv/repo/pinchflat_dev.db",
|
||||||
"lint:check": "prettier . --check --config=tooling/.prettierrc.js --ignore-path=tooling/.prettierignore --ignore-path=.gitignore",
|
"lint:check": "prettier . --check --config=.prettierrc.js --ignore-path=.prettierignore --ignore-path=.gitignore",
|
||||||
"lint:fix": "prettier . --write --config=tooling/.prettierrc.js --ignore-path=tooling/.prettierignore --ignore-path=.gitignore"
|
"lint:fix": "prettier . --write --config=.prettierrc.js --ignore-path=.prettierignore --ignore-path=.gitignore"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 424 KiB After Width: | Height: | Size: 436 KiB |
+13
@@ -0,0 +1,13 @@
|
|||||||
|
defmodule Pinchflat.Repo.Migrations.AddMarkedForDeletionAtToSources do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:sources) do
|
||||||
|
add :marked_for_deletion_at, :utc_datetime
|
||||||
|
end
|
||||||
|
|
||||||
|
alter table(:media_profiles) do
|
||||||
|
add :marked_for_deletion_at, :utc_datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
defmodule Pinchflat.Repo.Migrations.AddUseCookiesToSources do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:sources) do
|
||||||
|
add :use_cookies, :boolean, default: true, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
defmodule Pinchflat.Repo.Migrations.AddDurationLimitsToSources do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:sources) do
|
||||||
|
add :min_duration_seconds, :integer
|
||||||
|
add :max_duration_seconds, :integer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -130,9 +130,6 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
|
|||||||
_download_prevented =
|
_download_prevented =
|
||||||
media_item_fixture(source_id: source.id, media_filepath: "some/filepath.mp4", prevent_download: true)
|
media_item_fixture(source_id: source.id, media_filepath: "some/filepath.mp4", prevent_download: true)
|
||||||
|
|
||||||
_culled =
|
|
||||||
media_item_fixture(source_id: source.id, media_filepath: "some/filepath.mp4", culled_at: now())
|
|
||||||
|
|
||||||
assert [] = DownloadingHelpers.kickoff_redownload_for_existing_media(source)
|
assert [] = DownloadingHelpers.kickoff_redownload_for_existing_media(source)
|
||||||
|
|
||||||
refute_enqueued(worker: MediaDownloadWorker)
|
refute_enqueued(worker: MediaDownloadWorker)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
alias Pinchflat.Downloading.MediaDownloadWorker
|
alias Pinchflat.Downloading.MediaDownloadWorker
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
|
stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
|
||||||
|
|
||||||
media_item =
|
media_item =
|
||||||
@@ -53,10 +53,12 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
|
|
||||||
describe "perform/1" do
|
describe "perform/1" do
|
||||||
test "it saves attributes to the media_item", %{media_item: media_item} do
|
test "it saves attributes to the media_item", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
assert media_item.media_filepath == nil
|
assert media_item.media_filepath == nil
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
media_item = Repo.reload(media_item)
|
media_item = Repo.reload(media_item)
|
||||||
@@ -65,10 +67,12 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "it saves the metadata to the media_item", %{media_item: media_item} do
|
test "it saves the metadata to the media_item", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
assert media_item.metadata == nil
|
assert media_item.metadata == nil
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
assert Repo.reload(media_item).metadata != nil
|
assert Repo.reload(media_item).metadata != nil
|
||||||
@@ -138,13 +142,15 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "it saves the file's size to the database", %{media_item: media_item} do
|
test "it saves the file's size to the database", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl ->
|
||||||
metadata = render_parsed_metadata(:media_metadata)
|
metadata = render_parsed_metadata(:media_metadata)
|
||||||
FilesystemUtils.write_p!(metadata["filepath"], "test")
|
FilesystemUtils.write_p!(metadata["filepath"], "test")
|
||||||
|
|
||||||
{:ok, Phoenix.json_library().encode!(metadata)}
|
{:ok, Phoenix.json_library().encode!(metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
media_item = Repo.reload(media_item)
|
media_item = Repo.reload(media_item)
|
||||||
|
|
||||||
@@ -152,7 +158,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "does not set redownloaded_at by default", %{media_item: media_item} do
|
test "does not set redownloaded_at by default", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -162,32 +168,20 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
assert media_item.media_redownloaded_at == nil
|
assert media_item.media_redownloaded_at == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
test "calls the user script runner", %{media_item: media_item} do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
|
||||||
end)
|
|
||||||
|
|
||||||
expect(UserScriptRunnerMock, :run, fn :media_downloaded, data ->
|
|
||||||
assert data.id == media_item.id
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end)
|
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not blow up if the record doesn't exist" do
|
test "does not blow up if the record doesn't exist" do
|
||||||
assert :ok = perform_job(MediaDownloadWorker, %{id: 0})
|
assert :ok = perform_job(MediaDownloadWorker, %{id: 0})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "sets the no_force_overwrites runner option", %{media_item: media_item} do
|
test "sets the no_force_overwrites runner option", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, opts, _ot, _addl ->
|
||||||
assert :no_force_overwrites in opts
|
assert :no_force_overwrites in opts
|
||||||
refute :force_overwrites in opts
|
refute :force_overwrites in opts
|
||||||
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -203,23 +197,27 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "sets force_overwrites runner option", %{media_item: media_item} do
|
test "sets force_overwrites runner option", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, opts, _ot, _addl ->
|
||||||
assert :force_overwrites in opts
|
assert :force_overwrites in opts
|
||||||
refute :no_force_overwrites in opts
|
refute :no_force_overwrites in opts
|
||||||
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "perform/1 when testing re-downloads" do
|
describe "perform/1 when testing re-downloads" do
|
||||||
test "sets redownloaded_at on the media_item", %{media_item: media_item} do
|
test "sets redownloaded_at on the media_item", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id, quality_upgrade?: true})
|
perform_job(MediaDownloadWorker, %{id: media_item.id, quality_upgrade?: true})
|
||||||
media_item = Repo.reload(media_item)
|
media_item = Repo.reload(media_item)
|
||||||
|
|
||||||
@@ -227,14 +225,71 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "sets force_overwrites runner option", %{media_item: media_item} do
|
test "sets force_overwrites runner option", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, opts, _ot, _addl ->
|
||||||
assert :force_overwrites in opts
|
assert :force_overwrites in opts
|
||||||
refute :no_force_overwrites in opts
|
refute :no_force_overwrites in opts
|
||||||
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "perform/1 when testing user script callbacks" do
|
||||||
|
setup do
|
||||||
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "calls the media_pre_download user script runner", %{media_item: media_item} do
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_pre_download, data ->
|
||||||
|
assert data.id == media_item.id
|
||||||
|
|
||||||
|
{:ok, "", 0}
|
||||||
|
end)
|
||||||
|
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_downloaded, _ -> {:ok, "", 0} end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not download the media if the pre-download script returns an error", %{media_item: media_item} do
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_pre_download, _ -> {:ok, "", 1} end)
|
||||||
|
|
||||||
|
assert :ok = perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
|
media_item = Repo.reload!(media_item)
|
||||||
|
|
||||||
|
refute media_item.media_filepath
|
||||||
|
assert media_item.prevent_download
|
||||||
|
end
|
||||||
|
|
||||||
|
test "downloads media if the pre-download script is not present", %{media_item: media_item} do
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_pre_download, _ -> {:ok, :no_executable} end)
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_downloaded, _ -> {:ok, :no_executable} end)
|
||||||
|
|
||||||
|
assert :ok = perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
|
media_item = Repo.reload!(media_item)
|
||||||
|
|
||||||
|
assert media_item.media_filepath
|
||||||
|
refute media_item.prevent_download
|
||||||
|
end
|
||||||
|
|
||||||
|
test "calls the media_downloaded user script runner", %{media_item: media_item} do
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_pre_download, _ -> {:ok, "", 0} end)
|
||||||
|
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_downloaded, data ->
|
||||||
|
assert data.id == media_item.id
|
||||||
|
|
||||||
|
{:ok, "", 0}
|
||||||
|
end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
import Pinchflat.SourcesFixtures
|
import Pinchflat.SourcesFixtures
|
||||||
import Pinchflat.ProfilesFixtures
|
import Pinchflat.ProfilesFixtures
|
||||||
|
|
||||||
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Downloading.MediaDownloader
|
alias Pinchflat.Downloading.MediaDownloader
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
@@ -15,27 +16,29 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
)
|
)
|
||||||
|
|
||||||
stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
|
stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_args -> {:ok, ""} end)
|
||||||
|
|
||||||
{:ok, %{media_item: media_item}}
|
{:ok, %{media_item: media_item}}
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "download_for_media_item/3" do
|
describe "download_for_media_item/3" do
|
||||||
test "it calls the backend runner", %{media_item: media_item} do
|
test "calls the backend runner", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn url, _opts, ot, addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn url, _opts, ot, addl ->
|
||||||
assert url == media_item.original_url
|
assert url == media_item.original_url
|
||||||
assert ot == "after_move:%()j"
|
assert ot == "after_move:%()j"
|
||||||
assert [{:output_filepath, filepath}] = addl
|
assert [{:output_filepath, filepath} | _] = addl
|
||||||
assert is_binary(filepath)
|
assert is_binary(filepath)
|
||||||
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl_args -> {:ok, ""} end)
|
||||||
|
|
||||||
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it saves the metadata filepath to the database", %{media_item: media_item} do
|
test "saves the metadata filepath to the database", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -66,19 +69,51 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
|
|
||||||
describe "download_for_media_item/3 when testing override options" do
|
describe "download_for_media_item/3 when testing override options" do
|
||||||
test "includes override opts if specified", %{media_item: media_item} do
|
test "includes override opts if specified", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, opts, _ot, _addl ->
|
||||||
refute :force_overwrites in opts
|
refute :force_overwrites in opts
|
||||||
assert :no_force_overwrites in opts
|
assert :no_force_overwrites in opts
|
||||||
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
override_opts = [overwrite_behaviour: :no_force_overwrites]
|
override_opts = [overwrite_behaviour: :no_force_overwrites]
|
||||||
|
|
||||||
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item, override_opts)
|
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item, override_opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "download_for_media_item/3 when testing cookie usage" do
|
||||||
|
test "sets use_cookies if the source uses cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, addl ->
|
||||||
|
assert {:use_cookies, true} in addl
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl_args -> {:ok, ""} end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: true})
|
||||||
|
media_item = media_item_fixture(%{source_id: source.id})
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not set use_cookies if the source does not use cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, addl ->
|
||||||
|
assert {:use_cookies, false} in addl
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl_args -> {:ok, ""} end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: false})
|
||||||
|
media_item = media_item_fixture(%{source_id: source.id})
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "download_for_media_item/3 when testing retries" do
|
describe "download_for_media_item/3 when testing retries" do
|
||||||
test "returns a recovered tuple on recoverable errors", %{media_item: media_item} do
|
test "returns a recovered tuple on recoverable errors", %{media_item: media_item} do
|
||||||
message = "Unable to communicate with SponsorBlock"
|
message = "Unable to communicate with SponsorBlock"
|
||||||
@@ -93,13 +128,15 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
test "attempts to update the media item on recoverable errors", %{media_item: media_item} do
|
test "attempts to update the media item on recoverable errors", %{media_item: media_item} do
|
||||||
message = "Unable to communicate with SponsorBlock"
|
message = "Unable to communicate with SponsorBlock"
|
||||||
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, addl ->
|
||||||
[{:output_filepath, filepath}] = addl
|
[{:output_filepath, filepath} | _] = addl
|
||||||
File.write(filepath, render_metadata(:media_metadata))
|
File.write(filepath, render_metadata(:media_metadata))
|
||||||
|
|
||||||
{:error, message, 1}
|
{:error, message, 1}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
expect(YtDlpRunnerMock, :run, 1, fn _url, _opts, _ot, _addl_args -> {:ok, ""} end)
|
||||||
|
|
||||||
assert {:recovered, ^message} = MediaDownloader.download_for_media_item(media_item)
|
assert {:recovered, ^message} = MediaDownloader.download_for_media_item(media_item)
|
||||||
media_item = Repo.reload(media_item)
|
media_item = Repo.reload(media_item)
|
||||||
|
|
||||||
@@ -117,42 +154,48 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it sets the media_downloaded_at", %{media_item: media_item} do
|
test "sets the media_downloaded_at", %{media_item: media_item} do
|
||||||
assert media_item.media_downloaded_at == nil
|
assert media_item.media_downloaded_at == nil
|
||||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
assert DateTime.diff(DateTime.utc_now(), updated_media_item.media_downloaded_at) < 2
|
assert DateTime.diff(DateTime.utc_now(), updated_media_item.media_downloaded_at) < 2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the title", %{media_item: media_item} do
|
test "sets the culled_at to nil", %{media_item: media_item} do
|
||||||
|
Media.update_media_item(media_item, %{culled_at: DateTime.utc_now()})
|
||||||
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
|
assert updated_media_item.culled_at == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "extracts the title", %{media_item: media_item} do
|
||||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
assert updated_media_item.title == "Pinchflat Example Video"
|
assert updated_media_item.title == "Pinchflat Example Video"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the description", %{media_item: media_item} do
|
test "extracts the description", %{media_item: media_item} do
|
||||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
assert is_binary(updated_media_item.description)
|
assert is_binary(updated_media_item.description)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the media_filepath", %{media_item: media_item} do
|
test "extracts the media_filepath", %{media_item: media_item} do
|
||||||
assert media_item.media_filepath == nil
|
assert media_item.media_filepath == nil
|
||||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
assert String.ends_with?(updated_media_item.media_filepath, ".mkv")
|
assert String.ends_with?(updated_media_item.media_filepath, ".mkv")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the subtitle_filepaths", %{media_item: media_item} do
|
test "extracts the subtitle_filepaths", %{media_item: media_item} do
|
||||||
assert media_item.subtitle_filepaths == []
|
assert media_item.subtitle_filepaths == []
|
||||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
assert [["de", _], ["en", _] | _rest] = updated_media_item.subtitle_filepaths
|
assert [["de", _], ["en", _] | _rest] = updated_media_item.subtitle_filepaths
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the duration_seconds", %{media_item: media_item} do
|
test "extracts the duration_seconds", %{media_item: media_item} do
|
||||||
assert media_item.duration_seconds == nil
|
assert media_item.duration_seconds == nil
|
||||||
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
assert is_integer(updated_media_item.duration_seconds)
|
assert is_integer(updated_media_item.duration_seconds)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the thumbnail_filepath", %{media_item: media_item} do
|
test "extracts the thumbnail_filepath", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
metadata = render_parsed_metadata(:media_metadata)
|
metadata = render_parsed_metadata(:media_metadata)
|
||||||
|
|
||||||
thumbnail_filepath =
|
thumbnail_filepath =
|
||||||
@@ -175,8 +218,8 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
File.rm(updated_media_item.thumbnail_filepath)
|
File.rm(updated_media_item.thumbnail_filepath)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it extracts the metadata_filepath", %{media_item: media_item} do
|
test "extracts the metadata_filepath", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
metadata = render_parsed_metadata(:media_metadata)
|
metadata = render_parsed_metadata(:media_metadata)
|
||||||
|
|
||||||
infojson_filepath = metadata["infojson_filename"]
|
infojson_filepath = metadata["infojson_filename"]
|
||||||
@@ -195,14 +238,14 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
|
|
||||||
describe "download_for_media_item/3 when testing NFO generation" do
|
describe "download_for_media_item/3 when testing NFO generation" do
|
||||||
setup do
|
setup do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it generates an NFO file if the source is set to download NFOs" do
|
test "generates an NFO file if the source is set to download NFOs" do
|
||||||
profile = media_profile_fixture(%{download_nfo: true})
|
profile = media_profile_fixture(%{download_nfo: true})
|
||||||
source = source_fixture(%{media_profile_id: profile.id})
|
source = source_fixture(%{media_profile_id: profile.id})
|
||||||
media_item = media_item_fixture(%{source_id: source.id})
|
media_item = media_item_fixture(%{source_id: source.id})
|
||||||
@@ -215,7 +258,7 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
|||||||
File.rm!(updated_media_item.nfo_filepath)
|
File.rm!(updated_media_item.nfo_filepath)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it does not generate an NFO file if the source is set to not download NFOs" do
|
test "does not generate an NFO file if the source is set to not download NFOs" do
|
||||||
profile = media_profile_fixture(%{download_nfo: false})
|
profile = media_profile_fixture(%{download_nfo: false})
|
||||||
source = source_fixture(%{media_profile_id: profile.id})
|
source = source_fixture(%{media_profile_id: profile.id})
|
||||||
media_item = media_item_fixture(%{source_id: source.id})
|
media_item = media_item_fixture(%{source_id: source.id})
|
||||||
|
|||||||
@@ -7,15 +7,36 @@ defmodule Pinchflat.Downloading.MediaRetentionWorkerTest do
|
|||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Downloading.MediaRetentionWorker
|
alias Pinchflat.Downloading.MediaRetentionWorker
|
||||||
|
|
||||||
describe "perform/1" do
|
|
||||||
setup do
|
setup do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "perform/1" do
|
||||||
|
test "sets deleted media to not re-download" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_retention_date()
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
refute Repo.reload!(new_media_item).prevent_download
|
||||||
|
assert Repo.reload!(old_media_item).prevent_download
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets culled_at timestamp on deleted media" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_retention_date()
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
refute Repo.reload!(new_media_item).culled_at
|
||||||
|
assert Repo.reload!(old_media_item).culled_at
|
||||||
|
assert DateTime.diff(now(), Repo.reload!(old_media_item).culled_at) < 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "perform/1 when testing retention_period-based culling" do
|
||||||
test "deletes media files that are past their retention date" do
|
test "deletes media files that are past their retention date" do
|
||||||
{_source, old_media_item, new_media_item} = prepare_records()
|
{_source, old_media_item, new_media_item} = prepare_records_for_retention_date()
|
||||||
|
|
||||||
perform_job(MediaRetentionWorker, %{})
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
@@ -25,27 +46,33 @@ defmodule Pinchflat.Downloading.MediaRetentionWorkerTest do
|
|||||||
refute Repo.reload!(old_media_item).media_filepath
|
refute Repo.reload!(old_media_item).media_filepath
|
||||||
end
|
end
|
||||||
|
|
||||||
test "sets deleted media to not re-download" do
|
test "sets culled_at and prevent_download" do
|
||||||
{_source, old_media_item, new_media_item} = prepare_records()
|
{_source, old_media_item, new_media_item} = prepare_records_for_retention_date()
|
||||||
|
|
||||||
perform_job(MediaRetentionWorker, %{})
|
|
||||||
|
|
||||||
refute Repo.reload!(new_media_item).prevent_download
|
|
||||||
assert Repo.reload!(old_media_item).prevent_download
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sets culled_at timestamp on deleted media" do
|
|
||||||
{_source, old_media_item, new_media_item} = prepare_records()
|
|
||||||
|
|
||||||
perform_job(MediaRetentionWorker, %{})
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
refute Repo.reload!(new_media_item).culled_at
|
refute Repo.reload!(new_media_item).culled_at
|
||||||
assert Repo.reload!(old_media_item).culled_at
|
assert Repo.reload!(old_media_item).culled_at
|
||||||
assert DateTime.diff(now(), Repo.reload!(old_media_item).culled_at) < 1
|
refute Repo.reload!(new_media_item).prevent_download
|
||||||
|
assert Repo.reload!(old_media_item).prevent_download
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't cull if the source doesn't have a retention period" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_retention_date(nil)
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
assert File.exists?(new_media_item.media_filepath)
|
||||||
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
|
assert Repo.reload!(new_media_item).media_filepath
|
||||||
|
assert Repo.reload!(old_media_item).media_filepath
|
||||||
|
|
||||||
|
refute Repo.reload!(new_media_item).culled_at
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
end
|
end
|
||||||
|
|
||||||
test "doesn't cull media items that have prevent_culling set" do
|
test "doesn't cull media items that have prevent_culling set" do
|
||||||
{_source, old_media_item, _new_media_item} = prepare_records()
|
{_source, old_media_item, _new_media_item} = prepare_records_for_retention_date()
|
||||||
|
|
||||||
Media.update_media_item(old_media_item, %{prevent_culling: true})
|
Media.update_media_item(old_media_item, %{prevent_culling: true})
|
||||||
|
|
||||||
@@ -53,11 +80,99 @@ defmodule Pinchflat.Downloading.MediaRetentionWorkerTest do
|
|||||||
|
|
||||||
assert File.exists?(old_media_item.media_filepath)
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
assert Repo.reload!(old_media_item).media_filepath
|
assert Repo.reload!(old_media_item).media_filepath
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't cull if the media item has no media_filepath" do
|
||||||
|
{_source, old_media_item, _new_media_item} = prepare_records_for_retention_date()
|
||||||
|
|
||||||
|
Media.update_media_item(old_media_item, %{media_filepath: nil})
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp prepare_records do
|
describe "perform/1 when testing source cutoff-based culling" do
|
||||||
source = source_fixture(%{retention_period_days: 2})
|
test "culls media from before the cutoff date" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_source_cutoff_date()
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
assert File.exists?(new_media_item.media_filepath)
|
||||||
|
refute File.exists?(old_media_item.media_filepath)
|
||||||
|
assert Repo.reload!(new_media_item).media_filepath
|
||||||
|
refute Repo.reload!(old_media_item).media_filepath
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets culled_at but not prevent_download" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_source_cutoff_date()
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
refute Repo.reload!(new_media_item).culled_at
|
||||||
|
assert Repo.reload!(old_media_item).culled_at
|
||||||
|
refute Repo.reload!(new_media_item).prevent_download
|
||||||
|
refute Repo.reload!(old_media_item).prevent_download
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't cull media if the source doesn't have a cutoff date" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_source_cutoff_date(nil)
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
assert File.exists?(new_media_item.media_filepath)
|
||||||
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
|
assert Repo.reload!(new_media_item).media_filepath
|
||||||
|
assert Repo.reload!(old_media_item).media_filepath
|
||||||
|
|
||||||
|
refute Repo.reload!(new_media_item).culled_at
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't cull media from on or after the cutoff date" do
|
||||||
|
{_source, old_media_item, new_media_item} = prepare_records_for_source_cutoff_date(2)
|
||||||
|
|
||||||
|
Media.update_media_item(old_media_item, %{uploaded_at: now_minus(2, :days)})
|
||||||
|
Media.update_media_item(new_media_item, %{uploaded_at: now_minus(1, :day)})
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
assert File.exists?(new_media_item.media_filepath)
|
||||||
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
|
assert Repo.reload!(new_media_item).media_filepath
|
||||||
|
assert Repo.reload!(old_media_item).media_filepath
|
||||||
|
|
||||||
|
refute Repo.reload!(new_media_item).culled_at
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't cull media items that have prevent_culling set" do
|
||||||
|
{_source, old_media_item, _new_media_item} = prepare_records_for_source_cutoff_date()
|
||||||
|
|
||||||
|
Media.update_media_item(old_media_item, %{prevent_culling: true})
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
assert File.exists?(old_media_item.media_filepath)
|
||||||
|
assert Repo.reload!(old_media_item).media_filepath
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't cull if the media item has no media_filepath" do
|
||||||
|
{_source, old_media_item, _new_media_item} = prepare_records_for_source_cutoff_date()
|
||||||
|
|
||||||
|
Media.update_media_item(old_media_item, %{media_filepath: nil})
|
||||||
|
|
||||||
|
perform_job(MediaRetentionWorker, %{})
|
||||||
|
|
||||||
|
refute Repo.reload!(old_media_item).culled_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_records_for_retention_date(retention_period_days \\ 2) do
|
||||||
|
source = source_fixture(%{retention_period_days: retention_period_days})
|
||||||
|
|
||||||
old_media_item =
|
old_media_item =
|
||||||
media_item_with_attachments(%{
|
media_item_with_attachments(%{
|
||||||
@@ -73,4 +188,23 @@ defmodule Pinchflat.Downloading.MediaRetentionWorkerTest do
|
|||||||
|
|
||||||
{source, old_media_item, new_media_item}
|
{source, old_media_item, new_media_item}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp prepare_records_for_source_cutoff_date(download_cutoff_date_days_ago \\ 2) do
|
||||||
|
cutoff_date = if download_cutoff_date_days_ago, do: now_minus(download_cutoff_date_days_ago, :days), else: nil
|
||||||
|
source = source_fixture(%{download_cutoff_date: cutoff_date})
|
||||||
|
|
||||||
|
old_media_item =
|
||||||
|
media_item_with_attachments(%{
|
||||||
|
source_id: source.id,
|
||||||
|
uploaded_at: now_minus(3, :days)
|
||||||
|
})
|
||||||
|
|
||||||
|
new_media_item =
|
||||||
|
media_item_with_attachments(%{
|
||||||
|
source_id: source.id,
|
||||||
|
uploaded_at: now_minus(1, :day)
|
||||||
|
})
|
||||||
|
|
||||||
|
{source, old_media_item, new_media_item}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
|
|||||||
alias Pinchflat.FastIndexing.FastIndexingHelpers
|
alias Pinchflat.FastIndexing.FastIndexingHelpers
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, media_attributes_return_fixture()}
|
{:ok, media_attributes_return_fixture()}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -61,13 +61,41 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
|
|||||||
assert [_] = Tasks.list_tasks_for(media_item, "MediaDownloadWorker")
|
assert [_] = Tasks.list_tasks_for(media_item, "MediaDownloadWorker")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sets use_cookies if the source uses cookies" do
|
||||||
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
|
|
||||||
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
|
assert {:use_cookies, true} in addl
|
||||||
|
|
||||||
|
{:ok, media_attributes_return_fixture()}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: true})
|
||||||
|
|
||||||
|
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not set use_cookies if the source does not use cookies" do
|
||||||
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
|
|
||||||
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
|
assert {:use_cookies, false} in addl
|
||||||
|
|
||||||
|
{:ok, media_attributes_return_fixture()}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: false})
|
||||||
|
|
||||||
|
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
|
||||||
|
end
|
||||||
|
|
||||||
test "does not enqueue a download job if the media item does not match the format rules" do
|
test "does not enqueue a download job if the media item does not match the format rules" do
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
|
|
||||||
profile = media_profile_fixture(%{shorts_behaviour: :exclude})
|
profile = media_profile_fixture(%{shorts_behaviour: :exclude})
|
||||||
source = source_fixture(%{media_profile_id: profile.id})
|
source = source_fixture(%{media_profile_id: profile.id})
|
||||||
|
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
output =
|
output =
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
id: "video2",
|
id: "video2",
|
||||||
@@ -91,7 +119,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
|
|||||||
test "does not blow up if a media item cannot be created", %{source: source} do
|
test "does not blow up if a media item cannot be created", %{source: source} do
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
|
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -101,7 +129,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
|
|||||||
test "does not blow up if a media item causes a yt-dlp error", %{source: source} do
|
test "does not blow up if a media item causes a yt-dlp error", %{source: source} do
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
|
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
{:error, "message", 1}
|
{:error, "message", 1}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingWorkerTest do
|
|||||||
source = source_fixture(fast_index: true)
|
source = source_fixture(fast_index: true)
|
||||||
|
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, render_metadata(:media_metadata)} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, render_metadata(:media_metadata)} end)
|
||||||
|
|
||||||
expect(AppriseRunnerMock, :run, fn servers, opts ->
|
expect(AppriseRunnerMock, :run, fn servers, opts ->
|
||||||
assert "server_1" = servers
|
assert "server_1" = servers
|
||||||
@@ -110,7 +110,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingWorkerTest do
|
|||||||
source = source_fixture(fast_index: true, download_media: false)
|
source = source_fixture(fast_index: true, download_media: false)
|
||||||
|
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, render_metadata(:media_metadata)} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, render_metadata(:media_metadata)} end)
|
||||||
expect(AppriseRunnerMock, :run, 0, fn _servers, _opts -> {:ok, ""} end)
|
expect(AppriseRunnerMock, :run, 0, fn _servers, _opts -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(FastIndexingWorker, %{id: source.id})
|
perform_job(FastIndexingWorker, %{id: source.id})
|
||||||
@@ -120,7 +120,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingWorkerTest do
|
|||||||
source = source_fixture(fast_index: true, title_filter_regex: "foobar")
|
source = source_fixture(fast_index: true, title_filter_regex: "foobar")
|
||||||
|
|
||||||
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, render_metadata(:media_metadata)} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, render_metadata(:media_metadata)} end)
|
||||||
expect(AppriseRunnerMock, :run, 0, fn _servers, _opts -> {:ok, ""} end)
|
expect(AppriseRunnerMock, :run, 0, fn _servers, _opts -> {:ok, ""} end)
|
||||||
|
|
||||||
perform_job(FastIndexingWorker, %{id: source.id})
|
perform_job(FastIndexingWorker, %{id: source.id})
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunnerTest do
|
|||||||
File.write(filepath(), "#!/bin/bash\ntouch #{filename}\n")
|
File.write(filepath(), "#!/bin/bash\ntouch #{filename}\n")
|
||||||
|
|
||||||
refute File.exists?(filename)
|
refute File.exists?(filename)
|
||||||
assert :ok = Runner.run(:media_downloaded, %{})
|
assert {:ok, _, _} = Runner.run(:media_downloaded, %{})
|
||||||
assert File.exists?(filename)
|
assert File.exists?(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunnerTest do
|
|||||||
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||||
File.write(filepath(), "#!/bin/bash\necho $1 > #{tmp_dir}/event_name\n")
|
File.write(filepath(), "#!/bin/bash\necho $1 > #{tmp_dir}/event_name\n")
|
||||||
|
|
||||||
assert :ok = Runner.run(:media_downloaded, %{})
|
assert {:ok, _, _} = Runner.run(:media_downloaded, %{})
|
||||||
assert File.read!("#{tmp_dir}/event_name") == "media_downloaded\n"
|
assert File.read!("#{tmp_dir}/event_name") == "media_downloaded\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -35,26 +35,32 @@ defmodule Pinchflat.Lifecycle.UserScripts.CommandRunnerTest do
|
|||||||
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||||
File.write(filepath(), "#!/bin/bash\necho $2 > #{tmp_dir}/encoded_data\n")
|
File.write(filepath(), "#!/bin/bash\necho $2 > #{tmp_dir}/encoded_data\n")
|
||||||
|
|
||||||
assert :ok = Runner.run(:media_downloaded, %{foo: "bar"})
|
assert {:ok, _, _} = Runner.run(:media_downloaded, %{foo: "bar"})
|
||||||
assert File.read!("#{tmp_dir}/encoded_data") == "{\"foo\":\"bar\"}\n"
|
assert File.read!("#{tmp_dir}/encoded_data") == "{\"foo\":\"bar\"}\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does nothing if the lifecycle file is not present" do
|
test "does nothing if the lifecycle file is not present" do
|
||||||
:ok = File.rm(filepath())
|
:ok = File.rm(filepath())
|
||||||
|
|
||||||
assert :ok = Runner.run(:media_downloaded, %{})
|
assert {:ok, :no_executable} = Runner.run(:media_downloaded, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does nothing if the lifecycle file is empty" do
|
test "does nothing if the lifecycle file is empty" do
|
||||||
File.write(filepath(), "")
|
File.write(filepath(), "")
|
||||||
|
|
||||||
assert :ok = Runner.run(:media_downloaded, %{})
|
assert {:ok, :no_executable} = Runner.run(:media_downloaded, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns :ok if the command exits with a non-zero status" do
|
test "returns :ok if the command exits with a non-zero status" do
|
||||||
File.write(filepath(), "#!/bin/bash\nexit 1\n")
|
File.write(filepath(), "#!/bin/bash\nexit 1\n")
|
||||||
|
|
||||||
assert :ok = Runner.run(:media_downloaded, %{})
|
assert {:ok, _, 1} = Runner.run(:media_downloaded, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns the output of the command" do
|
||||||
|
File.write(filepath(), "#!/bin/bash\necho 'hello'\n")
|
||||||
|
|
||||||
|
assert {:ok, "hello\n", 0} = Runner.run(:media_downloaded, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "gets upset if you pass an invalid event type" do
|
test "gets upset if you pass an invalid event type" do
|
||||||
|
|||||||
+91
-113
@@ -41,99 +41,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "list_cullable_media_items/0" do
|
describe "list_upgradeable_media_items/0" do
|
||||||
test "returns media items where the source has a retention period" do
|
|
||||||
source_one = source_fixture(%{retention_period_days: 2})
|
|
||||||
source_two = source_fixture(%{retention_period_days: 0})
|
|
||||||
source_three = source_fixture(%{retention_period_days: nil})
|
|
||||||
|
|
||||||
_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source_two.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source_three.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
expected_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source_one.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
assert Media.list_cullable_media_items() == [expected_media_item]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns media_items with a media_filepath" do
|
|
||||||
source = source_fixture(%{retention_period_days: 2})
|
|
||||||
|
|
||||||
_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source.id,
|
|
||||||
media_filepath: nil,
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
expected_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
assert Media.list_cullable_media_items() == [expected_media_item]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns items that have passed their retention period" do
|
|
||||||
source = source_fixture(%{retention_period_days: 2})
|
|
||||||
|
|
||||||
_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(2, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
expected_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
assert Media.list_cullable_media_items() == [expected_media_item]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "doesn't return items that are set to prevent culling" do
|
|
||||||
source = source_fixture(%{retention_period_days: 2})
|
|
||||||
|
|
||||||
_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days),
|
|
||||||
prevent_culling: true
|
|
||||||
})
|
|
||||||
|
|
||||||
expected_media_item =
|
|
||||||
media_item_fixture(%{
|
|
||||||
source_id: source.id,
|
|
||||||
media_filepath: "/video/#{Faker.File.file_name(:video)}",
|
|
||||||
media_downloaded_at: now_minus(3, :days)
|
|
||||||
})
|
|
||||||
|
|
||||||
assert Media.list_cullable_media_items() == [expected_media_item]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "list_redownloadable_media_items/0" do
|
|
||||||
setup do
|
setup do
|
||||||
media_profile = media_profile_fixture(%{redownload_delay_days: 4})
|
media_profile = media_profile_fixture(%{redownload_delay_days: 4})
|
||||||
source = source_fixture(%{media_profile_id: media_profile.id, inserted_at: now_minus(10, :days)})
|
source = source_fixture(%{media_profile_id: media_profile.id, inserted_at: now_minus(10, :days)})
|
||||||
@@ -149,7 +57,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
media_downloaded_at: now_minus(5, :days)
|
media_downloaded_at: now_minus(5, :days)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == [media_item]
|
assert Media.list_upgradeable_media_items() == [media_item]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns media items that were downloaded in past but still meet redownload delay", %{source: source} do
|
test "returns media items that were downloaded in past but still meet redownload delay", %{source: source} do
|
||||||
@@ -160,7 +68,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
media_downloaded_at: now_minus(19, :days)
|
media_downloaded_at: now_minus(19, :days)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == [media_item]
|
assert Media.list_upgradeable_media_items() == [media_item]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items without a media_downloaded_at", %{source: source} do
|
test "does not return media items without a media_downloaded_at", %{source: source} do
|
||||||
@@ -171,7 +79,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
media_downloaded_at: nil
|
media_downloaded_at: nil
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items that are set to prevent download", %{source: source} do
|
test "does not return media items that are set to prevent download", %{source: source} do
|
||||||
@@ -183,7 +91,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
prevent_download: true
|
prevent_download: true
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items that have been culled", %{source: source} do
|
test "does not return media items that have been culled", %{source: source} do
|
||||||
@@ -195,7 +103,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
culled_at: now()
|
culled_at: now()
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items before the download delay", %{source: source} do
|
test "does not return media items before the download delay", %{source: source} do
|
||||||
@@ -206,7 +114,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
media_downloaded_at: now_minus(3, :days)
|
media_downloaded_at: now_minus(3, :days)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items that have already been redownloaded", %{source: source} do
|
test "does not return media items that have already been redownloaded", %{source: source} do
|
||||||
@@ -218,7 +126,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
media_redownloaded_at: now()
|
media_redownloaded_at: now()
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items that were first downloaded well after the uploaded_at", %{source: source} do
|
test "does not return media items that were first downloaded well after the uploaded_at", %{source: source} do
|
||||||
@@ -229,7 +137,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
uploaded_at: now_minus(20, :days)
|
uploaded_at: now_minus(20, :days)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items that were recently uploaded", %{source: source} do
|
test "does not return media items that were recently uploaded", %{source: source} do
|
||||||
@@ -240,7 +148,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
uploaded_at: now_minus(2, :days)
|
uploaded_at: now_minus(2, :days)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return media items without a redownload delay" do
|
test "does not return media items without a redownload delay" do
|
||||||
@@ -254,7 +162,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
media_downloaded_at: now_minus(5, :days)
|
media_downloaded_at: now_minus(5, :days)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Media.list_redownloadable_media_items() == []
|
assert Media.list_upgradeable_media_items() == []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -447,6 +355,48 @@ defmodule Pinchflat.MediaTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "list_pending_media_items_for/1 when min and max durations" do
|
||||||
|
test "returns media items that meet the min and max duration" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: 10, max_duration_seconds: 20})
|
||||||
|
|
||||||
|
_short_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 5})
|
||||||
|
normal_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 15})
|
||||||
|
_long_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 25})
|
||||||
|
|
||||||
|
assert Media.list_pending_media_items_for(source) == [normal_media_item]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not apply a min duration if none is specified" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: nil, max_duration_seconds: 20})
|
||||||
|
|
||||||
|
short_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 5})
|
||||||
|
normal_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 15})
|
||||||
|
_long_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 25})
|
||||||
|
|
||||||
|
assert Media.list_pending_media_items_for(source) == [short_media_item, normal_media_item]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not apply a max duration if none is specified" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: 10, max_duration_seconds: nil})
|
||||||
|
|
||||||
|
_short_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 5})
|
||||||
|
normal_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 15})
|
||||||
|
long_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 25})
|
||||||
|
|
||||||
|
assert Media.list_pending_media_items_for(source) == [normal_media_item, long_media_item]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not apply a min or max duration if none are specified" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: nil, max_duration_seconds: nil})
|
||||||
|
|
||||||
|
short_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 5})
|
||||||
|
normal_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 15})
|
||||||
|
long_media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 25})
|
||||||
|
|
||||||
|
assert Media.list_pending_media_items_for(source) == [short_media_item, normal_media_item, long_media_item]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "list_pending_media_items_for/1 when testing download prevention" do
|
describe "list_pending_media_items_for/1 when testing download prevention" do
|
||||||
test "returns only media items that are not prevented from downloading" do
|
test "returns only media items that are not prevented from downloading" do
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
@@ -526,6 +476,34 @@ defmodule Pinchflat.MediaTest do
|
|||||||
assert Media.pending_download?(media_item)
|
assert Media.pending_download?(media_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns true if the duration is between the min and max" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: 10, max_duration_seconds: 20})
|
||||||
|
media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 15})
|
||||||
|
|
||||||
|
assert Media.pending_download?(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns false if the duration is below the min" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: 10, max_duration_seconds: 20})
|
||||||
|
media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 5})
|
||||||
|
|
||||||
|
refute Media.pending_download?(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns false if the duration is above the max" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: 10, max_duration_seconds: 20})
|
||||||
|
media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 25})
|
||||||
|
|
||||||
|
refute Media.pending_download?(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns true if there is no min or max duration" do
|
||||||
|
source = source_fixture(%{min_duration_seconds: nil, max_duration_seconds: nil})
|
||||||
|
media_item = media_item_fixture(%{source_id: source.id, media_filepath: nil, duration_seconds: 15})
|
||||||
|
|
||||||
|
assert Media.pending_download?(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns true if the media item is not prevented from downloading" do
|
test "returns true if the media item is not prevented from downloading" do
|
||||||
media_item = media_item_fixture(%{media_filepath: nil, prevent_download: false})
|
media_item = media_item_fixture(%{media_filepath: nil, prevent_download: false})
|
||||||
|
|
||||||
@@ -757,8 +735,8 @@ defmodule Pinchflat.MediaTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "does delete the media item's metadata files" do
|
test "does delete the media item's metadata files" do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
media_item = Repo.preload(media_item_with_attachments(), :metadata)
|
media_item = Repo.preload(media_item_with_attachments(), [:metadata, :source])
|
||||||
|
|
||||||
update_attrs = %{
|
update_attrs = %{
|
||||||
metadata: %{
|
metadata: %{
|
||||||
@@ -776,7 +754,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
|
|
||||||
describe "delete_media_item/2 when testing file deletion" do
|
describe "delete_media_item/2 when testing file deletion" do
|
||||||
setup do
|
setup do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
@@ -789,8 +767,8 @@ defmodule Pinchflat.MediaTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "deletes the media item's metadata files" do
|
test "deletes the media item's metadata files" do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
media_item = Repo.preload(media_item_with_attachments(), :metadata)
|
media_item = Repo.preload(media_item_with_attachments(), [:metadata, :source])
|
||||||
|
|
||||||
update_attrs = %{
|
update_attrs = %{
|
||||||
metadata: %{
|
metadata: %{
|
||||||
@@ -837,7 +815,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
expect(UserScriptRunnerMock, :run, fn :media_deleted, data ->
|
expect(UserScriptRunnerMock, :run, fn :media_deleted, data ->
|
||||||
assert data.id == media_item.id
|
assert data.id == media_item.id
|
||||||
|
|
||||||
:ok
|
{:ok, "", 0}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = Media.delete_media_item(media_item, delete_files: true)
|
assert {:ok, _} = Media.delete_media_item(media_item, delete_files: true)
|
||||||
@@ -846,7 +824,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
|
|
||||||
describe "delete_media_files/2" do
|
describe "delete_media_files/2" do
|
||||||
setup do
|
setup do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
@@ -875,8 +853,8 @@ defmodule Pinchflat.MediaTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "does not delete the media item's metadata files" do
|
test "does not delete the media item's metadata files" do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
media_item = Repo.preload(media_item_with_attachments(), :metadata)
|
media_item = Repo.preload(media_item_with_attachments(), [:metadata, :source])
|
||||||
|
|
||||||
update_attrs = %{
|
update_attrs = %{
|
||||||
metadata: %{
|
metadata: %{
|
||||||
@@ -909,7 +887,7 @@ defmodule Pinchflat.MediaTest do
|
|||||||
expect(UserScriptRunnerMock, :run, fn :media_deleted, data ->
|
expect(UserScriptRunnerMock, :run, fn :media_deleted, data ->
|
||||||
assert data.id == media_item.id
|
assert data.id == media_item.id
|
||||||
|
|
||||||
:ok
|
{:ok, "", 0}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = Media.delete_media_files(media_item)
|
assert {:ok, _} = Media.delete_media_files(media_item)
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||||||
use Pinchflat.DataCase
|
use Pinchflat.DataCase
|
||||||
|
|
||||||
import Pinchflat.MediaFixtures
|
import Pinchflat.MediaFixtures
|
||||||
|
import Pinchflat.SourcesFixtures
|
||||||
|
|
||||||
alias Pinchflat.Metadata.MetadataFileHelpers, as: Helpers
|
alias Pinchflat.Metadata.MetadataFileHelpers, as: Helpers
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
media_item = media_item_fixture()
|
media_item = Repo.preload(media_item_fixture(), :source)
|
||||||
|
|
||||||
{:ok, %{media_item: media_item}}
|
{:ok, %{media_item: media_item}}
|
||||||
end
|
end
|
||||||
@@ -61,7 +62,7 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||||||
|
|
||||||
describe "download_and_store_thumbnail_for/2" do
|
describe "download_and_store_thumbnail_for/2" do
|
||||||
test "returns the filepath", %{media_item: media_item} do
|
test "returns the filepath", %{media_item: media_item} do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, ""} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:ok, ""} end)
|
||||||
|
|
||||||
filepath = Helpers.download_and_store_thumbnail_for(media_item)
|
filepath = Helpers.download_and_store_thumbnail_for(media_item)
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "calls yt-dlp with the expected options", %{media_item: media_item} do
|
test "calls yt-dlp with the expected options", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn url, opts, ot ->
|
expect(YtDlpRunnerMock, :run, fn url, opts, ot, _addl ->
|
||||||
assert url == media_item.original_url
|
assert url == media_item.original_url
|
||||||
assert ot == "after_move:%()j"
|
assert ot == "after_move:%()j"
|
||||||
|
|
||||||
@@ -87,8 +88,32 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
|
|||||||
Helpers.download_and_store_thumbnail_for(media_item)
|
Helpers.download_and_store_thumbnail_for(media_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sets use_cookies if the source uses cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
|
assert {:use_cookies, true} in addl
|
||||||
|
{:ok, ""}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: true})
|
||||||
|
media_item = Repo.preload(media_item_fixture(%{source_id: source.id}), :source)
|
||||||
|
|
||||||
|
Helpers.download_and_store_thumbnail_for(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not set use_cookies if the source does not use cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
|
assert {:use_cookies, false} in addl
|
||||||
|
{:ok, ""}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: false})
|
||||||
|
media_item = Repo.preload(media_item_fixture(%{source_id: source.id}), :source)
|
||||||
|
|
||||||
|
Helpers.download_and_store_thumbnail_for(media_item)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns nil if yt-dlp fails", %{media_item: media_item} do
|
test "returns nil if yt-dlp fails", %{media_item: media_item} do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "error"} end)
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:error, "error"} end)
|
||||||
|
|
||||||
filepath = Helpers.download_and_store_thumbnail_for(media_item)
|
filepath = Helpers.download_and_store_thumbnail_for(media_item)
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
describe "perform/1" do
|
describe "perform/1" do
|
||||||
test "won't call itself in an infinite loop" do
|
test "won't call itself in an infinite loop" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
_url, _opts, ot, _addl when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
||||||
_url, _opts, ot when ot == @metadata_ot -> {:ok, "{}"}
|
_url, _opts, ot, _addl when ot == @metadata_ot -> {:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
@@ -51,8 +51,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
describe "perform/1 when testing attribute updates" do
|
describe "perform/1 when testing attribute updates" do
|
||||||
test "the source description is saved" do
|
test "the source description is saved" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
_url, _opts, ot, _addl when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
||||||
_url, _opts, ot when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
_url, _opts, ot, _addl when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
source = source_fixture(%{description: nil})
|
source = source_fixture(%{description: nil})
|
||||||
@@ -68,8 +68,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
describe "perform/1 when testing metadata storage" do
|
describe "perform/1 when testing metadata storage" do
|
||||||
test "sets metadata location for source" do
|
test "sets metadata location for source" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
_url, _opts, ot, _addl when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
||||||
_url, _opts, ot when ot == @metadata_ot -> {:ok, "{}"}
|
_url, _opts, ot, _addl when ot == @metadata_ot -> {:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
source = Repo.preload(source_fixture(), :metadata)
|
source = Repo.preload(source_fixture(), :metadata)
|
||||||
@@ -87,8 +87,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
file_contents = Phoenix.json_library().encode!(%{"title" => "test"})
|
file_contents = Phoenix.json_library().encode!(%{"title" => "test"})
|
||||||
|
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
_url, _opts, ot, _addl when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
||||||
_url, _opts, ot when ot == @metadata_ot -> {:ok, file_contents}
|
_url, _opts, ot, _addl when ot == @metadata_ot -> {:ok, file_contents}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||||
@@ -100,8 +100,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "sets metadata image location for source" do
|
test "sets metadata image location for source" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
_url, _opts, ot, _addl when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
||||||
_url, _opts, ot when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
_url, _opts, ot, _addl when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
@@ -118,8 +118,8 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "stores metadata images for source" do
|
test "stores metadata images for source" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
_url, _opts, ot, _addl when ot == @source_details_ot -> {:ok, source_details_return_fixture()}
|
||||||
_url, _opts, ot when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
_url, _opts, ot, _addl when ot == @metadata_ot -> {:ok, render_metadata(:channel_source_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
@@ -138,12 +138,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
describe "perform/1 when testing source image downloading" do
|
describe "perform/1 when testing source image downloading" do
|
||||||
test "downloads and stores source images" do
|
test "downloads and stores source images" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, opts, ot when ot == @metadata_ot ->
|
_url, opts, ot, _addl when ot == @metadata_ot ->
|
||||||
assert {:convert_thumbnails, "jpg"} in opts
|
assert {:convert_thumbnails, "jpg"} in opts
|
||||||
|
|
||||||
{:ok, render_metadata(:channel_source_metadata)}
|
{:ok, render_metadata(:channel_source_metadata)}
|
||||||
@@ -168,10 +168,10 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "calls one set of yt-dlp metadata opts for channels" do
|
test "calls one set of yt-dlp metadata opts for channels" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
{:ok, source_details_return_fixture()}
|
{:ok, source_details_return_fixture()}
|
||||||
|
|
||||||
_url, opts, ot when ot == @metadata_ot ->
|
_url, opts, ot, _addl when ot == @metadata_ot ->
|
||||||
assert {:playlist_items, 0} in opts
|
assert {:playlist_items, 0} in opts
|
||||||
assert :write_all_thumbnails in opts
|
assert :write_all_thumbnails in opts
|
||||||
|
|
||||||
@@ -186,10 +186,10 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "calls another set of yt-dlp metadata opts for playlists" do
|
test "calls another set of yt-dlp metadata opts for playlists" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
{:ok, source_details_return_fixture()}
|
{:ok, source_details_return_fixture()}
|
||||||
|
|
||||||
_url, opts, ot when ot == @metadata_ot ->
|
_url, opts, ot, _addl when ot == @metadata_ot ->
|
||||||
assert {:playlist_items, 1} in opts
|
assert {:playlist_items, 1} in opts
|
||||||
assert :write_thumbnail in opts
|
assert :write_thumbnail in opts
|
||||||
|
|
||||||
@@ -204,12 +204,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "does not store source images if the profile is not set to" do
|
test "does not store source images if the profile is not set to" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, render_metadata(:channel_source_metadata)}
|
{:ok, render_metadata(:channel_source_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -226,12 +226,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "does not store source images if the series directory cannot be determined" do
|
test "does not store source images if the series directory cannot be determined" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, render_metadata(:channel_source_metadata)}
|
{:ok, render_metadata(:channel_source_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -245,17 +245,49 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
refute source.poster_filepath
|
refute source.poster_filepath
|
||||||
refute source.banner_filepath
|
refute source.banner_filepath
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sets use_cookies if the source uses cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, 2, fn
|
||||||
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
|
{:ok, source_details_return_fixture()}
|
||||||
|
|
||||||
|
_url, _opts, ot, addl when ot == @metadata_ot ->
|
||||||
|
assert {:use_cookies, true} in addl
|
||||||
|
{:ok, render_metadata(:channel_source_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
profile = media_profile_fixture(%{download_source_images: true})
|
||||||
|
source = source_fixture(media_profile_id: profile.id, use_cookies: true)
|
||||||
|
|
||||||
|
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not set use_cookies if the source does not use cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, 2, fn
|
||||||
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
|
{:ok, source_details_return_fixture()}
|
||||||
|
|
||||||
|
_url, _opts, ot, addl when ot == @metadata_ot ->
|
||||||
|
assert {:use_cookies, false} in addl
|
||||||
|
{:ok, render_metadata(:channel_source_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
profile = media_profile_fixture(%{download_source_images: true})
|
||||||
|
source = source_fixture(media_profile_id: profile.id, use_cookies: false)
|
||||||
|
|
||||||
|
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "perform/1 when determining the series_directory" do
|
describe "perform/1 when determining the series_directory" do
|
||||||
test "sets the series directory based on the returned media filepath" do
|
test "sets the series directory based on the returned media filepath" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -268,12 +300,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "does not set the series directory if it cannot be determined" do
|
test "does not set the series directory if it cannot be determined" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -283,17 +315,45 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
refute source.series_directory
|
refute source.series_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sets use_cookies if the source is set to use cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, 2, fn
|
||||||
|
_url, _opts, ot, addl when ot == @source_details_ot ->
|
||||||
|
assert {:use_cookies, true} in addl
|
||||||
|
{:ok, source_details_return_fixture()}
|
||||||
|
|
||||||
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{series_directory: nil, use_cookies: true})
|
||||||
|
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not set use_cookies if the source is not set to use cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, 2, fn
|
||||||
|
_url, _opts, ot, addl when ot == @source_details_ot ->
|
||||||
|
assert {:use_cookies, false} in addl
|
||||||
|
{:ok, source_details_return_fixture()}
|
||||||
|
|
||||||
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{series_directory: nil, use_cookies: false})
|
||||||
|
perform_job(SourceMetadataStorageWorker, %{id: source.id})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "perform/1 when storing the series NFO" do
|
describe "perform/1 when storing the series NFO" do
|
||||||
test "stores the NFO if specified" do
|
test "stores the NFO if specified" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -311,12 +371,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "does not store the NFO if not specified" do
|
test "does not store the NFO if not specified" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "Season 1", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -330,12 +390,12 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorkerTest do
|
|||||||
|
|
||||||
test "does not store the NFO if the series directory cannot be determined" do
|
test "does not store the NFO if the series directory cannot be determined" do
|
||||||
stub(YtDlpRunnerMock, :run, fn
|
stub(YtDlpRunnerMock, :run, fn
|
||||||
_url, _opts, ot when ot == @source_details_ot ->
|
_url, _opts, ot, _addl when ot == @source_details_ot ->
|
||||||
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
filename = Path.join([Application.get_env(:pinchflat, :media_directory), "foo", "bar.mp4"])
|
||||||
|
|
||||||
{:ok, source_details_return_fixture(%{filename: filename})}
|
{:ok, source_details_return_fixture(%{filename: filename})}
|
||||||
|
|
||||||
_url, _opts, ot when ot == @metadata_ot ->
|
_url, _opts, ot, _addl when ot == @metadata_ot ->
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
defmodule Pinchflat.Profiles.MediaProfileDeletionWorkerTest do
|
||||||
|
use Pinchflat.DataCase
|
||||||
|
|
||||||
|
import Pinchflat.MediaFixtures
|
||||||
|
import Pinchflat.SourcesFixtures
|
||||||
|
import Pinchflat.ProfilesFixtures
|
||||||
|
|
||||||
|
alias Pinchflat.Profiles.MediaProfileDeletionWorker
|
||||||
|
|
||||||
|
setup do
|
||||||
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
||||||
|
|
||||||
|
{:ok, %{profile: media_profile_fixture()}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "kickoff/3" do
|
||||||
|
test "starts the worker", %{profile: profile} do
|
||||||
|
assert [] = all_enqueued(worker: MediaProfileDeletionWorker)
|
||||||
|
assert {:ok, _} = MediaProfileDeletionWorker.kickoff(profile)
|
||||||
|
assert [_] = all_enqueued(worker: MediaProfileDeletionWorker)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can be called with additional job arguments", %{profile: profile} do
|
||||||
|
job_args = %{"delete_files" => true}
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaProfileDeletionWorker.kickoff(profile, job_args)
|
||||||
|
|
||||||
|
assert_enqueued(worker: MediaProfileDeletionWorker, args: %{"id" => profile.id, "delete_files" => true})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "perform/1" do
|
||||||
|
test "deletes the profile, sources, and media but leaves the files", %{profile: profile} do
|
||||||
|
source = source_fixture(%{media_profile_id: profile.id})
|
||||||
|
media_item = media_item_with_attachments(%{source_id: source.id})
|
||||||
|
|
||||||
|
perform_job(MediaProfileDeletionWorker, %{"id" => profile.id})
|
||||||
|
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(profile) end
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
||||||
|
assert File.exists?(media_item.media_filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "deletes the profile, sources, and media and files if specified", %{profile: profile} do
|
||||||
|
source = source_fixture(%{media_profile_id: profile.id})
|
||||||
|
media_item = media_item_with_attachments(%{source_id: source.id})
|
||||||
|
|
||||||
|
perform_job(MediaProfileDeletionWorker, %{"id" => profile.id, "delete_files" => true})
|
||||||
|
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(profile) end
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
||||||
|
refute File.exists?(media_item.media_filepath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -113,7 +113,7 @@ defmodule Pinchflat.ProfilesTest do
|
|||||||
|
|
||||||
describe "delete_media_profile/2 when deleting files" do
|
describe "delete_media_profile/2 when deleting files" do
|
||||||
setup do
|
setup do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
||||||
|
|
||||||
describe "kickoff_indexing_task/3" do
|
describe "kickoff_indexing_task/3" do
|
||||||
test "it schedules a job" do
|
test "schedules a job" do
|
||||||
source = source_fixture(index_frequency_minutes: 1)
|
source = source_fixture(index_frequency_minutes: 1)
|
||||||
|
|
||||||
assert {:ok, _} = SlowIndexingHelpers.kickoff_indexing_task(source)
|
assert {:ok, _} = SlowIndexingHelpers.kickoff_indexing_task(source)
|
||||||
@@ -23,7 +23,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
|
assert_enqueued(worker: MediaCollectionIndexingWorker, args: %{"id" => source.id})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it creates and attaches a task" do
|
test "creates and attaches a task" do
|
||||||
source = source_fixture(index_frequency_minutes: 1)
|
source = source_fixture(index_frequency_minutes: 1)
|
||||||
|
|
||||||
assert {:ok, %Task{} = task} = SlowIndexingHelpers.kickoff_indexing_task(source)
|
assert {:ok, %Task{} = task} = SlowIndexingHelpers.kickoff_indexing_task(source)
|
||||||
@@ -31,7 +31,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert task.source_id == source.id
|
assert task.source_id == source.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it deletes any pending media collection tasks for the source" do
|
test "deletes any pending media collection tasks for the source" do
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
{:ok, job} = Oban.insert(MediaCollectionIndexingWorker.new(%{"id" => source.id}))
|
{:ok, job} = Oban.insert(MediaCollectionIndexingWorker.new(%{"id" => source.id}))
|
||||||
task = task_fixture(source_id: source.id, job_id: job.id)
|
task = task_fixture(source_id: source.id, job_id: job.id)
|
||||||
@@ -41,7 +41,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it deletes any pending media tasks for the source" do
|
test "deletes any pending media tasks for the source" do
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
{:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
|
{:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
|
||||||
task = task_fixture(source_id: source.id, job_id: job.id)
|
task = task_fixture(source_id: source.id, job_id: job.id)
|
||||||
@@ -51,7 +51,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(task) end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it deletes any fast indexing tasks for the source" do
|
test "deletes any fast indexing tasks for the source" do
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
{:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
|
{:ok, job} = Oban.insert(FastIndexingWorker.new(%{"id" => source.id}))
|
||||||
task = task_fixture(source_id: source.id, job_id: job.id)
|
task = task_fixture(source_id: source.id, job_id: job.id)
|
||||||
@@ -90,7 +90,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
{:ok, [source: source_fixture()]}
|
{:ok, [source: source_fixture()]}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it creates a media_item record for each media ID returned", %{source: source} do
|
test "creates a media_item record for each media ID returned", %{source: source} do
|
||||||
assert media_items = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
assert media_items = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
assert Enum.count(media_items) == 3
|
assert Enum.count(media_items) == 3
|
||||||
@@ -101,7 +101,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert Enum.all?(media_items, fn %MediaItem{} -> true end)
|
assert Enum.all?(media_items, fn %MediaItem{} -> true end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it attaches all media_items to the given source", %{source: source} do
|
test "attaches all media_items to the given source", %{source: source} do
|
||||||
source_id = source.id
|
source_id = source.id
|
||||||
assert media_items = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
assert media_items = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert Enum.all?(media_items, fn %MediaItem{source_id: ^source_id} -> true end)
|
assert Enum.all?(media_items, fn %MediaItem{source_id: ^source_id} -> true end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it won't duplicate media_items based on media_id and source", %{source: source} do
|
test "won't duplicate media_items based on media_id and source", %{source: source} do
|
||||||
_first_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
_first_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
_duplicate_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
_duplicate_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert Enum.count(media_items) == 3
|
assert Enum.count(media_items) == 3
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it can duplicate media_ids for different sources", %{source: source} do
|
test "can duplicate media_ids for different sources", %{source: source} do
|
||||||
other_source = source_fixture()
|
other_source = source_fixture()
|
||||||
|
|
||||||
media_items = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
media_items = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
@@ -130,7 +130,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
Enum.map(media_items_other_source, & &1.media_id)
|
Enum.map(media_items_other_source, & &1.media_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns a list of media_items", %{source: source} do
|
test "returns a list of media_items", %{source: source} do
|
||||||
first_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
first_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
duplicate_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
duplicate_run = SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert first_ids == duplicate_ids
|
assert first_ids == duplicate_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it updates the source's last_indexed_at field", %{source: source} do
|
test "updates the source's last_indexed_at field", %{source: source} do
|
||||||
assert source.last_indexed_at == nil
|
assert source.last_indexed_at == nil
|
||||||
|
|
||||||
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
@@ -149,7 +149,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert DateTime.diff(DateTime.utc_now(), source.last_indexed_at) < 2
|
assert DateTime.diff(DateTime.utc_now(), source.last_indexed_at) < 2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it enqueues a job for each pending media item" do
|
test "enqueues a job for each pending media item" do
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
|
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
|
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it does not attach tasks if the source is set to not download" do
|
test "does not attach tasks if the source is set to not download" do
|
||||||
source = source_fixture(download_media: false)
|
source = source_fixture(download_media: false)
|
||||||
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
|
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
assert [] = Tasks.list_tasks_for(media_item)
|
assert [] = Tasks.list_tasks_for(media_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't blow up if a media item cannot be coerced into a struct", %{source: source} do
|
test "doesn't blow up if a media item cannot be coerced into a struct", %{source: source} do
|
||||||
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts ->
|
stub(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts ->
|
||||||
response =
|
response =
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
@@ -190,6 +190,28 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
|||||||
|
|
||||||
assert %Ecto.Changeset{} = changeset
|
assert %Ecto.Changeset{} = changeset
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sets use_cookies if the source uses cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl_opts ->
|
||||||
|
assert {:use_cookies, true} in addl_opts
|
||||||
|
{:ok, source_attributes_return_fixture()}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: true})
|
||||||
|
|
||||||
|
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't set use_cookies if the source doesn't use cookies" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl_opts ->
|
||||||
|
assert {:use_cookies, false} in addl_opts
|
||||||
|
{:ok, source_attributes_return_fixture()}
|
||||||
|
end)
|
||||||
|
|
||||||
|
source = source_fixture(%{use_cookies: false})
|
||||||
|
|
||||||
|
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "index_and_enqueue_download_for_media_items/1 when testing file watcher" do
|
describe "index_and_enqueue_download_for_media_items/1 when testing file watcher" do
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
defmodule Pinchflat.Sources.SourceDeletionWorkerTest do
|
||||||
|
use Pinchflat.DataCase
|
||||||
|
|
||||||
|
import Pinchflat.MediaFixtures
|
||||||
|
import Pinchflat.SourcesFixtures
|
||||||
|
|
||||||
|
alias Pinchflat.Sources.SourceDeletionWorker
|
||||||
|
|
||||||
|
setup do
|
||||||
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
||||||
|
|
||||||
|
{:ok, %{source: source_fixture()}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "kickoff/3" do
|
||||||
|
test "starts the worker", %{source: source} do
|
||||||
|
assert [] = all_enqueued(worker: SourceDeletionWorker)
|
||||||
|
assert {:ok, _} = SourceDeletionWorker.kickoff(source)
|
||||||
|
assert [_] = all_enqueued(worker: SourceDeletionWorker)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can be called with additional job arguments", %{source: source} do
|
||||||
|
job_args = %{"delete_files" => true}
|
||||||
|
|
||||||
|
assert {:ok, _} = SourceDeletionWorker.kickoff(source, job_args)
|
||||||
|
|
||||||
|
assert_enqueued(worker: SourceDeletionWorker, args: %{"id" => source.id, "delete_files" => true})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "perform/1" do
|
||||||
|
test "deletes the source but leaves the files", %{source: source} do
|
||||||
|
media_item = media_item_with_attachments(%{source_id: source.id})
|
||||||
|
|
||||||
|
perform_job(SourceDeletionWorker, %{"id" => source.id})
|
||||||
|
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
||||||
|
assert File.exists?(media_item.media_filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "deletes the source and files if specified", %{source: source} do
|
||||||
|
media_item = media_item_with_attachments(%{source_id: source.id})
|
||||||
|
|
||||||
|
perform_job(SourceDeletionWorker, %{"id" => source.id, "delete_files" => true})
|
||||||
|
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
||||||
|
refute File.exists?(media_item.media_filepath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -85,7 +85,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
|
|
||||||
describe "create_source/2" do
|
describe "create_source/2" do
|
||||||
test "automatically sets the UUID" do
|
test "automatically sets the UUID" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -97,7 +97,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "UUID is not writable by the user" do
|
test "UUID is not writable by the user" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -110,7 +110,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creates a source and adds name + ID from runner response for channels" do
|
test "creates a source and adds name + ID from runner response for channels" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -123,7 +123,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creates a source and adds name + ID for playlists" do
|
test "creates a source and adds name + ID for playlists" do
|
||||||
expect(YtDlpRunnerMock, :run, &playlist_mock/3)
|
expect(YtDlpRunnerMock, :run, &playlist_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -136,7 +136,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "adds an error if the runner fails" do
|
test "adds an error if the runner fails" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "some error", 1} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:error, "some error", 1} end)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -148,7 +148,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "you can specify a custom custom_name" do
|
test "you can specify a custom custom_name" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -162,7 +162,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "friendly name is pulled from collection_name if not specified" do
|
test "friendly name is pulled from collection_name if not specified" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -175,7 +175,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creation enforces uniqueness of collection_id scoped to the media_profile and title regex" do
|
test "creation enforces uniqueness of collection_id scoped to the media_profile and title regex" do
|
||||||
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok,
|
{:ok,
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
channel: "some channel name",
|
channel: "some channel name",
|
||||||
@@ -196,7 +196,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creation lets you duplicate collection_ids and profiles as long as the regex is different" do
|
test "creation lets you duplicate collection_ids and profiles as long as the regex is different" do
|
||||||
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok,
|
{:ok,
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
channel: "some channel name",
|
channel: "some channel name",
|
||||||
@@ -220,7 +220,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creation lets you duplicate collection_ids as long as the media profile is different" do
|
test "creation lets you duplicate collection_ids as long as the media profile is different" do
|
||||||
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, 2, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok,
|
{:ok,
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
channel: "some channel name",
|
channel: "some channel name",
|
||||||
@@ -244,8 +244,8 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "collection_type is inferred from source details" do
|
test "collection_type is inferred from source details" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
expect(YtDlpRunnerMock, :run, &playlist_mock/3)
|
expect(YtDlpRunnerMock, :run, &playlist_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -264,13 +264,13 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creation with invalid data fails fast and does not call the runner" do
|
test "creation with invalid data fails fast and does not call the runner" do
|
||||||
expect(YtDlpRunnerMock, :run, 0, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, 0, &channel_mock/4)
|
||||||
|
|
||||||
assert {:error, %Ecto.Changeset{}} = Sources.create_source(@invalid_source_attrs)
|
assert {:error, %Ecto.Changeset{}} = Sources.create_source(@invalid_source_attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creation will schedule the indexing task" do
|
test "creation will schedule the indexing task" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -283,7 +283,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creation schedules an index test even if the index frequency is 0" do
|
test "creation schedules an index test even if the index frequency is 0" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -297,7 +297,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "fast_index forces the index frequency to be a default value" do
|
test "fast_index forces the index frequency to be a default value" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -312,7 +312,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "disabling fast index will not change the index frequency" do
|
test "disabling fast index will not change the index frequency" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -327,7 +327,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "creating will kickoff a metadata storage worker" do
|
test "creating will kickoff a metadata storage worker" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -344,7 +344,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
|
|
||||||
describe "create_source/2 when testing options" do
|
describe "create_source/2 when testing options" do
|
||||||
test "run_post_commit_tasks: false won't enqueue post-commit tasks" do
|
test "run_post_commit_tasks: false won't enqueue post-commit tasks" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
media_profile_id: media_profile_fixture().id,
|
media_profile_id: media_profile_fixture().id,
|
||||||
@@ -368,7 +368,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "updates with invalid data fails fast and does not call the runner" do
|
test "updates with invalid data fails fast and does not call the runner" do
|
||||||
expect(YtDlpRunnerMock, :run, 0, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, 0, &channel_mock/4)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
|
|
||||||
@@ -376,7 +376,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "updating the original_url will re-fetch the source details for channels" do
|
test "updating the original_url will re-fetch the source details for channels" do
|
||||||
expect(YtDlpRunnerMock, :run, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, &channel_mock/4)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
update_attrs = %{original_url: "https://www.youtube.com/channel/abc123"}
|
update_attrs = %{original_url: "https://www.youtube.com/channel/abc123"}
|
||||||
@@ -387,7 +387,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "updating the original_url will re-fetch the source details for playlists" do
|
test "updating the original_url will re-fetch the source details for playlists" do
|
||||||
expect(YtDlpRunnerMock, :run, &playlist_mock/3)
|
expect(YtDlpRunnerMock, :run, &playlist_mock/4)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
update_attrs = %{original_url: "https://www.youtube.com/playlist?list=abc123"}
|
update_attrs = %{original_url: "https://www.youtube.com/playlist?list=abc123"}
|
||||||
@@ -398,7 +398,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "not updating the original_url will not re-fetch the source details" do
|
test "not updating the original_url will not re-fetch the source details" do
|
||||||
expect(YtDlpRunnerMock, :run, 0, &channel_mock/3)
|
expect(YtDlpRunnerMock, :run, 0, &channel_mock/4)
|
||||||
|
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
update_attrs = %{name: "some updated name"}
|
update_attrs = %{name: "some updated name"}
|
||||||
@@ -519,7 +519,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "updating will kickoff a metadata storage worker if the original_url changes" do
|
test "updating will kickoff a metadata storage worker if the original_url changes" do
|
||||||
expect(YtDlpRunnerMock, :run, &playlist_mock/3)
|
expect(YtDlpRunnerMock, :run, &playlist_mock/4)
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
update_attrs = %{original_url: "https://www.youtube.com/channel/cba321"}
|
update_attrs = %{original_url: "https://www.youtube.com/channel/cba321"}
|
||||||
|
|
||||||
@@ -617,7 +617,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
|
|
||||||
describe "delete_source/2 when deleting files" do
|
describe "delete_source/2 when deleting files" do
|
||||||
setup do
|
setup do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
@@ -681,6 +681,34 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "change_source/3 when testing min/max duration validations" do
|
||||||
|
test "succeeds if min and max are nil" do
|
||||||
|
source = source_fixture()
|
||||||
|
|
||||||
|
assert %{errors: []} = Sources.change_source(source, %{min_duration_seconds: nil, max_duration_seconds: nil})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "succeeds if either min or max is nil" do
|
||||||
|
source = source_fixture()
|
||||||
|
|
||||||
|
assert %{errors: []} = Sources.change_source(source, %{min_duration_seconds: nil, max_duration_seconds: 100})
|
||||||
|
assert %{errors: []} = Sources.change_source(source, %{min_duration_seconds: 100, max_duration_seconds: nil})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "succeeds if min is less than max" do
|
||||||
|
source = source_fixture()
|
||||||
|
|
||||||
|
assert %{errors: []} = Sources.change_source(source, %{min_duration_seconds: 100, max_duration_seconds: 200})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "fails if min is greater than or equal to max" do
|
||||||
|
source = source_fixture()
|
||||||
|
|
||||||
|
assert %{errors: [_]} = Sources.change_source(source, %{min_duration_seconds: 200, max_duration_seconds: 100})
|
||||||
|
assert %{errors: [_]} = Sources.change_source(source, %{min_duration_seconds: 100, max_duration_seconds: 100})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "change_source/3 when testing original_url validation" do
|
describe "change_source/3 when testing original_url validation" do
|
||||||
test "succeeds when an original URL is valid" do
|
test "succeeds when an original URL is valid" do
|
||||||
source = source_fixture()
|
source = source_fixture()
|
||||||
@@ -736,7 +764,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp playlist_mock(_url, _opts, _ot) do
|
defp playlist_mock(_url, _opts, _ot, _addl) do
|
||||||
{
|
{
|
||||||
:ok,
|
:ok,
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
@@ -748,7 +776,7 @@ defmodule Pinchflat.SourcesTest do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp channel_mock(_url, _opts, _ot) do
|
defp channel_mock(_url, _opts, _ot, _addl) do
|
||||||
channel_id = "some_channel_id_#{:rand.uniform(1_000_000)}"
|
channel_id = "some_channel_id_#{:rand.uniform(1_000_000)}"
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,18 +51,26 @@ defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
|||||||
{:ok, cookie_file: cookie_file, yt_dlp_file: yt_dlp_file}
|
{:ok, cookie_file: cookie_file, yt_dlp_file: yt_dlp_file}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "includes cookie options when cookies.txt exists", %{cookie_file: cookie_file} do
|
test "includes cookie options when cookies.txt exists and enabled", %{cookie_file: cookie_file} do
|
||||||
FilesystemUtils.write_p!(cookie_file, "cookie data")
|
FilesystemUtils.write_p!(cookie_file, "cookie data")
|
||||||
|
|
||||||
assert {:ok, output} = Runner.run(@media_url, [], "")
|
assert {:ok, output} = Runner.run(@media_url, [], "", use_cookies: true)
|
||||||
|
|
||||||
assert String.contains?(output, "--cookies #{cookie_file}")
|
assert String.contains?(output, "--cookies #{cookie_file}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't include cookie options when cookies.txt exists but disabled", %{cookie_file: cookie_file} do
|
||||||
|
FilesystemUtils.write_p!(cookie_file, "cookie data")
|
||||||
|
|
||||||
|
assert {:ok, output} = Runner.run(@media_url, [], "", use_cookies: false)
|
||||||
|
|
||||||
|
refute String.contains?(output, "--cookies #{cookie_file}")
|
||||||
|
end
|
||||||
|
|
||||||
test "doesn't include cookie options when cookies.txt blank", %{cookie_file: cookie_file} do
|
test "doesn't include cookie options when cookies.txt blank", %{cookie_file: cookie_file} do
|
||||||
FilesystemUtils.write_p!(cookie_file, " \n \n ")
|
FilesystemUtils.write_p!(cookie_file, " \n \n ")
|
||||||
|
|
||||||
assert {:ok, output} = Runner.run(@media_url, [], "")
|
assert {:ok, output} = Runner.run(@media_url, [], "", use_cookies: true)
|
||||||
|
|
||||||
refute String.contains?(output, "--cookies")
|
refute String.contains?(output, "--cookies")
|
||||||
refute String.contains?(output, cookie_file)
|
refute String.contains?(output, cookie_file)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
|||||||
MediaCollection.get_media_attributes_for_collection(@channel_url)
|
MediaCollection.get_media_attributes_for_collection(@channel_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it passes the expected default args" do
|
test "passes the expected default args" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, ot, _addl_opts ->
|
expect(YtDlpRunnerMock, :run, fn _url, opts, ot, _addl_opts ->
|
||||||
assert opts == [:simulate, :skip_download, :ignore_no_formats_error, :no_warnings]
|
assert opts == [:simulate, :skip_download, :ignore_no_formats_error, :no_warnings]
|
||||||
assert ot == Media.indexing_output_template()
|
assert ot == Media.indexing_output_template()
|
||||||
@@ -35,9 +35,10 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
|||||||
assert {:error, "Big issue", 1} = MediaCollection.get_media_attributes_for_collection(@channel_url)
|
assert {:error, "Big issue", 1} = MediaCollection.get_media_attributes_for_collection(@channel_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "passes the explict tmpfile path to runner" do
|
test "passes additional args to runner" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl_opts ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl_opts ->
|
||||||
assert [{:output_filepath, filepath}] = addl_opts
|
assert [{:output_filepath, filepath} | _] = addl_opts
|
||||||
|
assert {:use_cookies, false} in addl_opts
|
||||||
assert String.ends_with?(filepath, ".json")
|
assert String.ends_with?(filepath, ".json")
|
||||||
|
|
||||||
{:ok, ""}
|
{:ok, ""}
|
||||||
@@ -72,8 +73,8 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "get_source_details/1" do
|
describe "get_source_details/1" do
|
||||||
test "it returns a map with data on success" do
|
test "returns a map with data on success" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts ->
|
||||||
Phoenix.json_library().encode(%{
|
Phoenix.json_library().encode(%{
|
||||||
channel: "PinchflatTestChannel",
|
channel: "PinchflatTestChannel",
|
||||||
channel_id: "UCQH2",
|
channel_id: "UCQH2",
|
||||||
@@ -92,8 +93,8 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
|||||||
} = res
|
} = res
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it passes the expected args to the backend runner" do
|
test "passes the expected args to the runner" do
|
||||||
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
|
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot, _addl_opts ->
|
||||||
assert opts == [:simulate, :skip_download, :ignore_no_formats_error, playlist_end: 1]
|
assert opts == [:simulate, :skip_download, :ignore_no_formats_error, playlist_end: 1]
|
||||||
assert ot == "%(.{channel,channel_id,playlist_id,playlist_title,filename})j"
|
assert ot == "%(.{channel,channel_id,playlist_id,playlist_title,filename})j"
|
||||||
|
|
||||||
@@ -103,55 +104,73 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
|||||||
assert {:ok, _} = MediaCollection.get_source_details(@channel_url)
|
assert {:ok, _} = MediaCollection.get_source_details(@channel_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns an error if the runner returns an error" do
|
test "passes custom args to the runner" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, _ot, _addl_opts ->
|
||||||
|
assert {:foo, :bar} in opts
|
||||||
|
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaCollection.get_source_details(@channel_url, foo: :bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passes additional args to the runner" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn @channel_url, _opts, _ot, addl_opts ->
|
||||||
|
assert {:use_cookies, true} in addl_opts
|
||||||
|
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaCollection.get_source_details(@channel_url, [], use_cookies: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if the runner returns an error" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:error, "Big issue", 1} end)
|
||||||
|
|
||||||
assert {:error, "Big issue", 1} = MediaCollection.get_source_details(@channel_url)
|
assert {:error, "Big issue", 1} = MediaCollection.get_source_details(@channel_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns an error if the output is not JSON" do
|
test "returns an error if the output is not JSON" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:ok, "Not JSON"} end)
|
||||||
|
|
||||||
assert {:error, %Jason.DecodeError{}} = MediaCollection.get_source_details(@channel_url)
|
assert {:error, %Jason.DecodeError{}} = MediaCollection.get_source_details(@channel_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "get_source_metadata/1" do
|
describe "get_source_metadata/1" do
|
||||||
test "it returns a map with data on success" do
|
test "returns a map with data on success" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts ->
|
||||||
Phoenix.json_library().encode(%{channel: "PinchflatTestChannel"})
|
Phoenix.json_library().encode(%{channel: "PinchflatTestChannel"})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, res} = MediaCollection.get_source_metadata(@channel_url)
|
assert {:ok, res} = MediaCollection.get_source_metadata(@channel_url, playlist_items: 0)
|
||||||
|
|
||||||
assert %{"channel" => "PinchflatTestChannel"} = res
|
assert %{"channel" => "PinchflatTestChannel"} = res
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it passes the expected args to the backend runner" do
|
test "passes the expected args to the backend runner" do
|
||||||
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot ->
|
expect(YtDlpRunnerMock, :run, fn @channel_url, opts, ot, _addl_opts ->
|
||||||
assert opts == [:skip_download, playlist_items: 0]
|
assert opts == [:skip_download, playlist_items: 0]
|
||||||
assert ot == "playlist:%()j"
|
assert ot == "playlist:%()j"
|
||||||
|
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = MediaCollection.get_source_metadata(@channel_url)
|
assert {:ok, _} = MediaCollection.get_source_metadata(@channel_url, playlist_items: 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns an error if the runner returns an error" do
|
test "passes additional args to the runner" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
expect(YtDlpRunnerMock, :run, fn @channel_url, _opts, _ot, addl_opts ->
|
||||||
|
assert {:use_cookies, true} in addl_opts
|
||||||
|
|
||||||
assert {:error, "Big issue", 1} = MediaCollection.get_source_metadata(@channel_url)
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaCollection.get_source_metadata(@channel_url, [playlist_items: 0], use_cookies: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns an error if the output is not JSON" do
|
test "passes custom args to the runner" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:ok, "Not JSON"} end)
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl_opts ->
|
||||||
|
|
||||||
assert {:error, %Jason.DecodeError{}} = MediaCollection.get_source_metadata(@channel_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "allows you to pass additional opts" do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot ->
|
|
||||||
assert opts == [:skip_download, playlist_items: 1, real_opt: :yup]
|
assert opts == [:skip_download, playlist_items: 1, real_opt: :yup]
|
||||||
|
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
@@ -165,5 +184,17 @@ defmodule Pinchflat.YtDlp.MediaCollectionTest do
|
|||||||
MediaCollection.get_source_metadata(@channel_url, real_opt: :yup)
|
MediaCollection.get_source_metadata(@channel_url, real_opt: :yup)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns an error if the runner returns an error" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:error, "Big issue", 1} end)
|
||||||
|
|
||||||
|
assert {:error, "Big issue", 1} = MediaCollection.get_source_metadata(@channel_url, playlist_items: 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if the output is not JSON" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl_opts -> {:ok, "Not JSON"} end)
|
||||||
|
|
||||||
|
assert {:error, %Jason.DecodeError{}} = MediaCollection.get_source_metadata(@channel_url, playlist_items: 0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,15 +20,24 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
assert {:ok, _} = Media.download(@media_url)
|
assert {:ok, _} = Media.download(@media_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "passes along additional options" do
|
test "passes along custom command args" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, addl ->
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
||||||
assert [:no_simulate, :custom_arg] = opts
|
assert [:no_simulate, :custom_arg] = opts
|
||||||
|
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = Media.download(@media_url, [:custom_arg])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passes along additional options" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
assert [addl_arg: true] = addl
|
assert [addl_arg: true] = addl
|
||||||
|
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, _} = Media.download(@media_url, [:custom_arg], addl_arg: true)
|
assert {:ok, _} = Media.download(@media_url, [], addl_arg: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "parses and returns the generated file as JSON" do
|
test "parses and returns the generated file as JSON" do
|
||||||
@@ -51,7 +60,7 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
|
|
||||||
describe "download_thumbnail/2" do
|
describe "download_thumbnail/2" do
|
||||||
test "calls the backend runner with the expected arguments" do
|
test "calls the backend runner with the expected arguments" do
|
||||||
expect(YtDlpRunnerMock, :run, fn @media_url, opts, ot ->
|
expect(YtDlpRunnerMock, :run, fn @media_url, opts, ot, _addl ->
|
||||||
assert opts == [:no_simulate, :skip_download, :write_thumbnail, {:convert_thumbnail, "jpg"}]
|
assert opts == [:no_simulate, :skip_download, :write_thumbnail, {:convert_thumbnail, "jpg"}]
|
||||||
assert ot == "after_move:%()j"
|
assert ot == "after_move:%()j"
|
||||||
|
|
||||||
@@ -61,8 +70,8 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
assert {:ok, _} = Media.download_thumbnail(@media_url)
|
assert {:ok, _} = Media.download_thumbnail(@media_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "passes along additional options" do
|
test "passes along custom command args" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
||||||
assert :custom_arg in opts
|
assert :custom_arg in opts
|
||||||
|
|
||||||
{:ok, "{}"}
|
{:ok, "{}"}
|
||||||
@@ -71,8 +80,18 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
assert {:ok, _} = Media.download_thumbnail(@media_url, [:custom_arg])
|
assert {:ok, _} = Media.download_thumbnail(@media_url, [:custom_arg])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "passes along additional options" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
|
assert [addl_arg: true] = addl
|
||||||
|
|
||||||
|
{:ok, "{}"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = Media.download_thumbnail(@media_url, [], addl_arg: true)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns errors" do
|
test "returns errors" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opt, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opt, _ot, _addl ->
|
||||||
{:error, "something"}
|
{:error, "something"}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -82,7 +101,7 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
|
|
||||||
describe "get_media_attributes/1" do
|
describe "get_media_attributes/1" do
|
||||||
test "returns a list of video attributes" do
|
test "returns a list of video attributes" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, media_attributes_return_fixture()}
|
{:ok, media_attributes_return_fixture()}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -91,7 +110,7 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "it passes the expected default args" do
|
test "it passes the expected default args" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, opts, ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, opts, ot, _addl ->
|
||||||
assert opts == [:simulate, :skip_download]
|
assert opts == [:simulate, :skip_download]
|
||||||
assert ot == Media.indexing_output_template()
|
assert ot == Media.indexing_output_template()
|
||||||
|
|
||||||
@@ -101,8 +120,17 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
|||||||
assert {:ok, _} = Media.get_media_attributes(@media_url)
|
assert {:ok, _} = Media.get_media_attributes(@media_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "passes along additional options" do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, addl ->
|
||||||
|
assert [addl_arg: true] = addl
|
||||||
|
{:ok, media_attributes_return_fixture()}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, _} = Media.get_media_attributes(@media_url, addl_arg: true)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns the error straight through when the command fails" do
|
test "returns the error straight through when the command fails" do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot -> {:error, "Big issue", 1} end)
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> {:error, "Big issue", 1} end)
|
||||||
|
|
||||||
assert {:error, "Big issue", 1} = Media.get_media_attributes(@media_url)
|
assert {:error, "Big issue", 1} = Media.get_media_attributes(@media_url)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ defmodule PinchflatWeb.MediaItemControllerTest do
|
|||||||
describe "delete media" do
|
describe "delete media" do
|
||||||
setup do
|
setup do
|
||||||
media_item = media_item_with_attachments()
|
media_item = media_item_with_attachments()
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
%{media_item: media_item}
|
%{media_item: media_item}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
defmodule PinchflatWeb.MediaProfileControllerTest do
|
defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||||
use PinchflatWeb.ConnCase
|
use PinchflatWeb.ConnCase
|
||||||
|
|
||||||
import Pinchflat.MediaFixtures
|
|
||||||
import Pinchflat.SourcesFixtures
|
|
||||||
import Pinchflat.ProfilesFixtures
|
import Pinchflat.ProfilesFixtures
|
||||||
|
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Settings
|
alias Pinchflat.Settings
|
||||||
|
alias Pinchflat.Profiles.MediaProfileDeletionWorker
|
||||||
|
|
||||||
@create_attrs %{name: "some name", output_path_template: "output_template.{{ ext }}"}
|
@create_attrs %{name: "some name", output_path_template: "output_template.{{ ext }}"}
|
||||||
@update_attrs %{
|
@update_attrs %{
|
||||||
@@ -23,8 +22,17 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
|||||||
|
|
||||||
describe "index" do
|
describe "index" do
|
||||||
test "lists all media_profiles", %{conn: conn} do
|
test "lists all media_profiles", %{conn: conn} do
|
||||||
|
profile = media_profile_fixture()
|
||||||
conn = get(conn, ~p"/media_profiles")
|
conn = get(conn, ~p"/media_profiles")
|
||||||
|
|
||||||
assert html_response(conn, 200) =~ "Media Profiles"
|
assert html_response(conn, 200) =~ "Media Profiles"
|
||||||
|
assert html_response(conn, 200) =~ profile.name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "omits profiles that have marked_for_deletion_at set", %{conn: conn} do
|
||||||
|
profile = media_profile_fixture(marked_for_deletion_at: DateTime.utc_now())
|
||||||
|
conn = get(conn, ~p"/media_profiles")
|
||||||
|
refute html_response(conn, 200) =~ profile.name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -102,34 +110,28 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete media_profile when just deleting the records" do
|
describe "delete media_profile in all cases" do
|
||||||
setup [:create_media_profile]
|
setup [:create_media_profile]
|
||||||
|
|
||||||
test "deletes chosen media_profile and its associations", %{conn: conn, media_profile: media_profile} do
|
|
||||||
source = source_fixture(media_profile_id: media_profile.id)
|
|
||||||
media_item = media_item_with_attachments(%{source_id: source.id})
|
|
||||||
|
|
||||||
conn = delete(conn, ~p"/media_profiles/#{media_profile}")
|
|
||||||
assert redirected_to(conn) == ~p"/media_profiles"
|
|
||||||
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_profile) end
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "redirects to the media_profiles page", %{conn: conn, media_profile: media_profile} do
|
test "redirects to the media_profiles page", %{conn: conn, media_profile: media_profile} do
|
||||||
conn = delete(conn, ~p"/media_profiles/#{media_profile}")
|
conn = delete(conn, ~p"/media_profiles/#{media_profile}")
|
||||||
|
|
||||||
assert redirected_to(conn) == ~p"/media_profiles"
|
assert redirected_to(conn) == ~p"/media_profiles"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "doesn't delete any files", %{conn: conn, media_profile: media_profile} do
|
test "sets marked_for_deletion_at", %{conn: conn, media_profile: media_profile} do
|
||||||
source = source_fixture(media_profile_id: media_profile.id)
|
delete(conn, ~p"/media_profiles/#{media_profile}")
|
||||||
media_item = media_item_with_attachments(%{source_id: source.id})
|
assert Repo.reload!(media_profile).marked_for_deletion_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "delete media_profile when just deleting the records" do
|
||||||
|
setup [:create_media_profile]
|
||||||
|
|
||||||
|
test "enqueues a job without the delete_files arg", %{conn: conn, media_profile: media_profile} do
|
||||||
delete(conn, ~p"/media_profiles/#{media_profile}")
|
delete(conn, ~p"/media_profiles/#{media_profile}")
|
||||||
|
|
||||||
assert File.exists?(media_item.media_filepath)
|
assert [%{args: %{"delete_files" => false}}] = all_enqueued(worker: MediaProfileDeletionWorker)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -137,36 +139,15 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
|||||||
setup [:create_media_profile]
|
setup [:create_media_profile]
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> {:ok, "", 0} end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deletes chosen media_profile and its associations", %{conn: conn, media_profile: media_profile} do
|
test "enqueues a job with the delete_files arg", %{conn: conn, media_profile: media_profile} do
|
||||||
source = source_fixture(media_profile_id: media_profile.id)
|
|
||||||
media_item = media_item_with_attachments(%{source_id: source.id})
|
|
||||||
|
|
||||||
conn = delete(conn, ~p"/media_profiles/#{media_profile}?delete_files=true")
|
|
||||||
assert redirected_to(conn) == ~p"/media_profiles"
|
|
||||||
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_profile) end
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "redirects to the media_profiles page", %{conn: conn, media_profile: media_profile} do
|
|
||||||
conn = delete(conn, ~p"/media_profiles/#{media_profile}?delete_files=true")
|
|
||||||
|
|
||||||
assert redirected_to(conn) == ~p"/media_profiles"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "deletes the files", %{conn: conn, media_profile: media_profile} do
|
|
||||||
source = source_fixture(media_profile_id: media_profile.id)
|
|
||||||
media_item = media_item_with_attachments(%{source_id: source.id})
|
|
||||||
|
|
||||||
delete(conn, ~p"/media_profiles/#{media_profile}?delete_files=true")
|
delete(conn, ~p"/media_profiles/#{media_profile}?delete_files=true")
|
||||||
|
|
||||||
refute File.exists?(media_item.media_filepath)
|
assert [%{args: %{"delete_files" => true}}] = all_enqueued(worker: MediaProfileDeletionWorker)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
|
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
alias Pinchflat.Settings
|
alias Pinchflat.Settings
|
||||||
|
alias Pinchflat.Sources.SourceDeletionWorker
|
||||||
alias Pinchflat.Downloading.MediaDownloadWorker
|
alias Pinchflat.Downloading.MediaDownloadWorker
|
||||||
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
||||||
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
||||||
@@ -33,8 +34,26 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
|
|
||||||
describe "index" do
|
describe "index" do
|
||||||
test "lists all sources", %{conn: conn} do
|
test "lists all sources", %{conn: conn} do
|
||||||
|
source = source_fixture()
|
||||||
conn = get(conn, ~p"/sources")
|
conn = get(conn, ~p"/sources")
|
||||||
|
|
||||||
assert html_response(conn, 200) =~ "Sources"
|
assert html_response(conn, 200) =~ "Sources"
|
||||||
|
assert html_response(conn, 200) =~ source.custom_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "omits sources that have marked_for_deletion_at set", %{conn: conn} do
|
||||||
|
source = source_fixture(marked_for_deletion_at: DateTime.utc_now())
|
||||||
|
conn = get(conn, ~p"/sources")
|
||||||
|
|
||||||
|
refute html_response(conn, 200) =~ source.custom_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "omits sources who's media profile has marked_for_deletion_at set", %{conn: conn} do
|
||||||
|
media_profile = media_profile_fixture(marked_for_deletion_at: DateTime.utc_now())
|
||||||
|
source = source_fixture(media_profile_id: media_profile.id)
|
||||||
|
conn = get(conn, ~p"/sources")
|
||||||
|
|
||||||
|
refute html_response(conn, 200) =~ source.custom_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -63,7 +82,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
|
|
||||||
describe "create source" do
|
describe "create source" do
|
||||||
test "redirects to show when data is valid", %{conn: conn, create_attrs: create_attrs} do
|
test "redirects to show when data is valid", %{conn: conn, create_attrs: create_attrs} do
|
||||||
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/4)
|
||||||
conn = post(conn, ~p"/sources", source: create_attrs)
|
conn = post(conn, ~p"/sources", source: create_attrs)
|
||||||
|
|
||||||
assert %{id: id} = redirected_params(conn)
|
assert %{id: id} = redirected_params(conn)
|
||||||
@@ -79,7 +98,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "redirects to onboarding when onboarding", %{conn: conn, create_attrs: create_attrs} do
|
test "redirects to onboarding when onboarding", %{conn: conn, create_attrs: create_attrs} do
|
||||||
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/4)
|
||||||
|
|
||||||
Settings.set(onboarding: true)
|
Settings.set(onboarding: true)
|
||||||
conn = post(conn, ~p"/sources", source: create_attrs)
|
conn = post(conn, ~p"/sources", source: create_attrs)
|
||||||
@@ -108,7 +127,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
setup [:create_source]
|
setup [:create_source]
|
||||||
|
|
||||||
test "redirects when data is valid", %{conn: conn, source: source, update_attrs: update_attrs} do
|
test "redirects when data is valid", %{conn: conn, source: source, update_attrs: update_attrs} do
|
||||||
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/4)
|
||||||
|
|
||||||
conn = put(conn, ~p"/sources/#{source}", source: update_attrs)
|
conn = put(conn, ~p"/sources/#{source}", source: update_attrs)
|
||||||
assert redirected_to(conn) == ~p"/sources/#{source}"
|
assert redirected_to(conn) == ~p"/sources/#{source}"
|
||||||
@@ -127,51 +146,37 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete source when just deleting the records" do
|
describe "delete source in all cases" do
|
||||||
setup [:create_source]
|
setup [:create_source]
|
||||||
|
|
||||||
test "deletes chosen source and media_items", %{conn: conn, source: source, media_item: media_item} do
|
|
||||||
delete(conn, ~p"/sources/#{source}")
|
|
||||||
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "redirects to the sources page", %{conn: conn, source: source} do
|
test "redirects to the sources page", %{conn: conn, source: source} do
|
||||||
conn = delete(conn, ~p"/sources/#{source}")
|
conn = delete(conn, ~p"/sources/#{source}")
|
||||||
assert redirected_to(conn) == ~p"/sources"
|
assert redirected_to(conn) == ~p"/sources"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not delete the files", %{conn: conn, source: source, media_item: media_item} do
|
test "sets marked_for_deletion_at", %{conn: conn, source: source} do
|
||||||
delete(conn, ~p"/sources/#{source}")
|
delete(conn, ~p"/sources/#{source}")
|
||||||
assert File.exists?(media_item.media_filepath)
|
assert Repo.reload!(source).marked_for_deletion_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "delete source when just deleting the records" do
|
||||||
|
setup [:create_source]
|
||||||
|
|
||||||
|
test "enqueues a job without the delete_files arg", %{conn: conn, source: source} do
|
||||||
|
delete(conn, ~p"/sources/#{source}")
|
||||||
|
|
||||||
|
assert [%{args: %{"delete_files" => false}}] = all_enqueued(worker: SourceDeletionWorker)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete source when deleting the records and files" do
|
describe "delete source when deleting the records and files" do
|
||||||
setup [:create_source]
|
setup [:create_source]
|
||||||
|
|
||||||
setup do
|
test "enqueues a job without the delete_files arg", %{conn: conn, source: source} do
|
||||||
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "deletes chosen source and media_items", %{conn: conn, source: source, media_item: media_item} do
|
|
||||||
delete(conn, ~p"/sources/#{source}?delete_files=true")
|
delete(conn, ~p"/sources/#{source}?delete_files=true")
|
||||||
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(source) end
|
assert [%{args: %{"delete_files" => true}}] = all_enqueued(worker: SourceDeletionWorker)
|
||||||
assert_raise Ecto.NoResultsError, fn -> Repo.reload!(media_item) end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "redirects to the sources page", %{conn: conn, source: source} do
|
|
||||||
conn = delete(conn, ~p"/sources/#{source}?delete_files=true")
|
|
||||||
assert redirected_to(conn) == ~p"/sources"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "deletes the files", %{conn: conn, source: source, media_item: media_item} do
|
|
||||||
delete(conn, ~p"/sources/#{source}?delete_files=true")
|
|
||||||
refute File.exists?(media_item.media_filepath)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -270,7 +275,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
|||||||
%{source: source, media_item: media_item}
|
%{source: source, media_item: media_item}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp runner_function_mock(_url, _opts, _ot) do
|
defp runner_function_mock(_url, _opts, _ot, _addl) do
|
||||||
{
|
{
|
||||||
:ok,
|
:ok,
|
||||||
Phoenix.json_library().encode!(%{
|
Phoenix.json_library().encode!(%{
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
../assets/vendor/
|
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Get the current date in the format YYYY.MM.DD (stripping leading zeros)
|
||||||
|
DATE=$(date +"%Y.%-m.%-d")
|
||||||
|
|
||||||
|
# Get the current version from mix.exs
|
||||||
|
VERSION=$(grep "version: " mix.exs | cut -d '"' -f2)
|
||||||
|
|
||||||
|
echo "Bumping version from $VERSION to $DATE"
|
||||||
|
# Replace the version in mix.exs with the new version
|
||||||
|
sed -i "s/version: \"$VERSION\"/version: \"$DATE\"/g" mix.exs
|
||||||
|
|
||||||
|
# Run checks to ensure it's a valid mix.exs file
|
||||||
|
mix check
|
||||||
Reference in New Issue
Block a user