@@ -17,6 +17,7 @@ import selectors
1717import signal
1818import subprocess
1919import sys
20+ import tempfile
2021import time
2122from typing import Any
2223from urllib .parse import urlsplit
@@ -39,6 +40,7 @@ URL_CREDENTIALS_RE = re.compile(r"(?P<scheme>[A-Za-z][A-Za-z0-9+.-]*://)[^/\s:@]
3940RTMP_SECRET_PATH_RE = re .compile (r"(?i)\b(?P<prefix>rtmps?://[^\s/]+/)[^\s]+" )
4041YOUTUBE_SECRET_PATH_RE = re .compile (r"(?i)\b(?P<prefix>https?://[^\s/]*(?:youtube|google)[^\s/]*/)[^\s]+" )
4142PROGRESS_LINE_RE = re .compile (r"^[A-Za-z0-9_]+=.*$" )
43+ RTSP_OPTION_CACHE : dict [tuple [str , str ], bool ] = {}
4244SUPERVISOR_LOCK_FILE = pathlib .Path (
4345 os .environ .get ("YTA_SUPERVISOR_LOCK_FILE" , HOME / ".config/youtube-autoencoder/supervisor.lock" )
4446).expanduser ()
@@ -300,6 +302,9 @@ def stream_config() -> tuple[str, str]:
300302
301303
302304def binary_supports_rtsp_option (binary : str , option : str ) -> bool :
305+ cache_key = (binary , option )
306+ if cache_key in RTSP_OPTION_CACHE :
307+ return RTSP_OPTION_CACHE [cache_key ]
303308 try :
304309 result = subprocess .run (
305310 [binary , "-hide_banner" , "-h" , "demuxer=rtsp" ],
@@ -312,7 +317,9 @@ def binary_supports_rtsp_option(binary: str, option: str) -> bool:
312317 log (f"could not inspect { pathlib .Path (binary ).name } RTSP options: { exc } " )
313318 return False
314319 output = "\n " .join ((result .stdout or "" , result .stderr or "" ))
315- return any (line .lstrip ().startswith (f"-{ option } " ) for line in output .splitlines ())
320+ supported = any (line .lstrip ().startswith (f"-{ option } " ) for line in output .splitlines ())
321+ RTSP_OPTION_CACHE [cache_key ] = supported
322+ return supported
316323
317324
318325def ffmpeg_supports_rtsp_option (option : str ) -> bool :
@@ -575,38 +582,46 @@ def api_command_while_streaming(
575582 timeout : int ,
576583) -> dict [str , Any ]:
577584 cmd = [env ("YTA_YOUTUBE_API" , "youtube-autoencoder-api" ), * args ]
578- try :
579- api_process = subprocess .Popen (
580- cmd ,
581- text = True ,
582- stdout = subprocess .PIPE ,
583- stderr = subprocess .PIPE ,
584- )
585- except OSError as exc :
586- raise ApiCommandError (
587- returncode = - 1 ,
588- payload = {"message" : f"could not start YouTube API helper: { exc } " , "retry_class" : "fatal" },
589- ) from exc
590- deadline = time .monotonic () + timeout
591- try :
592- while api_process .poll () is None :
593- remaining = max (0.0 , deadline - time .monotonic ())
594- drain_ffmpeg_output (runtime , timeout = min (0.5 , remaining ))
585+ with (
586+ tempfile .TemporaryFile (mode = "w+t" , encoding = "utf-8" ) as stdout_file ,
587+ tempfile .TemporaryFile (mode = "w+t" , encoding = "utf-8" ) as stderr_file ,
588+ ):
589+ try :
590+ api_process = subprocess .Popen (
591+ cmd ,
592+ text = True ,
593+ stdout = stdout_file ,
594+ stderr = stderr_file ,
595+ )
596+ except OSError as exc :
597+ raise ApiCommandError (
598+ returncode = - 1 ,
599+ payload = {"message" : f"could not start YouTube API helper: { exc } " , "retry_class" : "fatal" },
600+ ) from exc
601+ deadline = time .monotonic () + timeout
602+ try :
603+ while api_process .poll () is None :
604+ remaining = max (0.0 , deadline - time .monotonic ())
605+ drain_ffmpeg_output (runtime , timeout = min (0.5 , remaining ))
606+ ensure_encoder_running (runtime )
607+ if time .monotonic () >= deadline :
608+ raise ApiCommandError (
609+ returncode = - 1 ,
610+ payload = {
611+ "message" : f"YouTube API command timed out after { timeout } s" ,
612+ "retry_class" : "api" ,
613+ },
614+ )
615+ drain_ffmpeg_output (runtime , timeout = 0.0 )
595616 ensure_encoder_running (runtime )
596- if time .monotonic () >= deadline :
597- raise ApiCommandError (
598- returncode = - 1 ,
599- payload = {
600- "message" : f"YouTube API command timed out after { timeout } s" ,
601- "retry_class" : "api" ,
602- },
603- )
604- drain_ffmpeg_output (runtime , timeout = 0.0 )
605- ensure_encoder_running (runtime )
606- stdout , stderr = api_process .communicate ()
607- except BaseException :
608- stop_process (api_process )
609- raise
617+ api_process .wait ()
618+ stdout_file .seek (0 )
619+ stderr_file .seek (0 )
620+ stdout = stdout_file .read ()
621+ stderr = stderr_file .read ()
622+ except BaseException :
623+ stop_process (api_process )
624+ raise
610625 return parse_api_result (api_process .returncode , stdout , stderr )
611626
612627
0 commit comments