Загрузка данных


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"