Give oversized-video retries a much longer budget than stall retries
Confirmed on a real post: VK can still be transcoding lower renditions 8+ minutes after publish, so a few seconds of backoff wasn't enough margin. Size-exceeded failures now get their own retry budget (5 attempts, 90s apart - up to 7.5 extra minutes) instead of sharing the short 3s backoff meant for stalls/timeouts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,12 @@ class Settings(BaseSettings):
|
|||||||
# Extra attempts after a transient failure (stall/timeout/network hiccup) before
|
# Extra attempts after a transient failure (stall/timeout/network hiccup) before
|
||||||
# falling back to a link-only post. Permanent failures (private/removed/etc.) never retry.
|
# falling back to a link-only post. Permanent failures (private/removed/etc.) never retry.
|
||||||
yt_dlp_retry_attempts: int = 2
|
yt_dlp_retry_attempts: int = 2
|
||||||
|
# A fresh upload's lower-quality renditions can still be mid-transcode on VK's side,
|
||||||
|
# so a height-capped URL briefly serves the full-res master and trips our size check -
|
||||||
|
# confirmed on a real post still happening 8 minutes after publish. Needs a separate,
|
||||||
|
# much longer retry budget than the generic stall/timeout case above.
|
||||||
|
yt_dlp_size_retry_attempts: int = 5
|
||||||
|
yt_dlp_size_retry_delay_sec: float = 90.0
|
||||||
|
|
||||||
# Text Styling & Decoration
|
# Text Styling & Decoration
|
||||||
header_text: str = ""
|
header_text: str = ""
|
||||||
|
|||||||
+22
-11
@@ -102,24 +102,35 @@ class MediaProcessor:
|
|||||||
async def download_video_ytdlp(
|
async def download_video_ytdlp(
|
||||||
self, url: str, prefix: str = "video_"
|
self, url: str, prefix: str = "video_"
|
||||||
) -> tuple[Optional[Path], Optional[str], bool]:
|
) -> tuple[Optional[Path], Optional[str], bool]:
|
||||||
"""Retries transient failures (stalls, timeouts, one-off yt-dlp/network hiccups)
|
"""Retries transient failures before giving up - a single flaky attempt used to fall
|
||||||
a couple of times before giving up - a single flaky attempt used to fall straight
|
straight through to the link-only fallback even though the video was perfectly
|
||||||
through to the link-only fallback even though the video was perfectly downloadable."""
|
downloadable. Stalls/timeouts/network hiccups get a couple of quick retries; a
|
||||||
|
size-exceeded result (likely VK still transcoding lower renditions) switches to a
|
||||||
|
much longer, more patient retry budget - see yt_dlp_size_retry_* in config.py."""
|
||||||
last_result: tuple[Optional[Path], Optional[str], bool] = (None, "video download failed", False)
|
last_result: tuple[Optional[Path], Optional[str], bool] = (None, "video download failed", False)
|
||||||
for attempt in range(1, settings.yt_dlp_retry_attempts + 2):
|
attempt = 0
|
||||||
|
max_attempts = settings.yt_dlp_retry_attempts + 1
|
||||||
|
delay = 3.0
|
||||||
|
while attempt < max_attempts:
|
||||||
|
attempt += 1
|
||||||
path, err, is_permanent = await self._download_video_ytdlp_attempt(url, prefix)
|
path, err, is_permanent = await self._download_video_ytdlp_attempt(url, prefix)
|
||||||
if path or is_permanent:
|
if path or is_permanent:
|
||||||
return path, err, is_permanent
|
return path, err, is_permanent
|
||||||
last_result = (path, err, is_permanent)
|
last_result = (path, err, is_permanent)
|
||||||
if attempt <= settings.yt_dlp_retry_attempts:
|
# A fresh upload's lower-quality renditions can still be mid-transcode on
|
||||||
# A fresh upload's lower-quality renditions can still be mid-transcode on
|
# VK's side - the "720p" URL then briefly serves the full-res master until
|
||||||
# VK's side - the "720p" URL then briefly serves the full-res master until
|
# the ladder catches up, which trips our size cap. Confirmed on a real post
|
||||||
# the ladder catches up, which trips our size cap. That's not a permanent
|
# still happening 8 minutes after publish, so this needs a much longer and
|
||||||
# rejection, just needs more time than a stalled connection does.
|
# more patient retry budget than a stalled connection does - switch to it
|
||||||
delay = 60.0 if "exceeds size limit" in (err or "") else 3.0
|
# (and restart the attempt count) the first time we see this specific error.
|
||||||
|
if "exceeds size limit" in (err or "") and max_attempts != settings.yt_dlp_size_retry_attempts + 1:
|
||||||
|
max_attempts = settings.yt_dlp_size_retry_attempts + 1
|
||||||
|
delay = settings.yt_dlp_size_retry_delay_sec
|
||||||
|
attempt = 0
|
||||||
|
if attempt < max_attempts:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"yt-dlp attempt {}/{} failed transiently for {}: {} - retrying in {}s",
|
"yt-dlp attempt {}/{} failed transiently for {}: {} - retrying in {}s",
|
||||||
attempt, settings.yt_dlp_retry_attempts + 1, url, err, delay,
|
attempt, max_attempts, url, err, delay,
|
||||||
)
|
)
|
||||||
await asyncio.sleep(delay)
|
await asyncio.sleep(delay)
|
||||||
return last_result
|
return last_result
|
||||||
|
|||||||
Reference in New Issue
Block a user