Skip to content

Commit 62214b8

Browse files
authored
[Enhancement] Run fast indexing on source creation and at higher priority (#583)
* Updated default job priorities for downloading queue * Added the ability to set priority to various downloading helpers * Sets sources to fast index on creation
1 parent 704d29d commit 62214b8

12 files changed

+113
-60
lines changed

config/runtime.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ config :pinchflat, Pinchflat.Repo,
4747
config :pinchflat, Oban,
4848
queues: [
4949
default: 10,
50-
fast_indexing: 6,
50+
fast_indexing: yt_dlp_worker_count,
5151
media_collection_indexing: yt_dlp_worker_count,
5252
media_fetching: yt_dlp_worker_count,
5353
remote_metadata: yt_dlp_worker_count,

lib/pinchflat/downloading/downloading_helpers.ex

+7-5
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ defmodule Pinchflat.Downloading.DownloadingHelpers do
2727
2828
Returns :ok
2929
"""
30-
def enqueue_pending_download_tasks(%Source{download_media: true} = source) do
30+
def enqueue_pending_download_tasks(source, job_opts \\ [])
31+
32+
def enqueue_pending_download_tasks(%Source{download_media: true} = source, job_opts) do
3133
source
3234
|> Media.list_pending_media_items_for()
33-
|> Enum.each(&MediaDownloadWorker.kickoff_with_task/1)
35+
|> Enum.each(&MediaDownloadWorker.kickoff_with_task(&1, %{}, job_opts))
3436
end
3537

36-
def enqueue_pending_download_tasks(%Source{download_media: false}) do
38+
def enqueue_pending_download_tasks(%Source{download_media: false}, _job_opts) do
3739
:ok
3840
end
3941

@@ -55,13 +57,13 @@ defmodule Pinchflat.Downloading.DownloadingHelpers do
5557
5658
Returns {:ok, %Task{}} | {:error, :should_not_download} | {:error, any()}
5759
"""
58-
def kickoff_download_if_pending(%MediaItem{} = media_item) do
60+
def kickoff_download_if_pending(%MediaItem{} = media_item, job_opts \\ []) do
5961
media_item = Repo.preload(media_item, :source)
6062

6163
if media_item.source.download_media && Media.pending_download?(media_item) do
6264
Logger.info("Kicking off download for media item ##{media_item.id} (#{media_item.media_id})")
6365

64-
MediaDownloadWorker.kickoff_with_task(media_item)
66+
MediaDownloadWorker.kickoff_with_task(media_item, %{}, job_opts)
6567
else
6668
{:error, :should_not_download}
6769
end

lib/pinchflat/downloading/media_download_worker.ex

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
33

44
use Oban.Worker,
55
queue: :media_fetching,
6+
priority: 5,
67
unique: [period: :infinity, states: [:available, :scheduled, :retryable, :executing]],
78
tags: ["media_item", "media_fetching", "show_in_dashboard"]
89

lib/pinchflat/fast_indexing/fast_indexing_helpers.ex

+5-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
4040
Returns [%MediaItem{}] where each item is a new media item that was created _but not necessarily
4141
downloaded_.
4242
"""
43-
def kickoff_download_tasks_from_youtube_rss_feed(%Source{} = source) do
43+
def index_and_kickoff_downloads(%Source{} = source) do
4444
# The media_profile is needed to determine the quality options to _then_ determine a more
4545
# accurate predicted filepath
4646
source = Repo.preload(source, [:media_profile])
@@ -53,6 +53,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
5353
Enum.map(new_media_ids, fn media_id ->
5454
case create_media_item_from_media_id(source, media_id) do
5555
{:ok, media_item} ->
56+
DownloadingHelpers.kickoff_download_if_pending(media_item, priority: 0)
5657
media_item
5758

5859
err ->
@@ -61,7 +62,9 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
6162
end
6263
end)
6364

64-
DownloadingHelpers.enqueue_pending_download_tasks(source)
65+
# Pick up any stragglers. Intentionally has a lower priority than the per-media item
66+
# kickoff above
67+
DownloadingHelpers.enqueue_pending_download_tasks(source, priority: 1)
6568

6669
Enum.filter(maybe_new_media_items, & &1)
6770
end

lib/pinchflat/fast_indexing/fast_indexing_worker.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ defmodule Pinchflat.FastIndexing.FastIndexingWorker do
3838
3939
Order of operations:
4040
1. FastIndexingWorker (this module) periodically checks the YouTube RSS feed for new media.
41-
with `FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed`
42-
2. If the above `kickoff_download_tasks_from_youtube_rss_feed` finds new media items in the RSS feed,
41+
with `FastIndexingHelpers.index_and_kickoff_downloads`
42+
2. If the above `index_and_kickoff_downloads` finds new media items in the RSS feed,
4343
it indexes them with a yt-dlp call to create the media item records then kicks off downloading
4444
tasks (MediaDownloadWorker) for any new media items _that should be downloaded_.
4545
3. Once downloads are kicked off, this worker sends a notification to the apprise server if applicable
@@ -67,7 +67,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingWorker do
6767

6868
new_media_items =
6969
source
70-
|> FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed()
70+
|> FastIndexingHelpers.index_and_kickoff_downloads()
7171
|> Enum.filter(&Media.pending_download?(&1))
7272

7373
if source.download_media do

lib/pinchflat/slow_indexing/slow_indexing_helpers.ex

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
3939
def kickoff_indexing_task(%Source{} = source, job_args \\ %{}, job_opts \\ []) do
4040
job_offset_seconds = if job_args[:force], do: 0, else: calculate_job_offset_seconds(source)
4141

42-
Tasks.delete_pending_tasks_for(source, "FastIndexingWorker")
4342
Tasks.delete_pending_tasks_for(source, "MediaCollectionIndexingWorker", include_executing: true)
4443

4544
MediaCollectionIndexingWorker.kickoff_with_task(source, job_args, job_opts ++ [schedule_in: job_offset_seconds])

lib/pinchflat/sources/sources.ex

+4
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ defmodule Pinchflat.Sources do
300300
%{__meta__: %{state: :built}} ->
301301
SlowIndexingHelpers.kickoff_indexing_task(source)
302302

303+
if Ecto.Changeset.get_field(changeset, :fast_index) do
304+
FastIndexingHelpers.kickoff_indexing_task(source)
305+
end
306+
303307
# If the record has been persisted, only run indexing if the
304308
# indexing frequency has been changed and is now greater than 0
305309
%{__meta__: %{state: :loaded}} ->

test/pinchflat/downloading/downloading_helpers_test.exs

+23-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
1010
alias Pinchflat.Downloading.MediaDownloadWorker
1111

1212
describe "enqueue_pending_download_tasks/1" do
13-
test "it enqueues a job for each pending media item" do
13+
test "enqueues a job for each pending media item" do
1414
source = source_fixture()
1515
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
1616

@@ -19,7 +19,7 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
1919
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
2020
end
2121

22-
test "it does not enqueue a job for media items with a filepath" do
22+
test "does not enqueue a job for media items with a filepath" do
2323
source = source_fixture()
2424
_media_item = media_item_fixture(source_id: source.id, media_filepath: "some/filepath.mp4")
2525

@@ -28,7 +28,7 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
2828
refute_enqueued(worker: MediaDownloadWorker)
2929
end
3030

31-
test "it attaches a task to each enqueued job" do
31+
test "attaches a task to each enqueued job" do
3232
source = source_fixture()
3333
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
3434

@@ -39,25 +39,34 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
3939
assert [_] = Tasks.list_tasks_for(media_item)
4040
end
4141

42-
test "it does not create a job if the source is set to not download" do
42+
test "does not create a job if the source is set to not download" do
4343
source = source_fixture(download_media: false)
4444

4545
assert :ok = DownloadingHelpers.enqueue_pending_download_tasks(source)
4646

4747
refute_enqueued(worker: MediaDownloadWorker)
4848
end
4949

50-
test "it does not attach tasks if the source is set to not download" do
50+
test "does not attach tasks if the source is set to not download" do
5151
source = source_fixture(download_media: false)
5252
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
5353

5454
assert :ok = DownloadingHelpers.enqueue_pending_download_tasks(source)
5555
assert [] = Tasks.list_tasks_for(media_item)
5656
end
57+
58+
test "can pass job options" do
59+
source = source_fixture()
60+
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
61+
62+
assert :ok = DownloadingHelpers.enqueue_pending_download_tasks(source, priority: 1)
63+
64+
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id}, priority: 1)
65+
end
5766
end
5867

5968
describe "dequeue_pending_download_tasks/1" do
60-
test "it deletes all pending tasks for a source's media items" do
69+
test "deletes all pending tasks for a source's media items" do
6170
source = source_fixture()
6271
media_item = media_item_fixture(source_id: source.id, media_filepath: nil)
6372

@@ -109,6 +118,14 @@ defmodule Pinchflat.Downloading.DownloadingHelpersTest do
109118

110119
refute_enqueued(worker: MediaDownloadWorker)
111120
end
121+
122+
test "can pass job options" do
123+
media_item = media_item_fixture(media_filepath: nil)
124+
125+
assert {:ok, _} = DownloadingHelpers.kickoff_download_if_pending(media_item, priority: 1)
126+
127+
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id}, priority: 1)
128+
end
112129
end
113130

114131
describe "kickoff_redownload_for_existing_media/1" do

test/pinchflat/downloading/media_download_worker_test.exs

+11-4
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,20 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
4646
assert_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id, "force" => true})
4747
end
4848

49-
test "can be called with additional job options", %{media_item: media_item} do
50-
job_opts = [max_attempts: 5]
49+
test "has a priority of 5 by default", %{media_item: media_item} do
50+
assert {:ok, _} = MediaDownloadWorker.kickoff_with_task(media_item)
51+
52+
[job] = all_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
5153

52-
assert {:ok, _} = MediaDownloadWorker.kickoff_with_task(media_item, %{}, job_opts)
54+
assert job.priority == 5
55+
end
56+
57+
test "priority can be set", %{media_item: media_item} do
58+
assert {:ok, _} = MediaDownloadWorker.kickoff_with_task(media_item, %{}, priority: 0)
5359

5460
[job] = all_enqueued(worker: MediaDownloadWorker, args: %{"id" => media_item.id})
55-
assert job.max_attempts == 5
61+
62+
assert job.priority == 0
5663
end
5764
end
5865

test/pinchflat/fast_indexing/fast_indexing_helpers_test.exs

+30-18
Original file line numberDiff line numberDiff line change
@@ -38,44 +38,56 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
3838
end
3939
end
4040

41-
describe "kickoff_download_tasks_from_youtube_rss_feed/1" do
42-
test "enqueues a new worker for each new media_id in the source's RSS feed", %{source: source} do
41+
describe "index_and_kickoff_downloads/1" do
42+
test "enqueues a worker for each new media_id in the source's RSS feed", %{source: source} do
4343
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
4444

45-
assert [media_item] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
45+
assert [media_item] = FastIndexingHelpers.index_and_kickoff_downloads(source)
4646

4747
assert [worker] = all_enqueued(worker: MediaDownloadWorker)
4848
assert worker.args["id"] == media_item.id
49+
assert worker.priority == 0
4950
end
5051

5152
test "does not enqueue a new worker for the source's media IDs we already know about", %{source: source} do
5253
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
5354
media_item_fixture(source_id: source.id, media_id: "test_1")
5455

55-
assert [] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
56+
assert [] = FastIndexingHelpers.index_and_kickoff_downloads(source)
5657

5758
refute_enqueued(worker: MediaDownloadWorker)
5859
end
5960

61+
test "kicks off a download task for all pending media but at a lower priority", %{source: source} do
62+
pending_item = media_item_fixture(source_id: source.id, media_filepath: nil)
63+
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
64+
65+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
66+
67+
assert [worker_1, _worker_2] = all_enqueued(worker: MediaDownloadWorker)
68+
assert worker_1.args["id"] == pending_item.id
69+
assert worker_1.priority == 1
70+
end
71+
6072
test "returns the found media items", %{source: source} do
6173
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
6274

63-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
75+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
6476
end
6577

6678
test "does not enqueue a download job if the source does not allow it" do
6779
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
6880
source = source_fixture(%{download_media: false})
6981

70-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
82+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
7183

7284
refute_enqueued(worker: MediaDownloadWorker)
7385
end
7486

7587
test "creates a download task record", %{source: source} do
7688
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
7789

78-
assert [media_item] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
90+
assert [media_item] = FastIndexingHelpers.index_and_kickoff_downloads(source)
7991

8092
assert [_] = Tasks.list_tasks_for(media_item, "MediaDownloadWorker")
8193
end
@@ -89,7 +101,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
89101
{:ok, media_attributes_return_fixture()}
90102
end)
91103

92-
FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
104+
FastIndexingHelpers.index_and_kickoff_downloads(source)
93105
end
94106

95107
test "sets use_cookies if the source uses cookies" do
@@ -103,7 +115,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
103115

104116
source = source_fixture(%{use_cookies: true})
105117

106-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
118+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
107119
end
108120

109121
test "does not set use_cookies if the source does not use cookies" do
@@ -117,7 +129,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
117129

118130
source = source_fixture(%{use_cookies: false})
119131

120-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
132+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
121133
end
122134

123135
test "does not enqueue a download job if the media item does not match the format rules" do
@@ -142,7 +154,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
142154
{:ok, output}
143155
end)
144156

145-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
157+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
146158

147159
refute_enqueued(worker: MediaDownloadWorker)
148160
end
@@ -154,7 +166,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
154166
{:ok, "{}"}
155167
end)
156168

157-
assert [] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
169+
assert [] = FastIndexingHelpers.index_and_kickoff_downloads(source)
158170
end
159171

160172
test "does not blow up if a media item causes a yt-dlp error", %{source: source} do
@@ -164,11 +176,11 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
164176
{:error, "message", 1}
165177
end)
166178

167-
assert [] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
179+
assert [] = FastIndexingHelpers.index_and_kickoff_downloads(source)
168180
end
169181
end
170182

171-
describe "kickoff_download_tasks_from_youtube_rss_feed/1 when testing backends" do
183+
describe "index_and_kickoff_downloads/1 when testing backends" do
172184
test "uses the YouTube API if it is enabled", %{source: source} do
173185
expect(HTTPClientMock, :get, fn url, _headers ->
174186
assert url =~ "https://youtube.googleapis.com/youtube/v3/playlistItems"
@@ -178,7 +190,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
178190

179191
Settings.set(youtube_api_key: "test_key")
180192

181-
assert [] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
193+
assert [] = FastIndexingHelpers.index_and_kickoff_downloads(source)
182194
end
183195

184196
test "the YouTube API creates records as expected", %{source: source} do
@@ -188,7 +200,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
188200

189201
Settings.set(youtube_api_key: "test_key")
190202

191-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
203+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
192204
end
193205

194206
test "RSS is used as a backup if the API fails", %{source: source} do
@@ -197,7 +209,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
197209

198210
Settings.set(youtube_api_key: "test_key")
199211

200-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
212+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
201213
end
202214

203215
test "RSS is used if the API is not enabled", %{source: source} do
@@ -209,7 +221,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
209221

210222
Settings.set(youtube_api_key: nil)
211223

212-
assert [%MediaItem{}] = FastIndexingHelpers.kickoff_download_tasks_from_youtube_rss_feed(source)
224+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
213225
end
214226
end
215227
end

0 commit comments

Comments
 (0)