Spaces:
Running
Running
| import os | |
| import shutil | |
| import subprocess | |
| import requests | |
| import zipfile | |
| import glob | |
| import uuid | |
| import boto3 | |
| from fastapi import FastAPI, BackgroundTasks | |
| from pydantic import BaseModel | |
| # --- CONFIG --- | |
| os.chdir("/tmp") | |
| os.environ["HOME"] = "/tmp" | |
| os.environ["UNITY_PATH"] = "/opt/unity/Editor/Unity" | |
| BASE_PROJECT_PATH = "/app/UnityProject" | |
| R2_CONFIG = { | |
| "account_id": "31f2970a3442da4e6f17f79064897226", | |
| "access_key": "44613d17ec50941707a7ddd9b17944bc", | |
| "secret_key": "50185c21ff5e4f53e412e2013305b6ab24b7eda16cc0d3dc66593eed64aa99a2", | |
| "bucket_name": "zepeto-builds" | |
| } | |
| app = FastAPI() | |
| class ConversionRequest(BaseModel): | |
| file_url: str | |
| filename: str | |
| def upload_to_r2(local_path, r2_filename): | |
| try: | |
| s3 = boto3.client('s3', endpoint_url=f"https://{R2_CONFIG['account_id']}.r2.cloudflarestorage.com", | |
| aws_access_key_id=R2_CONFIG['access_key'], aws_secret_access_key=R2_CONFIG['secret_key']) | |
| s3.upload_file(local_path, R2_CONFIG['bucket_name'], f"packed/{r2_filename}") | |
| print(f"β [R2] Upload Sukses: packed/{r2_filename}", flush=True) | |
| except Exception as e: print(f"β [R2] Gagal: {e}", flush=True) | |
| def make_zepeto_archive(source_dir, output_file): | |
| """Membungkus folder InputRaw menjadi file .zepeto (ZIP)""" | |
| with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as z: | |
| for root, dirs, files in os.walk(source_dir): | |
| for file in files: | |
| # Jangan masukkan file manifest root Unity atau file log | |
| if file.endswith((".manifest", ".txt")): continue | |
| file_path = os.path.join(root, file) | |
| z.write(file_path, os.path.relpath(file_path, source_dir)) | |
| def process_pipeline(req: ConversionRequest): | |
| session_id = str(uuid.uuid4())[:8] | |
| SESSION_PATH = f"/tmp/Project_{session_id}" | |
| extract_tmp = f"/tmp/extract_{session_id}" | |
| final_zepeto = f"/tmp/{req.filename}.zepeto" | |
| try: | |
| # 1. Download & Extract Repository | |
| r = requests.get(req.file_url) | |
| zip_tmp = f"/tmp/{session_id}.zip" | |
| with open(zip_tmp, 'wb') as f: f.write(r.content) | |
| os.makedirs(extract_tmp) | |
| with zipfile.ZipFile(zip_tmp, 'r') as z: z.extractall(extract_tmp) | |
| # 2. Setup Unity Project & Assets | |
| shutil.copytree(BASE_PROJECT_PATH, SESSION_PATH, ignore=shutil.ignore_patterns("Library", "Temp")) | |
| target_input = os.path.join(SESSION_PATH, "Assets", "InputRaw") | |
| os.makedirs(target_input, exist_ok=True) | |
| # Pindahkan SEMUA file asli (PNG, Meta, Prefab) ke InputRaw | |
| for item in os.listdir(extract_tmp): | |
| s = os.path.join(extract_tmp, item) | |
| d = os.path.join(target_input, item) | |
| if os.path.isdir(s): shutil.copytree(s, d) | |
| else: shutil.copy2(s, d) | |
| # 3. Jalankan Unity (Rimpang Poligon & Build Binary Platform) | |
| print(f"π [UNITY] Merampingkan Mesh & Membangun Platform...", flush=True) | |
| subprocess.run([os.environ["UNITY_PATH"], "-batchmode", "-nographics", "-quit", | |
| "-projectPath", SESSION_PATH, "-executeMethod", "TestBuilder.ManualConvert", | |
| "-logFile", "/dev/stdout"], check=False) | |
| # 4. PACKING (Membungkus folder InputRaw yang sudah lengkap menjadi .zepeto) | |
| print(f"π¦ [PACKING] Membuat archive {req.filename}.zepeto...", flush=True) | |
| make_zepeto_archive(target_input, final_zepeto) | |
| # 5. UPLOAD HASIL KE R2 | |
| if os.path.exists(final_zepeto): | |
| upload_to_r2(final_zepeto, f"{req.filename}.zepeto") | |
| else: | |
| print("β [FAIL] Gagal membuat file .zepeto", flush=True) | |
| finally: | |
| for p in [SESSION_PATH, extract_tmp, zip_tmp, final_zepeto]: | |
| if os.path.exists(p): shutil.rmtree(p) if os.path.isdir(p) else os.remove(p) | |
| async def api_convert(req: ConversionRequest, background_tasks: BackgroundTasks): | |
| background_tasks.add_task(process_pipeline, req) | |
| return {"status": "success"} |