AI slop update to switch warp docker ip on bot block
This commit is contained in:
@@ -8,6 +8,58 @@ import math
|
||||
from .Utils import Utils, DataHolder
|
||||
import traceback
|
||||
|
||||
# Path to the WARP IP-rotation script. Must be executable (chmod +x) and live
|
||||
# in the same folder as _start.sh (the systemd unit's WorkingDirectory), since
|
||||
# rotate_warp.sh itself cd's into the warp-docker compose folder.
|
||||
WARP_ROTATE_SCRIPT = "/home/gameservers/css_ze/torchlight3/rotate_warp.sh"
|
||||
WARP_ROTATE_TIMEOUT = 60 # seconds to allow rotate_warp.sh to bring WARP back up
|
||||
|
||||
# yt-dlp's stderr when YouTube's bot-check trips. Matched as two substrings
|
||||
# rather than one exact string since the apostrophe yt-dlp prints can vary
|
||||
# (straight vs curly quote) between versions.
|
||||
YOUTUBE_BOT_CHECK_MARKERS = (b"Sign in to confirm", b"not a bot")
|
||||
|
||||
|
||||
async def run_ytdlp_with_rotation(logger, *args):
|
||||
"""
|
||||
Run `yt-dlp *args` exactly as before. If yt-dlp's stderr shows YouTube's
|
||||
"Sign in to confirm you're not a bot" bot-check, rotate the WARP proxy's
|
||||
IP once (via rotate_warp.sh) and transparently retry the exact same
|
||||
yt-dlp call before giving up. The caller (and the end user) never sees
|
||||
the first, blocked attempt -- they just get a result, possibly a couple
|
||||
seconds slower.
|
||||
|
||||
Returns (stdout_bytes, stderr_bytes) from the final attempt.
|
||||
"""
|
||||
Out, Err = b"", b""
|
||||
for attempt in (1, 2):
|
||||
Proc = await asyncio.create_subprocess_exec(
|
||||
"yt-dlp", *args,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE)
|
||||
Out, Err = await Proc.communicate()
|
||||
|
||||
BotChecked = all(marker in Err for marker in YOUTUBE_BOT_CHECK_MARKERS)
|
||||
if BotChecked and attempt == 1:
|
||||
logger.warning("yt-dlp hit YouTube's bot-check, rotating WARP IP and retrying: %s", args)
|
||||
try:
|
||||
RotateProc = await asyncio.create_subprocess_exec(
|
||||
WARP_ROTATE_SCRIPT,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE)
|
||||
RotateOut, RotateErr = await asyncio.wait_for(
|
||||
RotateProc.communicate(), timeout=WARP_ROTATE_TIMEOUT)
|
||||
if RotateProc.returncode != 0:
|
||||
logger.error("rotate_warp.sh failed (rc=%s): %s", RotateProc.returncode,
|
||||
RotateErr.decode(errors="replace"))
|
||||
except (asyncio.TimeoutError, OSError) as e:
|
||||
logger.error("WARP rotation did not complete in time, retrying with same IP anyway: %s", e)
|
||||
continue
|
||||
break
|
||||
|
||||
return Out, Err
|
||||
|
||||
|
||||
def get_birtate(message):
|
||||
bitrate = []
|
||||
try:
|
||||
@@ -114,18 +166,22 @@ class URLFilter(BaseCommand):
|
||||
if TimeStr:
|
||||
Time = Utils.ParseTime(TimeStr)
|
||||
|
||||
Proc = await asyncio.create_subprocess_exec("yt-dlp", "--dump-json", "-g", url,
|
||||
stdout = asyncio.subprocess.PIPE)
|
||||
Out, _ = await Proc.communicate()
|
||||
# No "-g" here: we only need metadata (title/duration/webpage_url)
|
||||
# for the chat message. Actual playback happens by handing the
|
||||
# canonical webpage URL to yt-dlp again at play time (see
|
||||
# YTDLP_URI_PREFIX in FFmpegAudioPlayer.py) rather than resolving
|
||||
# to a direct googlevideo URL here and having a separate process
|
||||
# (ffmpeg) try to fetch it -- that signed-URL handoff turned out
|
||||
# to reliably 403 even with matching proxy/headers.
|
||||
Out, Err = await run_ytdlp_with_rotation(self.Logger, "--dump-json", "--no-playlist", url)
|
||||
|
||||
parts = Out.split(b'\n')
|
||||
parts.pop() # trailing new line
|
||||
if not Out.strip():
|
||||
self.Logger.error("yt-dlp returned no output for %s: %s", url, Err.decode(errors = "replace"))
|
||||
self.Torchlight().SayChat("Error: Could not fetch YouTube info, try again in a moment.")
|
||||
return url, Text
|
||||
|
||||
Info = parts.pop()
|
||||
url = parts.pop()
|
||||
|
||||
url = url.strip().decode("ascii")
|
||||
Info = self.json.loads(Info)
|
||||
Info = self.json.loads(Out.strip().split(b'\n')[-1])
|
||||
url = "ytdlp:" + Info.get("webpage_url", url)
|
||||
|
||||
if Info["extractor_key"] == "Youtube":
|
||||
self.Torchlight().SayChat("\x07E52D27[YouTube]\x01 {0} | {1} | {2:,}".format(
|
||||
@@ -704,14 +760,19 @@ class YouTubeSearch(BaseCommand):
|
||||
message[1] = message[1][:Temp.value]
|
||||
|
||||
search_term = message[1].split("pitch=")[0].split("tempo=")[0].split('backward=')[0].split('backwards=')[0]
|
||||
Proc = await asyncio.create_subprocess_exec("yt-dlp", "--dump-json", "-xg", "ytsearch:" + search_term,
|
||||
stdout = asyncio.subprocess.PIPE)
|
||||
Out, _ = await Proc.communicate()
|
||||
# No "-xg" here: only metadata is needed at this point. Playback goes
|
||||
# through yt-dlp itself again (piped into ffmpeg) rather than a
|
||||
# resolved googlevideo URL -- see the comment above YTDLP_URI_PREFIX
|
||||
# in FFmpegAudioPlayer.py for why.
|
||||
Out, Err = await run_ytdlp_with_rotation(self.Logger, "--dump-json", "--no-playlist", "ytsearch:" + search_term)
|
||||
|
||||
print('out value: ', Out)
|
||||
url, Info = Out.split(b'\n', maxsplit = 1)
|
||||
url = url.strip().decode("ascii")
|
||||
Info = self.json.loads(Info)
|
||||
if not Out.strip():
|
||||
self.Logger.error("yt-dlp returned no output for search '%s': %s", search_term, Err.decode(errors = "replace"))
|
||||
self.Torchlight().SayChat("Error: Could not fetch YouTube info, try again in a moment.")
|
||||
return 1
|
||||
|
||||
Info = self.json.loads(Out.strip().split(b'\n')[-1])
|
||||
url = "ytdlp:" + Info.get("webpage_url", "ytsearch:" + search_term)
|
||||
|
||||
if Info["extractor_key"] == "Youtube":
|
||||
self.Torchlight().SayChat("\x07E52D27[YouTube]\x01 {0} | {1} | {2:,}".format(
|
||||
@@ -987,4 +1048,3 @@ class Reload(BaseCommand):
|
||||
self.Logger.debug(sys._getframe().f_code.co_name + ' ' + str(message))
|
||||
self.Torchlight().Reload()
|
||||
return 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user