Skip to content

Commit ac89594

Browse files
authored
[Enhancement] Add option for a source to only use cookies when needed (#640)
* Updated model with new attribute * Update app logic to use new cookie logic * lots of tests * Updated UI and renamed attribute * Updated tests
1 parent 59f8aa6 commit ac89594

17 files changed

+243
-61
lines changed

lib/pinchflat/downloading/media_downloader.ex

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule Pinchflat.Downloading.MediaDownloader do
99

1010
alias Pinchflat.Repo
1111
alias Pinchflat.Media
12+
alias Pinchflat.Sources
1213
alias Pinchflat.Media.MediaItem
1314
alias Pinchflat.Utils.StringUtils
1415
alias Pinchflat.Metadata.NfoBuilder
@@ -151,7 +152,8 @@ defmodule Pinchflat.Downloading.MediaDownloader do
151152

152153
defp download_with_options(url, item_with_preloads, output_filepath, override_opts) do
153154
{:ok, options} = DownloadOptionBuilder.build(item_with_preloads, override_opts)
154-
use_cookies = item_with_preloads.source.use_cookies
155+
156+
use_cookies = Sources.use_cookies?(item_with_preloads.source, :downloading)
155157
runner_opts = [output_filepath: output_filepath, use_cookies: use_cookies]
156158

157159
case YtDlpMedia.get_downloadable_status(url, use_cookies: use_cookies) do

lib/pinchflat/fast_indexing/fast_indexing_helpers.ex

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
1212
alias Pinchflat.Repo
1313
alias Pinchflat.Media
1414
alias Pinchflat.Tasks
15+
alias Pinchflat.Sources
1516
alias Pinchflat.Sources.Source
1617
alias Pinchflat.FastIndexing.YoutubeRss
1718
alias Pinchflat.FastIndexing.YoutubeApi
@@ -88,12 +89,16 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpers do
8889

8990
defp create_media_item_from_media_id(source, media_id) do
9091
url = "https://www.youtube.com/watch?v=#{media_id}"
92+
# This is set to :metadata instead of :indexing since this happens _after_ the
93+
# actual indexing process. In reality, slow indexing is the only thing that
94+
# should be using :indexing.
95+
should_use_cookies = Sources.use_cookies?(source, :metadata)
9196

9297
command_opts =
9398
[output: DownloadOptionBuilder.build_output_path_for(source)] ++
9499
DownloadOptionBuilder.build_quality_options_for(source)
95100

96-
case YtDlpMedia.get_media_attributes(url, command_opts, use_cookies: source.use_cookies) do
101+
case YtDlpMedia.get_media_attributes(url, command_opts, use_cookies: should_use_cookies) do
97102
{:ok, media_attrs} ->
98103
Media.create_media_item_from_backend_attrs(source, media_attrs)
99104

lib/pinchflat/metadata/metadata_file_helpers.ex

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
99
needed
1010
"""
1111

12+
alias Pinchflat.Sources
1213
alias Pinchflat.Utils.FilesystemUtils
1314

1415
alias Pinchflat.YtDlp.Media, as: YtDlpMedia
@@ -66,7 +67,7 @@ defmodule Pinchflat.Metadata.MetadataFileHelpers do
6667
yt_dlp_filepath = generate_filepath_for(media_item_with_preloads, "thumbnail.%(ext)s")
6768
real_filepath = generate_filepath_for(media_item_with_preloads, "thumbnail.jpg")
6869
command_opts = [output: yt_dlp_filepath]
69-
addl_opts = [use_cookies: media_item_with_preloads.source.use_cookies]
70+
addl_opts = [use_cookies: Sources.use_cookies?(media_item_with_preloads.source, :metadata)]
7071

7172
case YtDlpMedia.download_thumbnail(media_item_with_preloads.original_url, command_opts, addl_opts) do
7273
{:ok, _} -> real_filepath

lib/pinchflat/metadata/source_metadata_storage_worker.ex

+3-2
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
9393
defp determine_series_directory(source) do
9494
output_path = DownloadOptionBuilder.build_output_path_for(source)
9595
runner_opts = [output: output_path]
96-
addl_opts = [use_cookies: source.use_cookies]
96+
addl_opts = [use_cookies: Sources.use_cookies?(source, :metadata)]
9797
{:ok, %{filepath: filepath}} = MediaCollection.get_source_details(source.original_url, runner_opts, addl_opts)
9898

9999
case MetadataFileHelpers.series_directory_from_media_filepath(filepath) do
@@ -113,6 +113,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
113113
defp fetch_metadata_for_source(source) do
114114
tmp_output_path = "#{tmp_directory()}/#{StringUtils.random_string(16)}/source_image.%(ext)S"
115115
base_opts = [convert_thumbnails: "jpg", output: tmp_output_path]
116+
should_use_cookies = Sources.use_cookies?(source, :metadata)
116117

117118
opts =
118119
if source.collection_type == :channel do
@@ -121,7 +122,7 @@ defmodule Pinchflat.Metadata.SourceMetadataStorageWorker do
121122
base_opts ++ [:write_thumbnail, playlist_items: 1]
122123
end
123124

124-
MediaCollection.get_source_metadata(source.original_url, opts, use_cookies: source.use_cookies)
125+
MediaCollection.get_source_metadata(source.original_url, opts, use_cookies: should_use_cookies)
125126
end
126127

127128
defp tmp_directory do

lib/pinchflat/slow_indexing/slow_indexing_helpers.ex

+2-1
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,14 @@ defmodule Pinchflat.SlowIndexing.SlowIndexingHelpers do
132132
{:ok, pid} = FileFollowerServer.start_link()
133133

134134
handler = fn filepath -> setup_file_follower_watcher(pid, filepath, source) end
135+
should_use_cookies = Sources.use_cookies?(source, :indexing)
135136

136137
command_opts =
137138
[output: DownloadOptionBuilder.build_output_path_for(source)] ++
138139
DownloadOptionBuilder.build_quality_options_for(source) ++
139140
build_download_archive_options(source, was_forced)
140141

141-
runner_opts = [file_listener_handler: handler, use_cookies: source.use_cookies]
142+
runner_opts = [file_listener_handler: handler, use_cookies: should_use_cookies]
142143
result = MediaCollection.get_media_attributes_for_collection(source.original_url, command_opts, runner_opts)
143144

144145
FileFollowerServer.stop(pid)

lib/pinchflat/sources/source.ex

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ defmodule Pinchflat.Sources.Source do
2828
series_directory
2929
index_frequency_minutes
3030
fast_index
31-
use_cookies
31+
cookie_behaviour
3232
download_media
3333
last_indexed_at
3434
original_url
@@ -78,7 +78,7 @@ defmodule Pinchflat.Sources.Source do
7878
field :collection_type, Ecto.Enum, values: [:channel, :playlist]
7979
field :index_frequency_minutes, :integer, default: 60 * 24
8080
field :fast_index, :boolean, default: false
81-
field :use_cookies, :boolean, default: false
81+
field :cookie_behaviour, Ecto.Enum, values: [:disabled, :when_needed, :all_operations], default: :disabled
8282
field :download_media, :boolean, default: true
8383
field :last_indexed_at, :utc_datetime
8484
# Only download media items that were published after this date

lib/pinchflat/sources/sources.ex

+15-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ defmodule Pinchflat.Sources do
3232
source.output_path_template_override || media_profile.output_path_template
3333
end
3434

35+
@doc """
36+
Returns a boolean indicating whether or not cookies should be used for a given operation.
37+
38+
Returns boolean()
39+
"""
40+
def use_cookies?(source, operation) when operation in [:indexing, :downloading, :metadata] do
41+
case source.cookie_behaviour do
42+
:disabled -> false
43+
:all_operations -> true
44+
:when_needed -> operation == :indexing
45+
end
46+
end
47+
3548
@doc """
3649
Returns the list of sources. Returns [%Source{}, ...]
3750
"""
@@ -181,9 +194,9 @@ defmodule Pinchflat.Sources do
181194

182195
defp add_source_details_to_changeset(source, changeset) do
183196
original_url = changeset.changes.original_url
184-
use_cookies = Ecto.Changeset.get_field(changeset, :use_cookies)
197+
should_use_cookies = Ecto.Changeset.get_field(changeset, :cookie_behaviour) == :all_operations
185198
# Skipping sleep interval since this is UI blocking and we want to keep this as fast as possible
186-
addl_opts = [use_cookies: use_cookies, skip_sleep_interval: true]
199+
addl_opts = [use_cookies: should_use_cookies, skip_sleep_interval: true]
187200

188201
case MediaCollection.get_source_details(original_url, [], addl_opts) do
189202
{:ok, source_details} ->

lib/pinchflat_web/controllers/sources/source_html.ex

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ defmodule PinchflatWeb.Sources.SourceHTML do
2727
]
2828
end
2929

30+
def friendly_cookie_behaviours do
31+
[
32+
{"Disabled", :disabled},
33+
{"When Needed", :when_needed},
34+
{"All Operations", :all_operations}
35+
]
36+
end
37+
3038
def cutoff_date_presets do
3139
[
3240
{"7 days", compute_date_offset(7)},

lib/pinchflat_web/controllers/sources/source_html/source_form.html.heex

+5-4
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@
8787
/>
8888

8989
<.input
90-
field={f[:use_cookies]}
91-
type="toggle"
92-
label="Use Cookies for Downloading"
93-
help="Uses your YouTube cookies for this source (if configured). Used for downloading private playlists and videos. See docs for important details"
90+
field={f[:cookie_behaviour]}
91+
options={friendly_cookie_behaviours()}
92+
type="select"
93+
label="Cookie Behaviour"
94+
help="Uses your YouTube cookies for this source (if configured). 'When Needed' tries to minimize cookie usage except for certain indexing and downloading tasks. See docs"
9495
/>
9596

9697
<section x-show="advancedMode">

priv/repo/erd.png

488 Bytes
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule Pinchflat.Repo.Migrations.AddCookieBehaviourToSources do
2+
use Ecto.Migration
3+
4+
def change do
5+
alter table(:sources) do
6+
add :cookie_behaviour, :string, null: false, default: "disabled"
7+
end
8+
9+
execute(
10+
"UPDATE sources SET cookie_behaviour = 'all_operations' WHERE use_cookies = TRUE",
11+
"UPDATE sources SET use_cookies = TRUE WHERE cookie_behaviour = 'all_operations'"
12+
)
13+
14+
alter table(:sources) do
15+
remove :use_cookies, :boolean, null: false, default: false
16+
end
17+
end
18+
end

test/pinchflat/downloading/media_downloader_test.exs

+24-3
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,36 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
157157
{:ok, ""}
158158
end)
159159

160-
source = source_fixture(%{use_cookies: true})
160+
source = source_fixture(%{cookie_behaviour: :all_operations})
161+
media_item = media_item_fixture(%{source_id: source.id})
162+
163+
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
164+
end
165+
166+
test "does not set use_cookies if the source uses cookies when needed" do
167+
expect(YtDlpRunnerMock, :run, 3, fn
168+
_url, :get_downloadable_status, _opts, _ot, addl ->
169+
assert {:use_cookies, false} in addl
170+
{:ok, "{}"}
171+
172+
_url, :download, _opts, _ot, addl ->
173+
assert {:use_cookies, false} in addl
174+
{:ok, render_metadata(:media_metadata)}
175+
176+
_url, :download_thumbnail, _opts, _ot, _addl ->
177+
{:ok, ""}
178+
end)
179+
180+
source = source_fixture(%{cookie_behaviour: :when_needed})
161181
media_item = media_item_fixture(%{source_id: source.id})
162182

163183
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
164184
end
165185

166186
test "does not set use_cookies if the source does not use cookies" do
167187
expect(YtDlpRunnerMock, :run, 3, fn
168-
_url, :get_downloadable_status, _opts, _ot, _addl ->
188+
_url, :get_downloadable_status, _opts, _ot, addl ->
189+
assert {:use_cookies, false} in addl
169190
{:ok, "{}"}
170191

171192
_url, :download, _opts, _ot, addl ->
@@ -176,7 +197,7 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
176197
{:ok, ""}
177198
end)
178199

179-
source = source_fixture(%{use_cookies: false})
200+
source = source_fixture(%{cookie_behaviour: :disabled})
180201
media_item = media_item_fixture(%{source_id: source.id})
181202

182203
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)

test/pinchflat/fast_indexing/fast_indexing_helpers_test.exs

+44-28
Original file line numberDiff line numberDiff line change
@@ -104,34 +104,6 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
104104
FastIndexingHelpers.index_and_kickoff_downloads(source)
105105
end
106106

107-
test "sets use_cookies if the source uses cookies" do
108-
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
109-
110-
stub(YtDlpRunnerMock, :run, fn _url, :get_media_attributes, _opts, _ot, addl ->
111-
assert {:use_cookies, true} in addl
112-
113-
{:ok, media_attributes_return_fixture()}
114-
end)
115-
116-
source = source_fixture(%{use_cookies: true})
117-
118-
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
119-
end
120-
121-
test "does not set use_cookies if the source does not use cookies" do
122-
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
123-
124-
stub(YtDlpRunnerMock, :run, fn _url, :get_media_attributes, _opts, _ot, addl ->
125-
assert {:use_cookies, false} in addl
126-
127-
{:ok, media_attributes_return_fixture()}
128-
end)
129-
130-
source = source_fixture(%{use_cookies: false})
131-
132-
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
133-
end
134-
135107
test "does not enqueue a download job if the media item does not match the format rules" do
136108
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
137109

@@ -180,6 +152,50 @@ defmodule Pinchflat.FastIndexing.FastIndexingHelpersTest do
180152
end
181153
end
182154

155+
describe "index_and_kickoff_downloads/1 when testing cookies" do
156+
test "sets use_cookies if the source uses cookies" do
157+
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
158+
159+
stub(YtDlpRunnerMock, :run, fn _url, :get_media_attributes, _opts, _ot, addl ->
160+
assert {:use_cookies, true} in addl
161+
162+
{:ok, media_attributes_return_fixture()}
163+
end)
164+
165+
source = source_fixture(%{cookie_behaviour: :all_operations})
166+
167+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
168+
end
169+
170+
test "does not set use_cookies if the source uses cookies when needed" do
171+
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
172+
173+
stub(YtDlpRunnerMock, :run, fn _url, :get_media_attributes, _opts, _ot, addl ->
174+
assert {:use_cookies, false} in addl
175+
176+
{:ok, media_attributes_return_fixture()}
177+
end)
178+
179+
source = source_fixture(%{cookie_behaviour: :when_needed})
180+
181+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
182+
end
183+
184+
test "does not set use_cookies if the source does not use cookies" do
185+
expect(HTTPClientMock, :get, fn _url -> {:ok, "<yt:videoId>test_1</yt:videoId>"} end)
186+
187+
stub(YtDlpRunnerMock, :run, fn _url, :get_media_attributes, _opts, _ot, addl ->
188+
assert {:use_cookies, false} in addl
189+
190+
{:ok, media_attributes_return_fixture()}
191+
end)
192+
193+
source = source_fixture(%{cookie_behaviour: :disabled})
194+
195+
assert [%MediaItem{}] = FastIndexingHelpers.index_and_kickoff_downloads(source)
196+
end
197+
end
198+
183199
describe "index_and_kickoff_downloads/1 when testing backends" do
184200
test "uses the YouTube API if it is enabled", %{source: source} do
185201
expect(HTTPClientMock, :get, fn url, _headers ->

test/pinchflat/metadata/metadata_file_helpers_test.exs

+21-7
Original file line numberDiff line numberDiff line change
@@ -88,36 +88,50 @@ defmodule Pinchflat.Metadata.MetadataFileHelpersTest do
8888
Helpers.download_and_store_thumbnail_for(media_item)
8989
end
9090

91+
test "returns nil if yt-dlp fails", %{media_item: media_item} do
92+
stub(YtDlpRunnerMock, :run, fn _url, :download_thumbnail, _opts, _ot, _addl -> {:error, "error"} end)
93+
94+
filepath = Helpers.download_and_store_thumbnail_for(media_item)
95+
96+
assert filepath == nil
97+
end
98+
end
99+
100+
describe "download_and_store_thumbnail_for/2 when testing cookie usage" do
91101
test "sets use_cookies if the source uses cookies" do
92102
expect(YtDlpRunnerMock, :run, fn _url, :download_thumbnail, _opts, _ot, addl ->
93103
assert {:use_cookies, true} in addl
94104
{:ok, ""}
95105
end)
96106

97-
source = source_fixture(%{use_cookies: true})
107+
source = source_fixture(%{cookie_behaviour: :all_operations})
98108
media_item = Repo.preload(media_item_fixture(%{source_id: source.id}), :source)
99109

100110
Helpers.download_and_store_thumbnail_for(media_item)
101111
end
102112

103-
test "does not set use_cookies if the source does not use cookies" do
113+
test "does not set use_cookies if the source uses cookies when needed" do
104114
expect(YtDlpRunnerMock, :run, fn _url, :download_thumbnail, _opts, _ot, addl ->
105115
assert {:use_cookies, false} in addl
106116
{:ok, ""}
107117
end)
108118

109-
source = source_fixture(%{use_cookies: false})
119+
source = source_fixture(%{cookie_behaviour: :when_needed})
110120
media_item = Repo.preload(media_item_fixture(%{source_id: source.id}), :source)
111121

112122
Helpers.download_and_store_thumbnail_for(media_item)
113123
end
114124

115-
test "returns nil if yt-dlp fails", %{media_item: media_item} do
116-
stub(YtDlpRunnerMock, :run, fn _url, :download_thumbnail, _opts, _ot, _addl -> {:error, "error"} end)
125+
test "does not set use_cookies if the source does not use cookies" do
126+
expect(YtDlpRunnerMock, :run, fn _url, :download_thumbnail, _opts, _ot, addl ->
127+
assert {:use_cookies, false} in addl
128+
{:ok, ""}
129+
end)
117130

118-
filepath = Helpers.download_and_store_thumbnail_for(media_item)
131+
source = source_fixture(%{cookie_behaviour: :disabled})
132+
media_item = Repo.preload(media_item_fixture(%{source_id: source.id}), :source)
119133

120-
assert filepath == nil
134+
Helpers.download_and_store_thumbnail_for(media_item)
121135
end
122136
end
123137

0 commit comments

Comments
 (0)