webzepetoku commited on
Commit
ebd67c4
·
verified ·
1 Parent(s): 6e9c4f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -59
app.py CHANGED
@@ -1,24 +1,24 @@
1
- import os, shutil, subprocess, requests, zipfile, uuid, boto3, glob
 
 
 
 
 
 
 
2
  from fastapi import FastAPI, BackgroundTasks
3
  from fastapi.responses import FileResponse, JSONResponse
4
  from pydantic import BaseModel
5
- from datetime import datetime
6
 
 
7
  os.chdir("/tmp")
8
  os.environ["HOME"] = "/tmp"
9
  os.environ["UNITY_PATH"] = "/opt/unity/Editor/Unity"
10
  BASE_PROJECT_PATH = "/app/UnityProject"
11
-
12
- R2_CONFIG = {
13
- "account_id": "31f2970a3442da4e6f17f79064897226",
14
- "access_key": "44613d17ec50941707a7ddd9b17944bc",
15
- "secret_key": "50185c21ff5e4f53e412e2013305b6ab24b7eda16cc0d3dc66593eed64aa99a2",
16
- "bucket_name": "zepeto-builds"
17
- }
18
 
19
  app = FastAPI()
20
 
21
- # Model disesuaikan dengan payload Worker yang kamu kirim tadi
22
  class ConversionRequest(BaseModel):
23
  transaction_id: str
24
  file_url: str
@@ -26,82 +26,146 @@ class ConversionRequest(BaseModel):
26
  z_id: str
27
  z_pw: str
28
 
29
- def make_structured_zepeto(source_dir, output_file):
30
- # Logika zipping asli yang kamu berikan (Berhasil)
31
- with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as z:
32
- for root, _, files in os.walk(source_dir):
33
- for file in files:
34
- if file.endswith(".manifest"): continue
35
- full_path = os.path.join(root, file)
36
- rel_path = os.path.relpath(full_path, source_dir)
37
- z.write(full_path, rel_path)
38
 
39
  def setup_license():
40
- source_ulf = "/app/Unity_lic.ulf"
 
 
 
 
41
  target_dir = "/tmp/.local/share/unity3d/Unity"
