[Enhancement] Redownload new media after a delay (#173)
* Added redownload-related columns * Added methods for fetching re-downloadable media items * Filled out redownload worker + tests * Added redownload worker to config.exs cron * Added to UI and README
This commit is contained in:
@@ -31,15 +31,39 @@ defmodule Pinchflat.Media do
|
||||
def list_cullable_media_items do
|
||||
MediaQuery.new()
|
||||
|> MediaQuery.with_media_filepath()
|
||||
|> MediaQuery.with_passed_retention_period()
|
||||
|> MediaQuery.with_no_culling_prevention()
|
||||
|> MediaQuery.where_past_retention_period()
|
||||
|> MediaQuery.where_culling_not_prevented()
|
||||
|> 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
|
||||
upload_date is at least redownload_delay_days ago AND
|
||||
`media_downloaded_at` - `redownload_delay_days` is before the media_item's `upload_date`.
|
||||
This logic grabs media that we've recently downloaded AND is recently uploaded, but
|
||||
doesn't grab media that we've recently downloaded and was uploaded a long time ago.
|
||||
This also makes things work as expected when downloading media from a source for the
|
||||
first time.
|
||||
|
||||
Returns [%MediaItem{}, ...]
|
||||
"""
|
||||
def list_redownloadable_media_items do
|
||||
MediaQuery.new()
|
||||
|> MediaQuery.with_media_downloaded_at()
|
||||
|> MediaQuery.where_download_not_prevented()
|
||||
|> MediaQuery.where_not_culled()
|
||||
|> MediaQuery.where_media_not_redownloaded()
|
||||
|> MediaQuery.where_past_redownload_delay()
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of pending media_items for a given source, where
|
||||
pending means the `media_filepath` is `nil` AND the media_item
|
||||
matches satisfies `MediaQuery.with_media_pending_download`. You
|
||||
matches satisfies `MediaQuery.where_pending_download`. You
|
||||
should really check out that function if you need to know more
|
||||
because it has a lot going on.
|
||||
|
||||
@@ -48,7 +72,7 @@ defmodule Pinchflat.Media do
|
||||
def list_pending_media_items_for(%Source{} = source) do
|
||||
MediaQuery.new()
|
||||
|> MediaQuery.for_source(source)
|
||||
|> MediaQuery.with_media_pending_download()
|
||||
|> MediaQuery.where_pending_download()
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@@ -66,7 +90,7 @@ defmodule Pinchflat.Media do
|
||||
|
||||
MediaQuery.new()
|
||||
|> MediaQuery.with_id(media_item.id)
|
||||
|> MediaQuery.with_media_pending_download()
|
||||
|> MediaQuery.where_pending_download()
|
||||
|> Repo.exists?()
|
||||
end
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ defmodule Pinchflat.Media.MediaItem do
|
||||
# These are user or system controlled fields
|
||||
:prevent_download,
|
||||
:prevent_culling,
|
||||
:culled_at
|
||||
:culled_at,
|
||||
:media_redownloaded_at
|
||||
]
|
||||
# Pretty much all the fields captured at index are required.
|
||||
@required_fields ~w(
|
||||
@@ -61,6 +62,7 @@ defmodule Pinchflat.Media.MediaItem do
|
||||
field :livestream, :boolean, default: false
|
||||
field :short_form_content, :boolean, default: false
|
||||
field :media_downloaded_at, :utc_datetime
|
||||
field :media_redownloaded_at, :utc_datetime
|
||||
field :upload_date, :date
|
||||
field :duration_seconds, :integer
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ defmodule Pinchflat.Media.MediaQuery do
|
||||
# Prefixes:
|
||||
# - for_* - belonging to a certain record
|
||||
# - join_* - for joining on a certain record
|
||||
# - with_* - for filtering based on full, concrete attributes
|
||||
# - with_*, where_* - for filtering based on full, concrete attributes
|
||||
# - matching_* - for filtering based on partial attributes (e.g. LIKE, regex, full-text search)
|
||||
#
|
||||
# Suffixes:
|
||||
@@ -33,24 +33,46 @@ defmodule Pinchflat.Media.MediaQuery do
|
||||
from(mi in query, join: s in assoc(mi, :source), as: :sources)
|
||||
end
|
||||
|
||||
def with_passed_retention_period(query) do
|
||||
def where_past_retention_period(query) do
|
||||
query
|
||||
|> require_assoc(:source)
|
||||
|> where(
|
||||
[mi, source],
|
||||
fragment(
|
||||
"IFNULL(?, 0) > 0 AND DATETIME('now', '-' || ? || ' day') > ?",
|
||||
source.retention_period_days,
|
||||
source.retention_period_days,
|
||||
mi.media_downloaded_at
|
||||
)
|
||||
fragment("""
|
||||
IFNULL(retention_period_days, 0) > 0 AND
|
||||
DATETIME('now', '-' || retention_period_days || ' day') > media_downloaded_at
|
||||
""")
|
||||
)
|
||||
end
|
||||
|
||||
def with_no_culling_prevention(query) do
|
||||
def where_past_redownload_delay(query) do
|
||||
query
|
||||
|> require_assoc(:source)
|
||||
|> require_assoc(:media_profile)
|
||||
|> where(
|
||||
[_mi, _source, _media_profile],
|
||||
# Returns media items where the upload_date is at least redownload_delay_days ago AND
|
||||
# downloaded_at minus the redownload_delay_days is before the upload date
|
||||
fragment("""
|
||||
IFNULL(redownload_delay_days, 0) > 0 AND
|
||||
DATETIME('now', '-' || redownload_delay_days || ' day') > upload_date AND
|
||||
DATETIME(media_downloaded_at, '-' || redownload_delay_days || ' day') < upload_date
|
||||
""")
|
||||
)
|
||||
end
|
||||
|
||||
def where_culling_not_prevented(query) do
|
||||
where(query, [mi], mi.prevent_culling == false)
|
||||
end
|
||||
|
||||
def where_not_culled(query) do
|
||||
where(query, [mi], is_nil(mi.culled_at))
|
||||
end
|
||||
|
||||
def where_media_not_redownloaded(query) do
|
||||
where(query, [mi], is_nil(mi.media_redownloaded_at))
|
||||
end
|
||||
|
||||
def with_id(query, id) do
|
||||
where(query, [mi], mi.id == ^id)
|
||||
end
|
||||
@@ -59,6 +81,10 @@ defmodule Pinchflat.Media.MediaQuery do
|
||||
where(query, [mi], mi.media_id in ^media_ids)
|
||||
end
|
||||
|
||||
def with_media_downloaded_at(query) do
|
||||
where(query, [mi], not is_nil(mi.media_downloaded_at))
|
||||
end
|
||||
|
||||
def with_media_filepath(query) do
|
||||
where(query, [mi], not is_nil(mi.media_filepath))
|
||||
end
|
||||
@@ -73,7 +99,7 @@ defmodule Pinchflat.Media.MediaQuery do
|
||||
|> where([mi, source], is_nil(source.download_cutoff_date) or mi.upload_date >= source.download_cutoff_date)
|
||||
end
|
||||
|
||||
def with_no_prevented_download(query) do
|
||||
def where_download_not_prevented(query) do
|
||||
where(query, [mi], mi.prevent_download == false)
|
||||
end
|
||||
|
||||
@@ -129,9 +155,9 @@ defmodule Pinchflat.Media.MediaQuery do
|
||||
)
|
||||
end
|
||||
|
||||
def with_media_pending_download(query) do
|
||||
def where_pending_download(query) do
|
||||
query
|
||||
|> with_no_prevented_download()
|
||||
|> where_download_not_prevented()
|
||||
|> with_no_media_filepath()
|
||||
|> with_upload_date_after_source_cutoff()
|
||||
|> with_format_matching_profile_preference()
|
||||
|
||||
Reference in New Issue
Block a user