studio/rbxl-importer/src/asset_proxy.py
min c375ae01ac
All checks were successful
CI / Lint (pull_request) Successful in 1m6s
CI / Build (pull_request) Successful in 2m2s
CI / Secret scan (pull_request) Successful in 26s
CI / PR size check (pull_request) Successful in 7s
CI / Deploy to S1 + S2 (pull_request) Has been skipped
feat(rbxl-import): импорт Roblox .rbxl карт в Rublox-проекты
Тест-фича для МИНа. Полное описание в 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>
2026-06-07 18:24:27 +03:00

102 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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}