video automations: rename Download->Process + group into a clear pipeline
- 'Download X Wishlist' -> 'Process X Wishlist' everywhere (label + action_type): matches the music side's 'process_wishlist', the already-process youtube action, and reads right (movie/episode do search+pick+download, not just download). action_types video_download_movie/episode_wishlist -> video_process_*. - group the new automations as a two-stage pipeline in the seed list + builder palette: Stage 1 SCANS (people/channels/playlists) fill the wishlist, Stage 2 PROCESSORS (movie/episode/youtube) drain it. icons/labels/drift test updated. brand-new this session so no migration needed. 337 automation tests green.
This commit is contained in:
parent
b404420c76
commit
7d7d242e5d
7 changed files with 56 additions and 58 deletions
|
|
@ -265,6 +265,8 @@ ACTIONS: list[dict] = [
|
|||
"config_fields": [
|
||||
{"key": "prune_ended", "type": "checkbox", "label": "Also remove ended/canceled shows from the watchlist", "default": True}
|
||||
]},
|
||||
# ── Watchlist → Wishlist pipeline ────────────────────────────────────────
|
||||
# Stage 1 — scans that FILL the wishlist from what you follow.
|
||||
{"type": "video_scan_watchlist_people", "label": "Scan Watchlist People", "icon": "users", "scope": "video",
|
||||
"description": "For everyone you follow on the watchlist, wishlist every movie they acted in or directed that you don't already own — the whole back catalog plus anything upcoming (kept as 'monitored' until it's released). First run backlogs everything; later runs are fast.", "available": True},
|
||||
{"type": "video_scan_watchlist_channels", "label": "Scan Watchlist Channels", "icon": "youtube", "scope": "video",
|
||||
|
|
@ -274,17 +276,18 @@ ACTIONS: list[dict] = [
|
|||
]},
|
||||
{"type": "video_scan_watchlist_playlists", "label": "Scan Watchlist Playlists", "icon": "list", "scope": "video",
|
||||
"description": "For every YouTube playlist you follow, wishlist its videos you don't have yet (and anything later added to it) — mirrors the whole list. Each playlist becomes its own show in the library (playlist-as-show). Shorts excluded. Pair with a schedule.", "available": True},
|
||||
{"type": "video_download_movie_wishlist", "label": "Download Movie Wishlist", "icon": "download", "scope": "video",
|
||||
# Stage 2 — processors that DRAIN the wishlist by downloading.
|
||||
{"type": "video_process_movie_wishlist", "label": "Process Movie Wishlist", "icon": "download", "scope": "video",
|
||||
"description": "Auto-grab your wished, released MOVIES from Soulseek: searches each, picks the best release per your quality profile, and downloads it (the monitor finishes + organises it). Skips unreleased ('monitored') ones and any already downloading. Needs the Movie library folder + slskd set. Pair with an hourly schedule.", "available": True,
|
||||
"config_fields": [
|
||||
{"key": "max_concurrent", "type": "number", "label": "Max simultaneous searches", "default": 3, "min": 1}
|
||||
]},
|
||||
{"type": "video_download_episode_wishlist", "label": "Download Episode Wishlist", "icon": "download", "scope": "video",
|
||||
{"type": "video_process_episode_wishlist", "label": "Process Episode Wishlist", "icon": "download", "scope": "video",
|
||||
"description": "Auto-grab your wished EPISODES from Soulseek: searches each, picks the best release per your quality profile, and downloads it. Fed by the 'Wishlist Today's Airings' scan. Needs the TV library folder + slskd set. Pair with an hourly schedule.", "available": True,
|
||||
"config_fields": [
|
||||
{"key": "max_concurrent", "type": "number", "label": "Max simultaneous searches", "default": 3, "min": 1}
|
||||
]},
|
||||
{"type": "video_process_youtube_wishlist", "label": "Download YouTube Wishlist", "icon": "download", "scope": "video",
|
||||
{"type": "video_process_youtube_wishlist", "label": "Process YouTube Wishlist", "icon": "download", "scope": "video",
|
||||
"description": "Download wished YouTube videos (yt-dlp), organised as a Plex 'TV by date' show (channel/year/date). Queues the WHOLE wishlist — the setting only limits how many download at the same time; each finished one starts the next, so it all drains. A completed download leaves the wishlist. Needs the YouTube library folder set on Settings → Downloads.", "available": True,
|
||||
"config_fields": [
|
||||
{"key": "max_concurrent", "type": "number", "label": "Max simultaneous downloads", "default": 3, "min": 1}
|
||||
|
|
|
|||
|
|
@ -232,41 +232,40 @@ def register_all(deps: AutomationDeps) -> None:
|
|||
'video_add_airing_episodes',
|
||||
lambda config: auto_video_add_airing_episodes(config, deps),
|
||||
)
|
||||
# Watchlist-people scan: wishlist every un-owned movie the followed people acted in or
|
||||
# directed (back catalog + upcoming).
|
||||
# ── Watchlist → Wishlist pipeline ─────────────────────────────────────────
|
||||
# Stage 1 — SCANS that fill the wishlist from what you follow.
|
||||
# People: wishlist every un-owned movie followed actors/directors made (catalog + upcoming).
|
||||
engine.register_action_handler(
|
||||
'video_scan_watchlist_people',
|
||||
lambda config: auto_video_scan_watchlist_people(config, deps),
|
||||
)
|
||||
# Watchlist-channels scan: wishlist new long-form uploads from followed YouTube
|
||||
# channels (forward-looking + last-N safety net).
|
||||
# Channels: new long-form uploads from followed YouTube channels (forward + last-N net).
|
||||
engine.register_action_handler(
|
||||
'video_scan_watchlist_channels',
|
||||
lambda config: auto_video_scan_watchlist_channels(config, deps),
|
||||
)
|
||||
# Watchlist-playlists scan: mirror followed YouTube playlists into the wishlist
|
||||
# (whole list + new additions; playlist-as-show).
|
||||
# Playlists: mirror followed YouTube playlists (whole list + new additions; playlist-as-show).
|
||||
engine.register_action_handler(
|
||||
'video_scan_watchlist_playlists',
|
||||
lambda config: auto_video_scan_watchlist_playlists(config, deps),
|
||||
)
|
||||
# Drain side: enqueue wished YouTube videos into the download queue (yt-dlp worker).
|
||||
# Stage 2 — PROCESSORS that drain the wishlist by downloading. Movie/episode go through
|
||||
# slskd (search → pick best → grab); the guard skips an hourly tick while a drain is still
|
||||
# working. YouTube goes through yt-dlp (queue all, a few concurrent).
|
||||
engine.register_action_handler(
|
||||
'video_process_youtube_wishlist',
|
||||
lambda config: auto_video_process_youtube_wishlist(config, deps),
|
||||
)
|
||||
# Soulseek drain: auto-grab wished movies / episodes (search → pick best → download).
|
||||
# The guard skips an hourly tick while a drain is still working (searches are slow).
|
||||
engine.register_action_handler(
|
||||
'video_download_movie_wishlist',
|
||||
'video_process_movie_wishlist',
|
||||
lambda config: auto_video_process_wishlist(config, deps, media_type='movie'),
|
||||
lambda: is_running('movie'),
|
||||
)
|
||||
engine.register_action_handler(
|
||||
'video_download_episode_wishlist',
|
||||
'video_process_episode_wishlist',
|
||||
lambda config: auto_video_process_wishlist(config, deps, media_type='episode'),
|
||||
lambda: is_running('episode'),
|
||||
)
|
||||
engine.register_action_handler(
|
||||
'video_process_youtube_wishlist',
|
||||
lambda config: auto_video_process_youtube_wishlist(config, deps),
|
||||
)
|
||||
|
||||
# Progress + history callbacks: the engine invokes these around
|
||||
# each handler run. Lift the closures from
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Automation handlers: ``video_download_movie_wishlist`` / ``video_download_episode_wishlist``.
|
||||
"""Automation handlers: ``video_process_movie_wishlist`` / ``video_process_episode_wishlist``.
|
||||
|
||||
The Soulseek counterpart of the YouTube wishlist drain — the piece that finally makes the
|
||||
people/airing scans pay off. For wished, RELEASED movies (and aired episodes) it searches
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Organisation: the playlist becomes its own "show" (playlist-as-show) — its vid
|
|||
wishlisted under the playlist's title, so the download worker files them as
|
||||
``Playlist Name / Season YEAR / Playlist Name - DATE - Title`` (matches the ytdl-sub
|
||||
``tv_show_name``-on-a-playlist convention). All other plumbing is shared with channels: the
|
||||
same wishlist rows, the same "Download YouTube Wishlist" drain, quality + org template.
|
||||
same wishlist rows, the same "Process YouTube Wishlist" drain, quality + org template.
|
||||
|
||||
Edge: a video in both a followed channel AND a followed playlist is wishlisted once (dedup
|
||||
by video id); whichever scan touched it last sets its show-name. Accepted.
|
||||
|
|
|
|||
|
|
@ -239,67 +239,62 @@ SYSTEM_AUTOMATIONS = [
|
|||
'action_config': {'mode': 'deep', 'media_type': 'movie'},
|
||||
'owned_by': 'video',
|
||||
},
|
||||
# Watchlist scans — keep the wishlist fed from the people + channels you follow.
|
||||
# People: daily (filmographies change slowly) at 03:00, after the airing/deep-scan
|
||||
# jobs so they don't overlap. No-ops cleanly if you follow nobody.
|
||||
# ── Watchlist → Wishlist pipeline ─────────────────────────────────────────
|
||||
# Stage 1: SCANS that FILL the wishlist from what you follow. (The airing-episodes
|
||||
# scan above is the show equivalent.) All no-op cleanly if you follow nobody.
|
||||
{
|
||||
'name': 'Auto-Scan Watchlist People',
|
||||
'trigger_type': 'daily_time',
|
||||
'trigger_config': {'time': '03:00'},
|
||||
'name': 'Auto-Scan Watchlist People', # followed actors/directors → wished movies
|
||||
'trigger_type': 'daily_time', # daily; filmographies change slowly
|
||||
'trigger_config': {'time': '03:00'}, # after airing/deep-scan jobs, no overlap
|
||||
'action_type': 'video_scan_watchlist_people',
|
||||
'owned_by': 'video',
|
||||
},
|
||||
# Channels: every 6h (YouTube posts at all hours). No-ops cleanly if you follow none.
|
||||
{
|
||||
'name': 'Auto-Scan Watchlist Channels',
|
||||
'name': 'Auto-Scan Watchlist Channels', # followed YouTube channels → wished videos
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 6, 'unit': 'hours'},
|
||||
'trigger_config': {'interval': 6, 'unit': 'hours'}, # YouTube posts at all hours
|
||||
'action_type': 'video_scan_watchlist_channels',
|
||||
'action_config': {'backfill_count': 10},
|
||||
'initial_delay': 1200,
|
||||
'owned_by': 'video',
|
||||
},
|
||||
# Playlists: every 6h, mirror the whole list (playlist-as-show). No-ops if you follow none.
|
||||
{
|
||||
'name': 'Auto-Scan Watchlist Playlists',
|
||||
'name': 'Auto-Scan Watchlist Playlists', # followed playlists (mirror-all) → wished videos
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 6, 'unit': 'hours'},
|
||||
'action_type': 'video_scan_watchlist_playlists',
|
||||
'initial_delay': 1320,
|
||||
'owned_by': 'video',
|
||||
},
|
||||
# Drain side: download wished YouTube videos hourly (queues the whole wishlist, runs a
|
||||
# few at a time). Skips quietly until a YouTube library folder is set.
|
||||
# Stage 2: PROCESSORS that DRAIN the wishlist by downloading. Hourly; each skips
|
||||
# quietly until its library folder (+ slskd, for movie/episode) is configured.
|
||||
{
|
||||
'name': 'Auto-Download YouTube Wishlist',
|
||||
'name': 'Auto-Process Movie Wishlist', # wished movies → slskd search/pick/grab
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 1, 'unit': 'hours'},
|
||||
'action_type': 'video_process_youtube_wishlist',
|
||||
'action_config': {'max_concurrent': 3},
|
||||
'initial_delay': 1500,
|
||||
'owned_by': 'video',
|
||||
},
|
||||
# Soulseek drains: auto-grab wished movies / episodes hourly (search → pick best →
|
||||
# download). A guard skips the tick while a drain is still working. Skip quietly until
|
||||
# the library folder + slskd are set.
|
||||
{
|
||||
'name': 'Auto-Download Movie Wishlist',
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 1, 'unit': 'hours'},
|
||||
'action_type': 'video_download_movie_wishlist',
|
||||
'action_type': 'video_process_movie_wishlist',
|
||||
'action_config': {'max_concurrent': 3},
|
||||
'initial_delay': 1620,
|
||||
'owned_by': 'video',
|
||||
},
|
||||
{
|
||||
'name': 'Auto-Download Episode Wishlist',
|
||||
'name': 'Auto-Process Episode Wishlist', # wished episodes → slskd search/pick/grab
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 1, 'unit': 'hours'},
|
||||
'action_type': 'video_download_episode_wishlist',
|
||||
'action_type': 'video_process_episode_wishlist',
|
||||
'action_config': {'max_concurrent': 3},
|
||||
'initial_delay': 1740,
|
||||
'owned_by': 'video',
|
||||
},
|
||||
{
|
||||
'name': 'Auto-Process YouTube Wishlist', # wished YouTube videos → yt-dlp download
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 1, 'unit': 'hours'},
|
||||
'action_type': 'video_process_youtube_wishlist',
|
||||
'action_config': {'max_concurrent': 3},
|
||||
'initial_delay': 1500,
|
||||
'owned_by': 'video',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ EXPECTED_ACTION_NAMES = frozenset({
|
|||
'video_scan_watchlist_people',
|
||||
'video_scan_watchlist_channels',
|
||||
'video_scan_watchlist_playlists',
|
||||
'video_process_movie_wishlist',
|
||||
'video_process_episode_wishlist',
|
||||
'video_process_youtube_wishlist',
|
||||
'video_download_movie_wishlist',
|
||||
'video_download_episode_wishlist',
|
||||
'video_clean_search_history',
|
||||
'video_clean_completed_downloads',
|
||||
'video_full_cleanup',
|
||||
|
|
@ -83,8 +83,8 @@ EXPECTED_GUARDED_ACTIONS = frozenset({
|
|||
'deep_scan_library',
|
||||
'run_duplicate_cleaner',
|
||||
'start_quality_scan',
|
||||
'video_download_movie_wishlist',
|
||||
'video_download_episode_wishlist',
|
||||
'video_process_movie_wishlist',
|
||||
'video_process_episode_wishlist',
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1777,8 +1777,9 @@ const _autoIcons = {
|
|||
video_scan_library: '\uD83C\uDFAC', video_scan_server: '\uD83D\uDD04', video_update_database: '\uD83D\uDDC4\uFE0F',
|
||||
video_add_airing_episodes: '\uD83D\uDCFA', video_deep_scan_movies: '\uD83C\uDFAC', video_deep_scan_tv: '\uD83D\uDCFA',
|
||||
video_scan_watchlist_people: '\uD83C\uDFAD', video_scan_watchlist_channels: '\uD83D\uDCE1',
|
||||
video_scan_watchlist_playlists: '\uD83C\uDFB5', video_process_youtube_wishlist: '\u2B07\uFE0F',
|
||||
video_download_movie_wishlist: '\uD83C\uDFAC', video_download_episode_wishlist: '\uD83D\uDCFA',
|
||||
video_scan_watchlist_playlists: '\uD83C\uDFB5',
|
||||
video_process_movie_wishlist: '\uD83C\uDFAC', video_process_episode_wishlist: '\uD83D\uDCFA',
|
||||
video_process_youtube_wishlist: '\u2B07\uFE0F',
|
||||
};
|
||||
|
||||
// --- Inspiration Templates ---
|
||||
|
|
@ -3376,9 +3377,9 @@ function _autoFormatAction(type) {
|
|||
video_scan_watchlist_people: 'Scan Watchlist People',
|
||||
video_scan_watchlist_channels: 'Scan Watchlist Channels',
|
||||
video_scan_watchlist_playlists: 'Scan Watchlist Playlists',
|
||||
video_process_youtube_wishlist: 'Download YouTube Wishlist',
|
||||
video_download_movie_wishlist: 'Download Movie Wishlist',
|
||||
video_download_episode_wishlist: 'Download Episode Wishlist',
|
||||
video_process_movie_wishlist: 'Process Movie Wishlist',
|
||||
video_process_episode_wishlist: 'Process Episode Wishlist',
|
||||
video_process_youtube_wishlist: 'Process YouTube Wishlist',
|
||||
};
|
||||
return labels[type] || type || 'Unknown';
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue