Fix duplicate-post and timeout bugs in TG/MAX posting pipeline
- Stop blindly retrying send_photo/send_video/send_media_group and MAX send_message on ambiguous network timeouts - a timeout doesn't prove the message wasn't delivered, and retrying risked posting duplicates (observed live: a video posted 3-5x after repeated timeout retries). - Raise/rework timeouts that were too short for real large-file transfer speeds: TG media upload timeout, and yt-dlp download now uses stall detection (killed only on true silence) instead of a flat ceiling that was cutting off legitimately slow-but-successful video downloads. - Fix sendRichMessage: ok=true with an unparseable message_id no longer triggers a fallback send (Telegram already created the message). - MAX: videos over the documented 250MB cap are now sent as a "watch via link" note instead of silently failing the upload. - Add PRAGMA busy_timeout to all DB connections. - Remove unused MAX_MEDIA_CHANNEL_ID (MAX's /uploads returns a portable token directly, no staging channel needed, unlike Telegram). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+34
-16
@@ -132,36 +132,54 @@ class MediaProcessor:
|
||||
f"/bestvideo[height<={settings.video_max_height}][filesize<{max_size}]+bestaudio"
|
||||
f"/bestvideo[height<={settings.video_max_height}]+bestaudio"
|
||||
),
|
||||
"--quiet", "--no-warnings",
|
||||
# No --quiet: we need yt-dlp's own progress lines to tell a slow-but-alive
|
||||
# download (fine, however long it takes) apart from a genuinely hung one -
|
||||
# a flat wall-clock timeout can't tell those apart and was killing large
|
||||
# videos that just needed more time (same class of bug as the TG upload
|
||||
# timeout). --newline makes each progress update its own line instead of
|
||||
# overwriting via \r, so we can read it with readline().
|
||||
"--newline", "--no-warnings",
|
||||
])
|
||||
|
||||
proc = None
|
||||
stderr = b""
|
||||
output_lines: list[str] = []
|
||||
deadline = asyncio.get_running_loop().time() + settings.yt_dlp_timeout_sec
|
||||
stall_reason: Optional[str] = None
|
||||
try:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
)
|
||||
_, stderr = await asyncio.wait_for(
|
||||
proc.communicate(), timeout=settings.yt_dlp_timeout_sec
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
if proc:
|
||||
while True:
|
||||
remaining_total = deadline - asyncio.get_running_loop().time()
|
||||
if remaining_total <= 0:
|
||||
stall_reason = "video download exceeded overall timeout"
|
||||
break
|
||||
wait_for = min(settings.yt_dlp_stall_timeout_sec, remaining_total)
|
||||
try:
|
||||
proc.kill()
|
||||
await proc.communicate()
|
||||
except Exception:
|
||||
pass
|
||||
output_path.unlink(missing_ok=True)
|
||||
await unregister_active_path(output_path)
|
||||
return None, "video download timeout", False
|
||||
line = await asyncio.wait_for(proc.stdout.readline(), timeout=wait_for)
|
||||
except asyncio.TimeoutError:
|
||||
stall_reason = "video download stalled (no progress from yt-dlp)"
|
||||
break
|
||||
if not line:
|
||||
break # stdout closed - process is finishing up
|
||||
output_lines.append(line.decode("utf-8", errors="ignore"))
|
||||
|
||||
if stall_reason:
|
||||
proc.kill()
|
||||
await proc.communicate()
|
||||
output_path.unlink(missing_ok=True)
|
||||
await unregister_active_path(output_path)
|
||||
return None, stall_reason, False
|
||||
|
||||
await proc.wait()
|
||||
finally:
|
||||
if netrc_path:
|
||||
Path(netrc_path).unlink(missing_ok=True)
|
||||
|
||||
if proc.returncode != 0 or not output_path.exists() or output_path.stat().st_size == 0:
|
||||
err_msg = (stderr or b"").decode("utf-8", errors="ignore").lower()
|
||||
err_msg = "".join(output_lines).lower()
|
||||
is_permanent = any(
|
||||
m in err_msg for m in (
|
||||
"removed", "unavailable", "private", "access denied", "does not pass filter", "sign in"
|
||||
|
||||
Reference in New Issue
Block a user