42
- if os.path.exists(source_ulf):
43
- os.makedirs(target_dir, exist_ok=True)
44
- shutil.copy2(source_ulf, os.path.join(target_dir, "Unity_lic.ulf"))
45
- print("✅ [LICENSE] Lisensi terpasang.", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  @app.get("/lisensi")
48
- async def get_alf():
49
- for f in glob.glob("/tmp/*.alf"): os.remove(f)
50
- subprocess.run([os.environ["UNITY_PATH"], "-batchmode", "-nographics", "-createManualActivationFile", "-logfile", "/dev/stdout"], cwd="/tmp")
51
- found = glob.glob("/tmp/*.alf")
52
- if found: return FileResponse(path=found[0], filename=os.path.basename(found[0]))
53
- return JSONResponse(status_code=500, content={"error": "Gagal buat .alf"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  def process_pipeline(req: ConversionRequest):
56
  setup_license()
57
 
58
- # Tambahkan timestamp sesuai permintaan sebelumnya agar file unik
59
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
60
- unique_filename = f"{req.filename}_{timestamp}"
61
-
62
- # Gunakan transaction_id untuk folder sesi agar tidak tabrakan
63
  SESSION_PATH = f"/tmp/Project_{req.transaction_id}"
64
  extract_tmp = f"/tmp/extract_{req.transaction_id}"
65
  zip_tmp = f"/tmp/{req.transaction_id}.zip"
66
- final_zepeto = f"/tmp/{unique_filename}.zepeto"
67
 
68
  try:
69
- # Download
70
- r = requests.get(req.file_url)
71
- open(zip_tmp, 'wb').write(r.content)
 
 
72
 
73
  os.makedirs(extract_tmp, exist_ok=True)
74
- with zipfile.ZipFile(zip_tmp, 'r') as z: z.extractall(extract_tmp)
 
75
 
76
- # Setup Project
 
77
  shutil.copytree(BASE_PROJECT_PATH, SESSION_PATH, ignore=shutil.ignore_patterns("Library", "Temp"))
78
  target_in = os.path.join(SESSION_PATH, "Assets/InputRaw")
79
  os.makedirs(target_in, exist_ok=True)
80
- for item in os.listdir(extract_tmp): shutil.move(os.path.join(extract_tmp, item), os.path.join(target_in, item))
 
 
 
81
 
82
- print(f"🚀 [UNITY] Memulai Export untuk {req.transaction_id}...", flush=True)
83
- subprocess.run([os.environ["UNITY_PATH"], "-batchmode", "-nographics", "-quit", "-projectPath", SESSION_PATH, "-executeMethod", "TestBuilder.ManualConvert", "-logFile", "/dev/stdout"], capture_output=False)
 
84
 
85
- export_dir = os.path.join(SESSION_PATH, "Assets/FinalPackage")
86
- if os.path.exists(export_dir) and len(os.listdir(export_dir)) > 0:
87
- make_structured_zepeto(export_dir, final_zepeto)
88
- 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'])
89
-
90
- r2_path = f"packed/{unique_filename}.zepeto"
91
- s3.upload_file(final_zepeto, R2_CONFIG['bucket_name'], r2_path)
92
- print(f"✅ [SUCCESS] File dikirim ke R2: {r2_path}", flush=True)
 
 
 
 
 
 
 
 
 
93
 
 
 
 
94
  finally:
95
- for p in [SESSION_PATH, extract_tmp, zip_tmp, final_zepeto]:
96
- if os.path.exists(p):
 
 
97
  if os.path.isdir(p): shutil.rmtree(p)
98
  else: os.remove(p)
99
 
100
- @app.post("/convert")
101
- async def api_convert(req: ConversionRequest, background_tasks: BackgroundTasks):
102
- background_tasks.add_task(process_pipeline, req)
103
- return {"status": "processing", "transaction_id": req.transaction_id}
104
-
105
  if __name__ == "__main__":
106
  import uvicorn
 
107
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import requests
5
+ import zipfile
6
+ import uuid
7
+ import glob
8
+ import datetime
9
  from fastapi import FastAPI, BackgroundTasks
10
  from fastapi.responses import FileResponse, JSONResponse
11
  from pydantic import BaseModel
 
12
 
13
+ # --- KONFIGURASI PATH ---
14
  os.chdir("/tmp")
15
  os.environ["HOME"] = "/tmp"
16
  os.environ["UNITY_PATH"] = "/opt/unity/Editor/Unity"
17
  BASE_PROJECT_PATH = "/app/UnityProject"
18
+ LICENSE_SOURCE = "/app/Unity_lic.ulf"
 
 
 
 
 
 
19
 
20
  app = FastAPI()
21
 
 
22
  class ConversionRequest(BaseModel):
23
  transaction_id: str
24
  file_url: str
 
26
  z_id: str
27
  z_pw: str
28
 
29
+ # --- FUNGSI PEMBANTU ---
 
 
 
 
 
 
 
 
30
 
31
  def setup_license():
32
+ """Menyiapkan lisensi dan mengecek waktu server untuk debug."""
33
+ # 1. Cek Waktu Server (Penting untuk mengatasi 'Time validation failed')
34
+ now = datetime.datetime.now()
35
+ print(f"⏰ [DEBUG] Waktu Server Sekarang: {now.strftime('%Y-%m-%d %H:%M:%S')}", flush=True)
36
+
37
  target_dir = "/tmp/.local/share/unity3d/Unity"
38
+ os.makedirs(target_dir, exist_ok=True)
39
+
40
+ if os.path.exists(LICENSE_SOURCE):
41
+ # Bersihkan lisensi lama di folder internal jika ada
42
+ internal_license = os.path.join(target_dir, "Unity_lic.ulf")
43
+ if os.path.exists(internal_license):
44
+ os.remove(internal_license)
45
+
46
+ shutil.copy2(LICENSE_SOURCE, internal_license)
47
+ print("✅ [SYSTEM] Lisensi disalin ke folder internal Unity.", flush=True)
48
+ else:
49
+ print("⚠️ [WARNING] File Unity_lic.ulf tidak ditemukan di /app/", flush=True)
50
+
51
+ def run_unity_command(project_path, method, extra_args=None):
52
+ """Menjalankan Unity dengan flag headless dan anti-stuck."""
53
+ cmd = [
54
+ os.environ["UNITY_PATH"],
55
+ "-batchmode",
56
+ "-nographics",
57
+ "-quit",
58
+ "-no-webservices", # Matikan pencarian layanan cloud
59
+ "-no-graphics", # Proteksi tambahan mode headless
60
+ "-silent-crashes", # Jangan munculkan popup error
61
+ "-manualLicenseFile", LICENSE_SOURCE, # Paksa baca file lisensi fisik
62
+ "-projectPath", project_path,
63
+ "-executeMethod", method,
64
+ "-logFile", "-" # Log keluar secara real-time ke console
65
+ ]
66
+ if extra_args:
67
+ cmd.extend(extra_args)
68
+
69
+ # Jalankan perintah dan tunggu sampai selesai
70
+ return subprocess.run(cmd, capture_output=False)
71
+
72
+ # --- ENDPOINTS ---
73
 
74
  @app.get("/lisensi")
75
+ async def generate_new_alf():
76
+ """Endpoint untuk generate file .alf baru jika lisensi lama Expired."""
77
+ # Bersihkan sisa file .alf sebelumnya
78
+ for f in glob.glob("/tmp/*.alf"):
79
+ try: os.remove(f)
80
+ except: pass
81
+
82
+ print("🛠️ [LICENSE] Meminta file aktivasi (.alf) baru dari Unity...", flush=True)
83
+ cmd = [
84
+ os.environ["UNITY_PATH"],
85
+ "-batchmode", "-nographics",
86
+ "-createManualActivationFile",
87
+ "-logfile", "/dev/stdout"
88
+ ]
89
+ subprocess.run(cmd, cwd="/tmp", timeout=60)
90
+
91
+ found_alf = glob.glob("/tmp/*.alf")
92
+ if found_alf:
93
+ print(f"✅ [LICENSE] File .alf berhasil dibuat: {found_alf[0]}", flush=True)
94
+ return FileResponse(path=found_alf[0], filename=os.path.basename(found_alf[0]))
95
+
96
+ return JSONResponse(status_code=500, content={"error": "Gagal generate .alf. Cek tab Logs."})
97
+
98
+ @app.post("/convert")
99
+ async def api_convert(req: ConversionRequest, background_tasks: BackgroundTasks):
100
+ """Endpoint utama untuk memicu pipeline convert dan upload."""
101
+ background_tasks.add_task(process_pipeline, req)
102
+ return {"status": "processing", "transaction_id": req.transaction_id}
103
+
104
+ # --- INTI PROSES (PIPELINE) ---
105
 
106
  def process_pipeline(req: ConversionRequest):
107
  setup_license()
108
 
109
+ # Folder unik untuk setiap transaksi
 
 
 
 
110
  SESSION_PATH = f"/tmp/Project_{req.transaction_id}"
111
  extract_tmp = f"/tmp/extract_{req.transaction_id}"
112
  zip_tmp = f"/tmp/{req.transaction_id}.zip"
 
113
 
114
  try:
115
+ # 1. DOWNLOAD & PREPARE
116
+ print(f"📥 [START] Mengambil aset: {req.transaction_id}", flush=True)
117
+ r = requests.get(req.file_url, timeout=30)
118
+ with open(zip_tmp, 'wb') as f:
119
+ f.write(r.content)
120
 
121
  os.makedirs(extract_tmp, exist_ok=True)
122
+ with zipfile.ZipFile(zip_tmp, 'r') as z:
123
+ z.extractall(extract_tmp)
124
 
125
+ # 2. SETUP PROJECT WORKSPACE
126
+ # Salin UnityProject dasar ke folder temporer agar project asli tetap bersih
127
  shutil.copytree(BASE_PROJECT_PATH, SESSION_PATH, ignore=shutil.ignore_patterns("Library", "Temp"))
128
  target_in = os.path.join(SESSION_PATH, "Assets/InputRaw")
129
  os.makedirs(target_in, exist_ok=True)
130
+
131
+ # Pindahkan file hasil ekstrak ke folder input project
132
+ for item in os.listdir(extract_tmp):
133
+ shutil.move(os.path.join(extract_tmp, item), os.path.join(target_in, item))
134
 
135
+ # 3. TAHAP 1: PENYEDERHANAAN MESH
136
+ print(f"🛠️ [STEP 1] Menjalankan TestBuilder.SimplifyOnly...", flush=True)
137
+ step1 = run_unity_command(SESSION_PATH, "TestBuilder.SimplifyOnly")
138
 
139
+ if step1.returncode != 0:
140
+ print(f"❌ [FAILED] Step 1 Gagal (Exit Code: {step1.returncode}). Cek Logs di atas.", flush=True)
141
+ return
142
+
143
+ # 4. TAHAP 2: UPLOAD KE ZEPETO
144
+ print(f"🚀 [STEP 2] Menjalankan Zepeto CLI Upload...", flush=True)
145
+ upload_args = [
146
+ "-id", req.z_id,
147
+ "-password", req.z_pw,
148
+ "-upload"
149
+ ]
150
+ step2 = run_unity_command(SESSION_PATH, "ZepetoPackageCliBuilder.BuildWithArgs", upload_args)
151
+
152
+ if step2.returncode == 0:
153
+ print(f"✅ [SUCCESS] Item {req.transaction_id} telah terunggah ke ZEPETO Studio.", flush=True)
154
+ else:
155
+ print(f"❌ [FAILED] Step 2 Gagal (Exit Code: {step2.returncode}).", flush=True)
156
 
157
+ except Exception as e:
158
+ print(f"🔥 [CRITICAL ERROR] {str(e)}", flush=True)
159
+
160
  finally:
161
+ # PEMBERSIHAN (Wajib agar disk tidak penuh)
162
+ print(f"🧹 [CLEANUP] Menghapus folder sesi {req.transaction_id}...", flush=True)
163
+ for p in [SESSION_PATH, extract_tmp, zip_tmp]:
164
+ if os.path.exists(p):
165
  if os.path.isdir(p): shutil.rmtree(p)
166
  else: os.remove(p)
167
 
 
 
 
 
 
168
  if __name__ == "__main__":
169
  import uvicorn
170
+ # Jalankan server FastAPI
171
  uvicorn.run(app, host="0.0.0.0", port=7860)