mirror of
https://github.com/alexta69/metube.git
synced 2026-09-21 13:35:01 +00:00
feat: let a subscription carry clip bounds (closes #1049)
A subscription already carries every other download option and applies it to
each video it queues. Clip bounds were the one exception: 4f83174 added them
for one-off downloads and carved subscriptions out, rejecting the fields in the
subscribe route and stripping them in the UI before the request was built. So
the fields sat visible in the shared advanced-options panel while quietly doing
nothing for a subscription.
Carry them like the rest: two fields on SubscriptionInfo, threaded through the
check flow to add_entry. Stored records that predate the fields take the
defaults, since _from_stored filters by field name.
One thing does not carry over. parse_download_options reads a YouTube t=
timestamp from the URL and turns it into a clip start, which is right when you
paste a link to a moment in a video you want. A subscription URL is a channel
or a playlist, so a timestamp left on it says nothing about the videos that
feed will yield, and honouring it would silently truncate every future
download. The subscribe route therefore takes clip bounds only when the caller
sent the fields explicitly; the t= param is still stripped from the stored URL.
Worth knowing when using this: the range is a fixed offset applied to every
video, so it suits feeds with a consistent shape - a standing intro, a fixed
sponsor read - and will cut in the wrong place on a feed without one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-3
@@ -917,9 +917,6 @@ async def subscribe(request):
|
||||
raise web.HTTPBadRequest(reason='check_interval_minutes must be an integer') from exc
|
||||
if cic < 1:
|
||||
raise web.HTTPBadRequest(reason='check_interval_minutes must be at least 1')
|
||||
if o.get('clip_start') is not None or o.get('clip_end') is not None:
|
||||
raise web.HTTPBadRequest(reason='clip options are not supported for subscriptions')
|
||||
|
||||
try:
|
||||
skip_subscriber_only = coerce_optional_bool(
|
||||
post.get('skip_subscriber_only'),
|
||||
@@ -929,6 +926,19 @@ async def subscribe(request):
|
||||
except ValueError as exc:
|
||||
raise web.HTTPBadRequest(reason=str(exc)) from exc
|
||||
|
||||
# A t= timestamp in the URL means "start playing here" and parse_download_options
|
||||
# turns it into a clip start, which is right for a one-off download of that video.
|
||||
# A subscription URL is a channel or playlist, so a timestamp left on it says
|
||||
# nothing about the videos it will yield — honour clip fields only when the
|
||||
# caller supplied them explicitly, rather than silently clipping every future
|
||||
# download. The t= param is still stripped from the stored URL.
|
||||
clip_given = (
|
||||
_clip_field_provided_in_post(post.get('clip_start'))
|
||||
or _clip_field_provided_in_post(post.get('clip_end'))
|
||||
)
|
||||
sub_clip_start = o['clip_start'] if clip_given else None
|
||||
sub_clip_end = o['clip_end'] if clip_given else None
|
||||
|
||||
result = await submgr.add_subscription(
|
||||
o['url'],
|
||||
check_interval_minutes=cic,
|
||||
@@ -948,6 +958,8 @@ async def subscribe(request):
|
||||
ytdl_options_overrides=o['ytdl_options_overrides'],
|
||||
title_regex=post.get('title_regex'),
|
||||
skip_subscriber_only=skip_subscriber_only,
|
||||
clip_start=sub_clip_start,
|
||||
clip_end=sub_clip_end,
|
||||
)
|
||||
return web.Response(text=serializer.encode(result))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user