All checks were successful
Тест-фича для МИНа. Полное описание в rbxl-importer/INFO_PROCESS.md. Backend (rbxl-importer/ на VM 130 S1): - Python-парсер Roblox Binary (28+ типов значений) - Asset downloader через Marfusha proxy + .ROBLOSECURITY cookie - Mesh→GLB конвертер (v1-v5) - Converter Roblox-классов → project_data - Flask API: /analyze + /create Frontend: - API.js + components/RbxlImportModal.jsx (drag-n-drop) Тестовый импорт Easy Obby: project_id 2697, 2244 primitives + 742 lua-scripts + 5 ассетов. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
102 lines
4.2 KiB
Python
102 lines
4.2 KiB
Python
"""
|
||
asset_proxy.py — конфигурация HTTP-прокси для скачивания Roblox-ассетов.
|
||
|
||
Roblox блокирует direct-traffic из РФ — нужно ходить через VPN/прокси.
|
||
|
||
Текущая стратегия:
|
||
1. По умолчанию — НИЧЕГО НЕ КАЧАТЬ (mode='disabled'), пишем ассеты со status='pending'.
|
||
Импорт продолжается, геометрия и скрипты работают.
|
||
Mesh/Texture/Sound показываются как заглушки до скачки.
|
||
2. mode='cloudflare_worker' — ходить через Cloudflare Worker (см. cf-worker/asset-proxy.js).
|
||
Worker принимает /asset?id=<id> и проксирует на assetdelivery.roblox.com.
|
||
3. mode='direct' — без прокси (для тестов или если запускаем с зарубежного хоста).
|
||
|
||
ENV:
|
||
ROBLOX_PROXY_MODE = disabled | direct | cloudflare_worker
|
||
ROBLOX_PROXY_URL = https://rbxl-proxy.workers.dev # для cloudflare_worker mode
|
||
|
||
Использование в asset_downloader:
|
||
from asset_proxy import get_proxy_config
|
||
cfg = get_proxy_config()
|
||
if cfg.mode == 'disabled':
|
||
raise PendingDownload(rbx_asset_id)
|
||
url = cfg.build_url(rbx_asset_id)
|
||
resp = session.get(url, ...)
|
||
"""
|
||
import os
|
||
from dataclasses import dataclass
|
||
|
||
|
||
@dataclass
|
||
class ProxyConfig:
|
||
mode: str # disabled | direct | cloudflare_worker
|
||
base_url: str # для direct mode = 'https://assetdelivery.roblox.com/v1/asset'
|
||
# для cf worker = 'https://rbxl-proxy.workers.dev/asset'
|
||
headers: dict # доп. заголовки (например auth для CF Worker)
|
||
|
||
def build_url(self, rbx_asset_id: int) -> str:
|
||
return f'{self.base_url}?id={rbx_asset_id}'
|
||
|
||
|
||
class PendingDownload(Exception):
|
||
"""Бросается когда mode='disabled' — ассет не скачан но импорт продолжается."""
|
||
def __init__(self, rbx_asset_id: int):
|
||
self.rbx_asset_id = rbx_asset_id
|
||
super().__init__(f"asset {rbx_asset_id} download pending (proxy disabled)")
|
||
|
||
|
||
@dataclass
|
||
class HttpProxyConfig:
|
||
"""Дополнение к ProxyConfig для режима http_proxy.
|
||
|
||
proxies — словарь который requests.Session() ест как есть.
|
||
"""
|
||
proxies: dict
|
||
|
||
|
||
def get_proxy_config() -> ProxyConfig:
|
||
mode = os.environ.get('ROBLOX_PROXY_MODE', 'disabled')
|
||
if mode == 'direct':
|
||
return ProxyConfig(
|
||
mode='direct',
|
||
base_url='https://assetdelivery.roblox.com/v1/asset',
|
||
headers={'User-Agent': 'Roblox/WinInet'},
|
||
)
|
||
elif mode == 'cloudflare_worker':
|
||
url = os.environ.get('ROBLOX_PROXY_URL')
|
||
if not url:
|
||
raise RuntimeError("ROBLOX_PROXY_URL не задан для cloudflare_worker mode")
|
||
secret = os.environ.get('ROBLOX_PROXY_SECRET', '')
|
||
return ProxyConfig(
|
||
mode='cloudflare_worker',
|
||
base_url=url.rstrip('/') + '/asset',
|
||
headers={
|
||
'User-Agent': 'rublox-rbxl-importer/1.0',
|
||
'X-Proxy-Auth': secret,
|
||
},
|
||
)
|
||
elif mode == 'http_proxy':
|
||
# Используем внешний HTTP-прокси для исходящего трафика.
|
||
# base_url остаётся реальный Roblox CDN, запрос идёт через прокси.
|
||
# ENV: ROBLOX_HTTP_PROXY = http://user:pass@host:port
|
||
proxy_url = os.environ.get('ROBLOX_HTTP_PROXY', '')
|
||
if not proxy_url:
|
||
raise RuntimeError("ROBLOX_HTTP_PROXY не задан для http_proxy mode")
|
||
return ProxyConfig(
|
||
mode='http_proxy',
|
||
base_url='https://assetdelivery.roblox.com/v1/asset',
|
||
headers={'User-Agent': 'Roblox/WinInet'},
|
||
)
|
||
elif mode == 'disabled':
|
||
return ProxyConfig(mode='disabled', base_url='', headers={})
|
||
else:
|
||
raise RuntimeError(f"unknown ROBLOX_PROXY_MODE={mode!r}")
|
||
|
||
|
||
def get_http_proxies() -> dict:
|
||
"""Для requests-сессии: {'http': ..., 'https': ...}."""
|
||
url = os.environ.get('ROBLOX_HTTP_PROXY', '')
|
||
if not url:
|
||
return {}
|
||
return {'http': url, 'https': url}
|