perf(재생지연): 유튜브 URL 음성접속 전 프리워밍 + 리졸버 진행요청 합치기(coalescing)로 첫 재생 지연 ~2초 단축

This commit is contained in:
tkrmagid
2026-09-11 23:02:35 +09:00
parent e73f23582c
commit 3a4849acab
3 changed files with 55 additions and 9 deletions

View File

@@ -27,6 +27,7 @@ YTDLP = os.environ.get("YTDLP", "/usr/local/bin/yt-dlp")
COOKIE_FILE = os.environ.get("YTRESOLVER_COOKIE", "/root/bot/db/ytcookie.txt")
PORT = int(os.environ.get("YTRESOLVER_PORT", "8779"))
CACHE = {}
INFLIGHT = {} # target -> {"event": Event, "url": str|None} 진행 중 리졸브 합치기(coalescing)
LOCK = threading.Lock()
TTL = 1800 # 리졸브된 URL 캐시 30분(googlevideo URL 자체는 약 6시간 유효)
@@ -53,12 +54,7 @@ def first_url(out):
return lines[0] if lines else None
def resolve(target):
now = time.time()
with LOCK:
hit = CACHE.get(target)
if hit and hit[1] > now:
return hit[0]
def _do_resolve(target):
try:
out = run_ytdlp(target, use_cookies=False)
url = first_url(out)
@@ -69,13 +65,45 @@ def resolve(target):
except subprocess.TimeoutExpired:
print("[result] TIMEOUT", file=sys.stderr, flush=True)
return None
ok = bool(url and url.startswith("http"))
if not ok:
if not (url and url.startswith("http")):
print(f"[result] FAIL {(out.stderr or out.stdout or '')[:240]}", file=sys.stderr, flush=True)
return None
print(f"[result] OK {url[:60]}", file=sys.stderr, flush=True)
with LOCK:
CACHE[target] = (url, now + TTL)
CACHE[target] = (url, time.time() + TTL)
return url
def resolve(target):
now = time.time()
with LOCK:
hit = CACHE.get(target)
if hit and hit[1] > now:
return hit[0]
# 같은 target이 이미 리졸브 중이면, 새 yt-dlp를 띄우지 않고 그 결과를 기다린다.
# (봇이 음성 접속과 동시에 미리 리졸브를 걸어두면, 실제 재생 요청이 이 진행분에 합쳐져
# 음성 접속 시간과 리졸브 시간이 겹쳐 첫 재생 지연이 크게 줄어든다.)
entry = INFLIGHT.get(target)
owner = entry is None
if owner:
entry = {"event": threading.Event(), "url": None}
INFLIGHT[target] = entry
if not owner:
entry["event"].wait(50)
with LOCK:
hit = CACHE.get(target)
return hit[0] if hit and hit[1] > time.time() else entry.get("url")
# owner: 실제 리졸브 수행
url = None
try:
url = _do_resolve(target)
except Exception as e:
print(f"[result] ERR {str(e)[:120]}", file=sys.stderr, flush=True)
finally:
entry["url"] = url
with LOCK:
INFLIGHT.pop(target, None)
entry["event"].set()
return url