fix(metadata): auto-download missing source images on profile change or indexing
Add two mechanisms to ensure source images are downloaded: 1. Trigger SourceMetadataStorageWorker when media_profile_id changes, so if a user switches to a profile with download_source_images enabled, the images will be fetched. 2. After successful indexing, check if source images are missing but should exist (profile has download_source_images: true). If so, kick off the metadata storage worker to download them. This fixes issues where sources created before download_source_images was added, or sources that had failed metadata jobs, never got their artwork downloaded.
This commit is contained in:
parent
80ae7ea1ff
commit
50a1ff61f4
5 changed files with 68 additions and 2 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Issues discovered while debugging Jellyfin artwork problems.
|
Issues discovered while debugging Jellyfin artwork problems.
|
||||||
|
|
||||||
## Issue 1: Source images not downloaded for early sources
|
## Issue 1: Source images not downloaded for early sources [COMPLETED]
|
||||||
|
|
||||||
**Problem:** Sources created before `download_source_images` was added to the media profile have empty `poster_filepath`, `fanart_filepath`, and `banner_filepath` in the `sources` table. The `source_metadata` table may have stale references to files that were never actually downloaded.
|
**Problem:** Sources created before `download_source_images` was added to the media profile have empty `poster_filepath`, `fanart_filepath`, and `banner_filepath` in the `sources` table. The `source_metadata` table may have stale references to files that were never actually downloaded.
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ Issues discovered while debugging Jellyfin artwork problems.
|
||||||
2. Downloads missing source images for sources that don't have them
|
2. Downloads missing source images for sources that don't have them
|
||||||
3. Updates both `sources` and `source_metadata` tables
|
3. Updates both `sources` and `source_metadata` tables
|
||||||
|
|
||||||
## Issue 2: Source images not downloaded for some new sources
|
## Issue 2: Source images not downloaded for some new sources [COMPLETED]
|
||||||
|
|
||||||
**Problem:** Some recently added sources have no `source_metadata` entry at all, even though they use a profile with `download_source_images: true`.
|
**Problem:** Some recently added sources have no `source_metadata` entry at all, even though they use a profile with `download_source_images: true`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
|
||||||
alias Pinchflat.SlowIndexing.FileFollowerServer
|
alias Pinchflat.SlowIndexing.FileFollowerServer
|
||||||
alias Pinchflat.Downloading.DownloadOptionBuilder
|
alias Pinchflat.Downloading.DownloadOptionBuilder
|
||||||
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
||||||
|
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
||||||
|
|
||||||
alias Pinchflat.YtDlp.Media, as: YtDlpMedia
|
alias Pinchflat.YtDlp.Media, as: YtDlpMedia
|
||||||
|
|
||||||
|
|
@ -245,6 +246,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
|
||||||
# Updates the source after a successful indexing run. This includes:
|
# Updates the source after a successful indexing run. This includes:
|
||||||
# - Setting `last_indexed_at` to the current time
|
# - Setting `last_indexed_at` to the current time
|
||||||
# - Advancing `download_cutoff_date` to 7 days ago if it's older (or nil)
|
# - Advancing `download_cutoff_date` to 7 days ago if it's older (or nil)
|
||||||
|
# - Kicking off metadata storage if source images are missing but should be downloaded
|
||||||
#
|
#
|
||||||
# Advancing the cutoff date prevents yt-dlp from scanning through months of old videos
|
# Advancing the cutoff date prevents yt-dlp from scanning through months of old videos
|
||||||
# on every index. We use 7 days as a buffer to ensure we don't miss any videos that
|
# on every index. We use 7 days as a buffer to ensure we don't miss any videos that
|
||||||
|
|
@ -260,6 +262,8 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
|
||||||
|> maybe_advance_cutoff_date(source.download_cutoff_date, new_cutoff_date)
|
|> maybe_advance_cutoff_date(source.download_cutoff_date, new_cutoff_date)
|
||||||
|
|
||||||
Sources.update_source(source, update_attrs, run_post_commit_tasks: false)
|
Sources.update_source(source, update_attrs, run_post_commit_tasks: false)
|
||||||
|
|
||||||
|
maybe_kickoff_metadata_storage_for_missing_images(source)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_advance_cutoff_date(attrs, nil, new_cutoff_date) do
|
defp maybe_advance_cutoff_date(attrs, nil, new_cutoff_date) do
|
||||||
|
|
@ -273,4 +277,24 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
|
||||||
attrs
|
attrs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Kicks off metadata storage if source images should be downloaded but are missing.
|
||||||
|
# This handles the case where:
|
||||||
|
# 1. A source was created before download_source_images was enabled on the profile
|
||||||
|
# 2. The source metadata worker failed or was interrupted
|
||||||
|
# 3. The profile's download_source_images setting was later enabled
|
||||||
|
defp maybe_kickoff_metadata_storage_for_missing_images(source) do
|
||||||
|
source = Repo.preload(source, :media_profile)
|
||||||
|
|
||||||
|
if source.media_profile.download_source_images && source_images_missing?(source) do
|
||||||
|
Logger.info("Source #{source.id} is missing images, kicking off metadata storage")
|
||||||
|
SourceMetadataStorageWorker.kickoff_with_task(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp source_images_missing?(source) do
|
||||||
|
is_nil(source.poster_filepath) || is_nil(source.fanart_filepath)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,11 @@ defmodule Pinchflat.Sources do
|
||||||
{_, %{original_url: _}} ->
|
{_, %{original_url: _}} ->
|
||||||
SourceMetadataStorageWorker.kickoff_with_task(source)
|
SourceMetadataStorageWorker.kickoff_with_task(source)
|
||||||
|
|
||||||
|
# If the media_profile_id has changed, re-fetch metadata to potentially
|
||||||
|
# download source images if the new profile has download_source_images enabled
|
||||||
|
{_, %{media_profile_id: _}} ->
|
||||||
|
SourceMetadataStorageWorker.kickoff_with_task(source)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
||||||
alias Pinchflat.Downloading.MediaDownloadWorker
|
alias Pinchflat.Downloading.MediaDownloadWorker
|
||||||
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
alias Pinchflat.SlowIndexing.SlowIndexingHelpers
|
||||||
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
alias Pinchflat.SlowIndexing.MediaCollectionIndexingWorker
|
||||||
|
alias Pinchflat.Metadata.SourceMetadataStorageWorker
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
{:ok, %{source: source_fixture()}}
|
{:ok, %{source: source_fixture()}}
|
||||||
|
|
@ -259,6 +260,32 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpersTest do
|
||||||
assert source.download_cutoff_date == recent_cutoff
|
assert source.download_cutoff_date == recent_cutoff
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "kicks off metadata storage if source images are missing but should be downloaded" do
|
||||||
|
profile = media_profile_fixture(%{download_source_images: true})
|
||||||
|
source = source_fixture(%{media_profile_id: profile.id, poster_filepath: nil, fanart_filepath: nil})
|
||||||
|
|
||||||
|
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
|
assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not kick off metadata storage if source images exist" do
|
||||||
|
source = source_with_metadata_attachments()
|
||||||
|
|
||||||
|
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
|
refute_enqueued(worker: SourceMetadataStorageWorker)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not kick off metadata storage if profile does not download source images" do
|
||||||
|
profile = media_profile_fixture(%{download_source_images: false})
|
||||||
|
source = source_fixture(%{media_profile_id: profile.id, poster_filepath: nil})
|
||||||
|
|
||||||
|
SlowIndexingHelpers.index_and_enqueue_download_for_media_items(source)
|
||||||
|
|
||||||
|
refute_enqueued(worker: SourceMetadataStorageWorker)
|
||||||
|
end
|
||||||
|
|
||||||
test "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)
|
||||||
|
|
|
||||||
|
|
@ -565,6 +565,16 @@ defmodule Pinchflat.SourcesTest do
|
||||||
|
|
||||||
refute_enqueued(worker: SourceMetadataStorageWorker)
|
refute_enqueued(worker: SourceMetadataStorageWorker)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updating will kickoff a metadata storage worker if the media_profile_id changes" do
|
||||||
|
source = source_fixture()
|
||||||
|
new_profile = media_profile_fixture()
|
||||||
|
update_attrs = %{media_profile_id: new_profile.id}
|
||||||
|
|
||||||
|
assert {:ok, %Source{} = source} = Sources.update_source(source, update_attrs)
|
||||||
|
|
||||||
|
assert_enqueued(worker: SourceMetadataStorageWorker, args: %{"id" => source.id})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "update_source/3 when testing media download tasks" do
|
describe "update_source/3 when testing media download tasks" do
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue