Adds SAVE_MEDIA_AFTER_BULK_ACTION (env:
TUBESYNC_SAVE_MEDIA_AFTER_BULK_ACTION, default False). When enabled,
each admin bulk action queues a save_media task per changed item so
flags are re-evaluated without waiting for the next source edit or
indexing run. When disabled (the default) behaviour is unchanged:
re-evaluation happens on the next source edit/index pass.
Requested by tcely in review.
Managing large libraries through the Django admin required editing
every media item individually to toggle 'skip' or 'can download'
(#493). Add the standard admin bulk actions for exactly the two
fields requested there:
- Set / unset 'skip' — also sets manual_skip to match, so a bulk
un-skip is not immediately re-skipped by the filtering logic, and a
bulk skip is treated as the operator's explicit decision
- Set / unset 'can download'
Also adds list_filter for can_download/skip/downloaded so the items
to bulk-edit can actually be narrowed down first.
Uses queryset.update() so a bulk action on thousands of rows is a
single UPDATE, not thousands of model saves.
Tests cover both action pairs and the action registration.
Fixes#493
Match the reviewer design: persist the index response on Metadata keyed
by the videos/streams URL, look those rows up in download_source_images,
and retry with CancelExecution until that data exists. Keep merging
valid stored values so incomplete later responses do not wipe images.
Co-authored-by: Ryan Duguid <ryan@duguid.com.au>
Implements #1550.
index_media's response already carries the channel avatar/banner/
thumbnail URLs, but they were discarded, forcing download_source_images
to run its own separate yt-dlp extraction just for images.
- Source gains avatar_image_url, banner_image_url and
thumbnail_image_url columns plus save_indexed_image_urls(), which
picks the same URLs get_image_info would have picked (avatar_uncropped,
banner_uncropped, tallest remaining thumbnail).
- get_index() persists the URLs from each index response.
- download_source_images() uses the stored URLs when present and only
falls back to a live extraction when none are stored yet.
- use unbuffered reads into a memory view
- cast the memory view for faster operations
- process buffered files first
- switch the file_buffer_pool to `queue.SimpleQueue`
- reduce the number of tasks sent to `ThreadPoolExecutor`
- translate Windows path separators