Added ability to specify overwrite behaviour when downloading media
This commit is contained in:
parent
5c86e7192e
commit
dc57909c24
6 changed files with 102 additions and 33 deletions
|
|
@ -15,11 +15,11 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilder do
|
||||||
|
|
||||||
Returns {:ok, [Keyword.t()]}
|
Returns {:ok, [Keyword.t()]}
|
||||||
"""
|
"""
|
||||||
def build(%MediaItem{} = media_item_with_preloads) do
|
def build(%MediaItem{} = media_item_with_preloads, override_opts \\ []) do
|
||||||
media_profile = media_item_with_preloads.source.media_profile
|
media_profile = media_item_with_preloads.source.media_profile
|
||||||
|
|
||||||
built_options =
|
built_options =
|
||||||
default_options() ++
|
default_options(override_opts) ++
|
||||||
subtitle_options(media_profile) ++
|
subtitle_options(media_profile) ++
|
||||||
thumbnail_options(media_item_with_preloads) ++
|
thumbnail_options(media_item_with_preloads) ++
|
||||||
metadata_options(media_profile) ++
|
metadata_options(media_profile) ++
|
||||||
|
|
@ -50,11 +50,12 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilder do
|
||||||
build_output_path_for(%MediaItem{source: source_with_preloads})
|
build_output_path_for(%MediaItem{source: source_with_preloads})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp default_options do
|
defp default_options(override_opts) do
|
||||||
|
overwrite_behaviour = Keyword.get(override_opts, :overwrite_behaviour, :force_overwrites)
|
||||||
|
|
||||||
[
|
[
|
||||||
:no_progress,
|
:no_progress,
|
||||||
# Add force-overwrites to make sure redownloading works
|
overwrite_behaviour,
|
||||||
:force_overwrites,
|
|
||||||
# This makes the date metadata conform to what jellyfin expects
|
# This makes the date metadata conform to what jellyfin expects
|
||||||
parse_metadata: "%(upload_date>%Y-%m-%d)s:(?P<meta_date>.+)"
|
parse_metadata: "%(upload_date>%Y-%m-%d)s:(?P<meta_date>.+)"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
Does not download media if its source is set to not download media
|
Does not download media if its source is set to not download media
|
||||||
(unless forced).
|
(unless forced).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `force`: force download even if the source is set to not download media. Fully
|
||||||
|
re-downloads media, including the video
|
||||||
|
- `redownload?`: re-downloads media, including the video. Does not force download
|
||||||
|
if the source is set to not download media
|
||||||
|
|
||||||
Returns :ok | {:ok, %MediaItem{}} | {:error, any, ...any}
|
Returns :ok | {:ok, %MediaItem{}} | {:error, any, ...any}
|
||||||
"""
|
"""
|
||||||
@impl Oban.Worker
|
@impl Oban.Worker
|
||||||
|
|
@ -47,7 +53,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
|
|
||||||
# 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
|
||||||
download_media_and_schedule_jobs(media_item, is_redownload)
|
download_media_and_schedule_jobs(media_item, is_redownload, should_force)
|
||||||
else
|
else
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
@ -56,8 +62,11 @@ 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
|
||||||
|
|
||||||
defp download_media_and_schedule_jobs(media_item, is_redownload) do
|
defp download_media_and_schedule_jobs(media_item, is_redownload, should_force) do
|
||||||
case MediaDownloader.download_for_media_item(media_item) do
|
overwrite_behaviour = if should_force || is_redownload, do: :force_overwrites, else: :no_force_overwrites
|
||||||
|
override_opts = [overwrite_behaviour: overwrite_behaviour]
|
||||||
|
|
||||||
|
case MediaDownloader.download_for_media_item(media_item, override_opts) do
|
||||||
{:ok, downloaded_media_item} ->
|
{:ok, downloaded_media_item} ->
|
||||||
{:ok, updated_media_item} =
|
{:ok, updated_media_item} =
|
||||||
Media.update_media_item(downloaded_media_item, %{
|
Media.update_media_item(downloaded_media_item, %{
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ defmodule Pinchflat.Downloading.MediaDownloader do
|
||||||
|
|
||||||
Returns {:ok, %MediaItem{}} | {:error, any, ...any}
|
Returns {:ok, %MediaItem{}} | {:error, any, ...any}
|
||||||
"""
|
"""
|
||||||
def download_for_media_item(%MediaItem{} = media_item) do
|
def download_for_media_item(%MediaItem{} = media_item, override_opts \\ []) do
|
||||||
output_filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
output_filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
||||||
media_with_preloads = Repo.preload(media_item, [:metadata, source: :media_profile])
|
media_with_preloads = Repo.preload(media_item, [:metadata, source: :media_profile])
|
||||||
|
|
||||||
case download_with_options(media_item.original_url, media_with_preloads, output_filepath) do
|
case download_with_options(media_item.original_url, media_with_preloads, output_filepath, override_opts) do
|
||||||
{:ok, parsed_json} ->
|
{:ok, parsed_json} ->
|
||||||
update_media_item_from_parsed_json(media_with_preloads, parsed_json)
|
update_media_item_from_parsed_json(media_with_preloads, parsed_json)
|
||||||
|
|
||||||
|
|
@ -103,8 +103,8 @@ defmodule Pinchflat.Downloading.MediaDownloader do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp download_with_options(url, item_with_preloads, output_filepath) do
|
defp download_with_options(url, item_with_preloads, output_filepath, override_opts) do
|
||||||
{:ok, options} = DownloadOptionBuilder.build(item_with_preloads)
|
{:ok, options} = DownloadOptionBuilder.build(item_with_preloads, override_opts)
|
||||||
|
|
||||||
YtDlpMedia.download(url, options, output_filepath: output_filepath)
|
YtDlpMedia.download(url, options, output_filepath: output_filepath)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,13 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilderTest do
|
||||||
assert :force_overwrites in res
|
assert :force_overwrites in res
|
||||||
assert {:parse_metadata, "%(upload_date>%Y-%m-%d)s:(?P<meta_date>.+)"} in res
|
assert {:parse_metadata, "%(upload_date>%Y-%m-%d)s:(?P<meta_date>.+)"} in res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "includes override options if specified", %{media_item: media_item} do
|
||||||
|
assert {:ok, res} = DownloadOptionBuilder.build(media_item, overwrite_behaviour: :no_force_overwrites)
|
||||||
|
|
||||||
|
refute :force_overwrites in res
|
||||||
|
assert :no_force_overwrites in res
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "build/1 when testing subtitle options" do
|
describe "build/1 when testing subtitle options" do
|
||||||
|
|
|
||||||
|
|
@ -136,15 +136,6 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "downloads anyway if forced", %{media_item: media_item} do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> :ok end)
|
|
||||||
|
|
||||||
Sources.update_source(media_item.source, %{download_media: false})
|
|
||||||
Media.update_media_item(media_item, %{prevent_download: true})
|
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
|
||||||
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, fn _url, _opts, _ot, _addl ->
|
||||||
metadata = render_parsed_metadata(:media_metadata)
|
metadata = render_parsed_metadata(:media_metadata)
|
||||||
|
|
@ -159,18 +150,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
||||||
assert media_item.media_size_bytes > 0
|
assert media_item.media_size_bytes > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "saves redownloaded_at if this is for a redownload", %{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 ->
|
|
||||||
{:ok, render_metadata(:media_metadata)}
|
|
||||||
end)
|
|
||||||
|
|
||||||
perform_job(MediaDownloadWorker, %{id: media_item.id, redownload?: true})
|
|
||||||
media_item = Repo.reload(media_item)
|
|
||||||
|
|
||||||
assert media_item.media_redownloaded_at != nil
|
|
||||||
end
|
|
||||||
|
|
||||||
test "doesn't save redownloaded_at if this is not for a redownload", %{media_item: media_item} do
|
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
@ -198,5 +178,62 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
||||||
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
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
||||||
|
assert :no_force_overwrites in opts
|
||||||
|
refute :force_overwrites in opts
|
||||||
|
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "perform/1 when testing forced downloads" do
|
||||||
|
test "ignores 'prevent_download' if forced", %{media_item: media_item} do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl -> :ok end)
|
||||||
|
|
||||||
|
Sources.update_source(media_item.source, %{download_media: false})
|
||||||
|
Media.update_media_item(media_item, %{prevent_download: true})
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets force_overwrites runner option", %{media_item: media_item} do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
||||||
|
assert :force_overwrites in opts
|
||||||
|
refute :no_force_overwrites in opts
|
||||||
|
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "perform/1 when testing re-downloads" do
|
||||||
|
test "sets redownloaded_at on the media_item", %{media_item: media_item} do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id, redownload?: true})
|
||||||
|
media_item = Repo.reload(media_item)
|
||||||
|
|
||||||
|
assert media_item.media_redownloaded_at != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets force_overwrites runner option", %{media_item: media_item} do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
||||||
|
assert :force_overwrites in opts
|
||||||
|
refute :no_force_overwrites in opts
|
||||||
|
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id, force: true})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,21 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "download_for_media_item/3 when testing override options" do
|
||||||
|
test "includes override opts if specified", %{media_item: media_item} do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, opts, _ot, _addl ->
|
||||||
|
refute :force_overwrites in opts
|
||||||
|
assert :no_force_overwrites in opts
|
||||||
|
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
override_opts = [overwrite_behaviour: :no_force_overwrites]
|
||||||
|
|
||||||
|
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item, override_opts)
|
||||||
|
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"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue