Загрузка данных
set -e
mkdir -p /opt/vk-resolver
cat > /opt/vk-resolver/resolver.py <<'PY'
import os, time, hmac, subprocess, threading
from urllib.parse import urlparse, parse_qs
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
TOKEN = os.environ["RESOLVER_TOKEN"]
FORMAT = "best[height<=1080][height>=720]/best[height<=1080]/best"
ALLOWED = {
"vkvideo.ru", "www.vkvideo.ru",
"vk.com", "www.vk.com", "m.vk.com",
"vk.ru", "www.vk.ru"
}
CACHE = {}
LOCK = threading.Lock()
SLOTS = threading.BoundedSemaphore(3)
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def reply(self, status, message=""):
data = message.encode()
self.send_response(status)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
if self.command != "HEAD":
self.wfile.write(data)
def do_HEAD(self):
self.route()
def do_GET(self):
self.route()
def route(self):
p = urlparse(self.path)
if p.path == "/health":
return self.reply(200, "OK")
if p.path != "/resolve":
return self.reply(404, "Not found")
q = parse_qs(p.query)
if not hmac.compare_digest(q.get("token", [""])[0], TOKEN):
return self.reply(403, "Forbidden")
source = q.get("url", [""])[0]
u = urlparse(source)
if u.scheme != "https" or u.hostname not in ALLOWED:
return self.reply(400, "Unsupported source URL")
with LOCK:
item = CACHE.get(source)
direct = item[1] if item and item[0] > time.time() else None
if not direct:
if not SLOTS.acquire(blocking=False):
return self.reply(503, "Resolver busy")
try:
result = subprocess.run(
["yt-dlp", "--no-warnings", "--no-playlist",
"--socket-timeout", "10", "-f", FORMAT,
"--get-url", source],
capture_output=True, text=True, timeout=25
)
urls = [
line.strip() for line in result.stdout.splitlines()
if line.strip().startswith(("https://", "http://"))
]
if result.returncode != 0 or not urls:
print("yt-dlp error:", result.stderr[-500:], flush=True)
return self.reply(502, "Could not extract video URL")
direct = urls[0]
with LOCK:
CACHE[source] = (time.time() + 30, direct)
except subprocess.TimeoutExpired:
return self.reply(504, "Extraction timed out")
finally:
SLOTS.release()
self.send_response(302)
self.send_header("Location", direct)
self.send_header("Cache-Control", "no-store")
self.send_header("Content-Length", "0")
self.end_headers()
ThreadingHTTPServer(("0.0.0.0", 8787), Handler).serve_forever()
PY
cat > /opt/vk-resolver/Dockerfile <<'EOF'
FROM python:3.12-slim
RUN pip install --no-cache-dir yt-dlp
COPY resolver.py /app/resolver.py
CMD ["python", "-u", "/app/resolver.py"]
EOF
TOKEN="$(openssl rand -hex 24)"
printf '%s' "$TOKEN" > /opt/vk-resolver/token.txt
chmod 600 /opt/vk-resolver/token.txt
docker build -t vk-resolver:local /opt/vk-resolver
docker rm -f vk-resolver 2>/dev/null || true
docker run -d \
--name vk-resolver \
--restart unless-stopped \
--memory=256m \
--cpus=0.5 \
-p 0.0.0.0:8787:8787 \
-e "RESOLVER_TOKEN=$TOKEN" \
vk-resolver:local
echo "Посредник запущен. Токен сохранён в /opt/vk-resolver/token.txt"