's'
This commit is contained in:
208
web2_push_proxy.py
Normal file
208
web2_push_proxy.py
Normal file
@@ -0,0 +1,208 @@
|
||||
"""YouTube push proxy resolution (from d2ypp2 douyin_youtube_ffplay, simplified)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
_PROXY_ENV_KEYS = (
|
||||
"http_proxy",
|
||||
"https_proxy",
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"ALL_PROXY",
|
||||
"all_proxy",
|
||||
)
|
||||
|
||||
|
||||
def _is_disabled_value(value: str) -> bool:
|
||||
return (value or "").strip().lower() in {"0", "false", "no", "off", "none", "direct"}
|
||||
|
||||
|
||||
def _is_local_tcp_open(host: str, port: int, timeout: float = 0.35) -> bool:
|
||||
try:
|
||||
with socket.create_connection((host, port), timeout=timeout):
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def normalize_push_proxy_mode(configured: str | None = None) -> str:
|
||||
value = configured or os.environ.get("YOUTUBE_PUSH_PROXY_MODE") or os.environ.get("LIVE_YOUTUBE_PUSH_PROXY_MODE")
|
||||
if not value:
|
||||
value = "auto"
|
||||
low = (value or "").strip().lower()
|
||||
if low in {"0", "false", "no", "off", "none", "direct"}:
|
||||
return "off"
|
||||
if low in {"env", "environment", "http_proxy", "https_proxy"}:
|
||||
return "env"
|
||||
if low in {"proxychains", "proxy", "chain", "proxychains4"}:
|
||||
return "proxychains"
|
||||
return "auto"
|
||||
|
||||
|
||||
def resolve_youtube_push_proxy(configured: str | None = None) -> str:
|
||||
for value in (
|
||||
configured,
|
||||
os.environ.get("YOUTUBE_PUSH_PROXY"),
|
||||
os.environ.get("LIVE_YOUTUBE_PUSH_PROXY"),
|
||||
):
|
||||
value = (value or "").strip()
|
||||
if _is_disabled_value(value):
|
||||
return ""
|
||||
if value:
|
||||
return value
|
||||
|
||||
auto = (os.environ.get("YOUTUBE_PUSH_PROXY_AUTO") or "1").strip().lower()
|
||||
port_raw = os.environ.get("YOUTUBE_PUSH_PROXY_PORT") or os.environ.get("LIVE_YOUTUBE_PUSH_PROXY_PORT") or "7890"
|
||||
try:
|
||||
proxy_port = int(str(port_raw).strip())
|
||||
except (TypeError, ValueError):
|
||||
proxy_port = 7890
|
||||
if auto not in {"0", "false", "no", "off"} and _is_local_tcp_open("127.0.0.1", proxy_port):
|
||||
return f"socks5://127.0.0.1:{proxy_port}"
|
||||
|
||||
for key in ("https_proxy", "http_proxy", "HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "all_proxy"):
|
||||
value = (os.environ.get(key) or "").strip()
|
||||
if _is_disabled_value(value):
|
||||
return ""
|
||||
if value:
|
||||
return value
|
||||
return ""
|
||||
|
||||
|
||||
def redact_proxy_url(value: str) -> str:
|
||||
return re.sub(r"(://[^:/@\s]+:)[^@/\s]+(@)", r"\1<redacted>\2", value or "")
|
||||
|
||||
|
||||
def should_use_proxychains(push_proxy: str, proxy_mode: str = "auto") -> bool:
|
||||
if not push_proxy:
|
||||
return False
|
||||
mode = normalize_push_proxy_mode(proxy_mode)
|
||||
if mode in {"off", "env"}:
|
||||
return False
|
||||
if mode == "proxychains":
|
||||
return bool(shutil.which("proxychains4"))
|
||||
scheme = (urlparse(push_proxy if "://" in push_proxy else f"socks5://{push_proxy}").scheme or "socks5").lower()
|
||||
if scheme.startswith("socks"):
|
||||
return bool(shutil.which("proxychains4"))
|
||||
return False
|
||||
|
||||
|
||||
def proxychains_proxy_line(push_proxy: str) -> str | None:
|
||||
value = (push_proxy or "").strip()
|
||||
if not value:
|
||||
return None
|
||||
parsed = urlparse(value if "://" in value else f"socks5://{value}")
|
||||
host = parsed.hostname
|
||||
port = parsed.port
|
||||
if not host or not port:
|
||||
return None
|
||||
scheme = (parsed.scheme or "socks5").lower()
|
||||
if scheme in {"socks", "socks5", "socks5h"}:
|
||||
proxy_type = "socks5"
|
||||
elif scheme == "socks4":
|
||||
proxy_type = "socks4"
|
||||
elif scheme in {"http", "https"}:
|
||||
proxy_type = "http"
|
||||
else:
|
||||
return None
|
||||
parts = [proxy_type, host, str(port)]
|
||||
if parsed.username:
|
||||
parts.append(parsed.username)
|
||||
if parsed.password:
|
||||
parts.append(parsed.password)
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
def proxychains_config_for_push(push_proxy: str, *, base_dir: str | Path | None = None) -> str | None:
|
||||
explicit = (
|
||||
os.environ.get("YOUTUBE_PROXYCHAINS_CONF")
|
||||
or os.environ.get("LIVE_YOUTUBE_PROXYCHAINS_CONF")
|
||||
or ""
|
||||
).strip()
|
||||
if explicit and os.path.isfile(explicit):
|
||||
return explicit
|
||||
|
||||
proxy_line = proxychains_proxy_line(push_proxy)
|
||||
root = Path(base_dir or os.path.dirname(os.path.abspath(__file__)))
|
||||
if proxy_line:
|
||||
runtime_dir = root / ".process_runner"
|
||||
try:
|
||||
runtime_dir.mkdir(parents=True, exist_ok=True)
|
||||
conf = runtime_dir / "proxychains-youtube.conf"
|
||||
conf.write_text(
|
||||
"strict_chain\n"
|
||||
"proxy_dns\n"
|
||||
"tcp_read_time_out 30000\n"
|
||||
"tcp_connect_time_out 8000\n\n"
|
||||
"[ProxyList]\n"
|
||||
f"{proxy_line}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
try:
|
||||
os.chmod(conf, 0o600)
|
||||
except OSError:
|
||||
pass
|
||||
return str(conf)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
bundled = root / "config" / "proxychains-youtube.conf"
|
||||
if bundled.is_file():
|
||||
return str(bundled)
|
||||
return None
|
||||
|
||||
|
||||
def ffmpeg_env_with_push_proxy(
|
||||
push_proxy: str,
|
||||
proxy_mode: str = "auto",
|
||||
*,
|
||||
use_proxychains: bool = False,
|
||||
base_env: dict[str, str] | None = None,
|
||||
) -> dict[str, str]:
|
||||
env = dict(base_env or os.environ)
|
||||
for key in _PROXY_ENV_KEYS:
|
||||
env.pop(key, None)
|
||||
push_proxy = (push_proxy or "").strip()
|
||||
if not push_proxy:
|
||||
return env
|
||||
mode = normalize_push_proxy_mode(proxy_mode)
|
||||
if mode == "off" or use_proxychains:
|
||||
return env
|
||||
for key in _PROXY_ENV_KEYS:
|
||||
env[key] = push_proxy
|
||||
return env
|
||||
|
||||
|
||||
def proxychains_command(ffmpeg_command: list[str], push_proxy: str, proxy_mode: str = "auto", *, base_dir: str | Path | None = None) -> list[str] | None:
|
||||
if not should_use_proxychains(push_proxy, proxy_mode):
|
||||
return None
|
||||
conf = proxychains_config_for_push(push_proxy, base_dir=base_dir)
|
||||
if not conf:
|
||||
return None
|
||||
return ["proxychains4", "-q", "-f", conf, *ffmpeg_command]
|
||||
|
||||
|
||||
def apply_push_proxy_to_ffmpeg_cmd(
|
||||
ffmpeg_command: list[str],
|
||||
push_proxy: str,
|
||||
proxy_mode: str = "auto",
|
||||
*,
|
||||
base_dir: str | Path | None = None,
|
||||
) -> tuple[list[str], bool]:
|
||||
"""Return (command, used_proxychains). Falls back to -http_proxy when proxychains unavailable."""
|
||||
wrapped = proxychains_command(ffmpeg_command, push_proxy, proxy_mode, base_dir=base_dir)
|
||||
if wrapped:
|
||||
return wrapped, True
|
||||
mode = normalize_push_proxy_mode(proxy_mode)
|
||||
if push_proxy and mode != "off":
|
||||
cmd = list(ffmpeg_command)
|
||||
cmd.insert(1, "-http_proxy")
|
||||
cmd.insert(2, push_proxy)
|
||||
return cmd, False
|
||||
return ffmpeg_command, False
|
||||
Reference in New Issue
Block a user