envio de arquivo filnalizado.

main
henrique 4 weeks ago
parent 46d0ea6fda
commit 0e8cefea8d

@ -12,6 +12,10 @@ importlib.reload(vr4life_ui)
if __name__ == "__main__":
from pymxs import runtime as rt
app = vr4life_ui.AutoBakeManager()
rt.vr4_app = app
app.show()
# Previne que o Garbage Collector limpe a interface durante o Render pesado
global _vr4life_global_window
_vr4life_global_window = vr4life_ui.AutoBakeManager()
rt.vr4_app = _vr4life_global_window
_vr4life_global_window.show()

@ -0,0 +1 @@
{"api_hash": "c863105dd63bd3c46f24c3d0f42f44ec9a6348bf6543b76911c32ea73350d083815b8daf2df54e9473f49f57c788ee4af350c9c970ea7f737e517a510188c0e1"}

@ -1 +1 @@
[{"name": "3d66-Editable_Poly-23107237-414", "status": "Pronto", "polys": "27,680", "res": "2048px", "id": "979.0"}, {"name": "3d66-Editable_Poly-23107237-415", "status": "Pronto", "polys": "15,776", "res": "2048px", "id": "797.2"}, {"name": "3d66diban-020", "status": "Pronto", "polys": "600", "res": "2048px", "id": "861.0"}]
[]

@ -3,122 +3,255 @@ from pymxs import runtime as rt
try: from PySide6 import QtWidgets
except ImportError: from PySide2 import QtWidgets
import urllib.request
import urllib.parse
import json
def mock_connect_api(ui):
if len(ui.e_hash.text()) < 5: QtWidgets.QMessageBox.warning(ui, "Acesso Negado", "Hash inválida."); return
ui.b_conn.setText("Conectando..."); QtWidgets.QApplication.processEvents(); time.sleep(1)
for w in [ui.c_chn, ui.c_pst, ui.e_tit, ui.e_mp3, ui.b_mp3]: w.setEnabled(True); w.setStyleSheet("background: white; color: black; font-weight: bold;")
ui.c_chn.clear(); ui.c_chn.addItems(["Canal: Imóveis Virtuais", "Canal: Showroom Zombisco"])
ui.c_pst.clear(); ui.c_pst.addItems(["[ + Cadastrar Nova Postagem ]", "Editar: Apartamento V1"])
ui.chk_up.setEnabled(True); ui.chk_up.setStyleSheet("color: #FFD700; font-weight: bold;"); ui.chk_up.setChecked(True)
hash_txt = ui.e_hash.text().strip()
if len(hash_txt) < 5:
QtWidgets.QMessageBox.warning(ui, "Acesso Negado", "Hash inválida.")
return
ui.b_conn.setText("Conectando..."); QtWidgets.QApplication.processEvents()
# Faz chamada real para a API
url = "https://api.vr4life.com/public/listaCanais"
req_data = urllib.parse.urlencode({"tx_hash_login_externo": hash_txt}).encode('utf-8')
req = urllib.request.Request(url, data=req_data, headers={'Content-Type': 'application/x-www-form-urlencoded'})
try:
with urllib.request.urlopen(req, timeout=10) as response:
res_body = response.read().decode('utf-8')
canais = json.loads(res_body)
ui.c_chn.clear()
# Espera receber lista: [{"nm_canal": "...", "tx_hash_canal": "..."}, ...]
if isinstance(canais, list) and len(canais) > 0:
for c in canais:
n_c = str(c.get("nm_canal", "Canal Desconhecido"))
h_c = str(c.get("tx_hash_canal", ""))
ui.c_chn.addItem(n_c, h_c)
else:
raise Exception("Nenhum canal encontrado para este Hash.")
except Exception as e:
ui.b_conn.setText("🔄 Tentar Novamente")
QtWidgets.QMessageBox.critical(ui, "Erro de API", f"Falha ao conectar: {str(e)}")
return
# Ativa campos após sucesso
for w in [ui.c_chn, ui.rdo_add, ui.rdo_edt, ui.e_tit, ui.e_mp3, ui.b_mp3]:
w.setEnabled(True)
w.setStyleSheet("background: white; color: black; font-weight: bold;")
ui.b_up.setEnabled(True)
ui.b_conn.setText("✅ Conectado!"); ui.b_conn.setStyleSheet("background: #000; color: #0F0; font-weight: bold; border: 1px solid #0F0;")
on_post_changed(ui, 0)
def on_post_changed(ui, idx):
if idx == 0:
ui.e_tit.setText(""); ui.e_tit.setPlaceholderText("Título do projeto...")
ui.chk_tmb.setChecked(True); ui.chk_tmb.setEnabled(False); ui.chk_tmb.setStyleSheet("color: #00FF00; font-weight: bold;")
else:
ui.e_tit.setText(ui.c_pst.currentText().replace("Editar: ", ""))
ui.chk_tmb.setEnabled(True); ui.chk_tmb.setStyleSheet("color: white; font-weight: bold;")
# Auto-carrega postagens do primeiro canal
fetch_posts_for_channel(ui)
def fetch_posts_for_channel(ui):
ui.c_pst.blockSignals(True)
ui.c_pst.clear()
def finalize_export(ui, p_bk, p_glb):
if not os.path.exists(p_glb): os.makedirs(p_glb)
fo = []; rt.execute("max modify mode"); tgs = ui.get_processable_items()
hash_txt = ui.e_hash.text().strip()
idx = ui.c_chn.currentIndex()
has_posts = False
ui.pb.setFormat("Preparando materiais VR (Multi-ID)..."); QtWidgets.QApplication.processEvents()
if idx >= 0:
ch_hash = ui.c_chn.itemData(idx)
print(f"[DEBUG UI] Canal Index {idx}. Valor Extraído do ItemData: '{ch_hash}'")
if ch_hash and str(ch_hash).strip():
ch_hash = str(ch_hash).strip()
url = "https://api.vr4life.com/public/listaPostagemCanal"
dict_data = {"tx_hash_login_externo": hash_txt, "tx_hash_canal": ch_hash}
req_data = urllib.parse.urlencode(dict_data).encode('utf-8')
print(f"\n[DEBUG API] Chamando: {url}")
print(f"[DEBUG API] Payload (FormData): {dict_data}")
req = urllib.request.Request(url, data=req_data, headers={'Content-Type': 'application/x-www-form-urlencoded'})
for d in tgs:
if ui._is_cancelled: break
if "Bake" in d['item'].text(1) or "Já existe" in d['item'].text(1) or "Mat" in d['item'].text(1):
try:
max_id = rt.execute(f"""(local o = getNodeByName "{d['name']}"; local mid = 1; if o != undefined and isKindOf o Editable_Poly do ( for f = 1 to (polyop.getNumFaces o) do ( local id = polyop.getFaceMatID o f; if id > mid do mid = id ) ); mid)""")
if max_id > 1:
# CONSTRUTOR DE MULTI-MATERIAL (Para objetos que foram processados pelo P4: Multi-UV)
ms_mat = f"""(
local o = getNodeByName "{d['name']}"
local mm = MultiMaterial numsubs:{int(max_id)} name:(o.name + "_MultiMat")
for i = 1 to {int(max_id)} do (
local ip = @"{p_bk}/{d['name']}_B_ID" + (i as string) + ".jpg"
if doesFileExist ip do (
local m = PhysicalMaterial name:(o.name + "_MatID_" + (i as string))
m.base_color_map = BitmapTexture filename:ip
m.roughness = 1.0; m.metalness = 0.0; try(m.reflectivity = 0.0; m.reflection_weight = 0.0)catch()
mm[i] = m
)
)
o.material = mm
)"""
rt.execute(ms_mat)
d['item'].setText(1, "Mat OK (Multi)")
fo.append(rt.getNodeByName(d['name']))
else:
# MATERIAL PADRÃO (Para objetos normais que cabem numa textura só)
ip = os.path.join(p_bk, f"{d['name']}_B.jpg").replace("\\", "/")
if os.path.exists(ip):
rt.execute(f"""(
local o = getNodeByName "{d['name']}"
if o != undefined do (
local m = PhysicalMaterial name:(o.name + "_Mat"); m.base_color_map = BitmapTexture filename:@"{ip}"
m.roughness = 1.0; m.metalness = 0.0; try(m.reflectivity = 0.0; m.reflection_weight = 0.0)catch()
o.material = m
)
)""")
fo.append(rt.getNodeByName(d['name'])); d['item'].setText(1, "Mat OK")
with urllib.request.urlopen(req, timeout=10) as response:
raw_res = response.read().decode('utf-8')
posts = json.loads(raw_res)
if isinstance(posts, list) and len(posts) > 0:
has_posts = True
for p in posts:
t_p = p.get('tx_titulo', 'Sem Título')
h_p = p.get('hash_postagem', '')
print(f"[DEBUG API] Encontrou Postagem: '{t_p}' | Hash: {h_p}")
ui.c_pst.addItem(t_p, h_p)
except urllib.error.HTTPError as e:
err_body = e.read().decode('utf-8')
QtWidgets.QMessageBox.critical(ui, f"Erro HTTP {e.code}", f"O Servidor RECUSOU a requisição.\n\nResposta:\n{err_body}")
except Exception as e:
d['item'].setText(1, "Erro Mat"); print(f"Erro Material em {d['name']}: {e}")
QtWidgets.QMessageBox.warning(ui, "Aviso de Rede", f"Falha na comunicação ou falha lendo o JSON de postagens:\n\n{e}\n\n(A API pode ter retornado HTML ao invés de JSON puro?)")
else:
QtWidgets.QMessageBox.warning(ui, "Aviso", "O Hash deste canal não foi preenchido.")
ui.c_pst.blockSignals(False)
# Se não retornou posts, forçar modo "Novo" e bloquear edição
if not has_posts:
ui.rdo_add.setChecked(True)
ui.rdo_edt.setEnabled(False)
else:
ui.rdo_edt.setEnabled(True)
on_post_changed(ui)
def on_post_changed(ui):
if ui.rdo_add.isChecked():
ui.c_pst.setEnabled(False)
ui.c_pst.setStyleSheet("background: #CCC; color: black; font-weight: normal;")
ui.e_tit.setText(""); ui.e_tit.setPlaceholderText("Título do NOVO projeto...")
ui.b_tmb.setEnabled(False)
else:
ui.c_pst.setEnabled(True)
ui.c_pst.setStyleSheet("background: white; color: black; font-weight: bold;")
if ui.c_pst.count() > 0:
ui.e_tit.setText(ui.c_pst.currentText())
ui.b_tmb.setEnabled(True)
def finalize_export(ui, p_glb, f_name):
# Transição de abas após o término do Exportador V19
ui.tabs.setCurrentIndex(3)
def generate_thumbnail(ui):
p_glb = ui.edt_p_glb.text().replace("\\", "/")
if not p_glb.endswith("/"): p_glb += "/"
tgs = ui.get_processable_items()
if len(tgs) == 1: sn = f"{tgs[0]['name']}_Export"
else: sn = "VR4Life_Scene_Export"
tp = os.path.join(p_glb, f"{sn}_Thumb.jpg").replace("\\", "/")
ui.pb.setFormat("📸 Tirando Snapshot da Cena..."); QtWidgets.QApplication.processEvents()
rt.execute(f"""( local c = getActiveCamera(); if c == undefined do ( local ac = for cam in cameras where (classof cam != TargetObject) collect cam; if ac.count > 0 do c = ac[1] ); try ( if c != undefined then ( render camera:c outputwidth:1280 outputheight:720 outputfile:@"{tp}" vfb:false quiet:true ) else ( render outputwidth:1280 outputheight:720 outputfile:@"{tp}" vfb:false quiet:true ) ) catch() )""")
ui.pb.setFormat("✅ Thumbnail Gerado e Salvo!"); ui.pb.setValue(100)
if not fo and not ui._is_cancelled:
QtWidgets.QMessageBox.warning(ui, "Exportação Interrompida", "Nenhum objeto validado para exportação.")
try: os.startfile(p_glb)
except: pass
def upload_to_cloud(ui):
p_glb = ui.edt_p_glb.text().replace("\\", "/")
if not p_glb.endswith("/"): p_glb += "/"
tgs = ui.get_processable_items()
if len(tgs) == 1: sn = f"{tgs[0]['name']}_Export"
else: sn = "VR4Life_Scene_Export"
oz = os.path.join(p_glb, f"{sn}.zip").replace("\\", "/")
tp = os.path.join(p_glb, f"{sn}_Thumb.jpg").replace("\\", "/")
if not os.path.exists(oz):
try:
zip_files = [os.path.join(p_glb, f) for f in os.listdir(p_glb) if f.lower().endswith('.zip')]
if zip_files:
oz = max(zip_files, key=os.path.getmtime)
base_zip_name = os.path.basename(oz).replace(".zip", "")
alt_tp = os.path.join(p_glb, f"{base_zip_name}_Thumb.jpg")
if os.path.exists(alt_tp): tp = alt_tp
except Exception as e: print(f"Erro ao buscar zip recente: {e}")
if not os.path.exists(oz):
QtWidgets.QMessageBox.warning(ui, "Aviso", f"Nenhum arquivo ZIP encontrado na pasta:\n{p_glb}\n\nFaça o Bake primeiro!")
return
ui.pb.setFormat(f"📡 API: Sincronizando com a Nuvem..."); QtWidgets.QApplication.processEvents()
hx_login = ui.e_hash.text().strip()
ch_idx = ui.c_chn.currentIndex()
hx_canal = ui.c_chn.itemData(ch_idx) if ch_idx >= 0 else ""
titulo = ui.e_tit.text().strip()
hx_postagem = ""
if ui.rdo_edt.isChecked():
pst_idx = ui.c_pst.currentIndex()
if pst_idx >= 0: hx_postagem = ui.c_pst.itemData(pst_idx)
if not hx_login or not hx_canal or not titulo:
QtWidgets.QMessageBox.warning(ui, "Campos Inválidos", "Por favor conecte-se à API, selecione um Canal e digite um Título antes de enviar!")
ui.pb.setFormat("Pronto"); ui.pb.setValue(0)
return
if fo and not ui._is_cancelled:
rt.select(fo); sn = rt.maxFileName.split(".")[0] if rt.maxFileName else "Cena_VR4LIFE"
og = os.path.join(p_glb, f"{sn}.glb").replace("\\", "/"); oz = os.path.join(p_glb, f"{sn}.zip").replace("\\", "/")
tp = os.path.join(p_glb, f"{sn}_Thumb.jpg").replace("\\", "/"); mp = ui.e_mp3.text(); hm = os.path.exists(mp)
to = False
if ui.chk_tmb.isChecked() and ui.chk_tmb.isEnabled():
ui.pb.setFormat("📸 Thumbnail (1280x720)..."); QtWidgets.QApplication.processEvents()
rt.execute(f"""( local c = getActiveCamera(); if c == undefined do ( local ac = for cam in cameras where (classof cam != TargetObject) collect cam; if ac.count > 0 do c = ac[1] ); try ( if c != undefined then ( render camera:c outputwidth:1280 outputheight:720 outputfile:@"{tp}" vfb:false quiet:true ) else ( render outputwidth:1280 outputheight:720 outputfile:@"{tp}" vfb:false quiet:true ) ) catch() )""")
if os.path.exists(tp): to = True
ui.pb.setFormat("Limpando Hierarquia e XForm..."); QtWidgets.QApplication.processEvents()
rt.execute("""
(
local sel = selection as array
for o in sel do (
o.parent = undefined
ResetXForm o
collapseStack o
if not isKindOf o Editable_Poly do try(convertToPoly o)catch()
)
)
""")
ui.pb.setFormat("Montando GLB..."); QtWidgets.QApplication.processEvents()
try: rt.exportFile(og, rt.name("noPrompt"), selectedOnly=True, using=rt.GLTFExporter)
except: rt.execute(f'exportFile "{og}" #noPrompt selectedOnly:true')
zo = False
if os.path.exists(og):
ui.pb.setFormat("🗜️ ZIPando GLB..."); QtWidgets.QApplication.processEvents()
try:
with zipfile.ZipFile(oz, 'w', zipfile.ZIP_DEFLATED) as zf: zf.write(og, os.path.basename(og))
zo = True
except: pass
if ui.chk_up.isChecked() and ui.chk_up.isEnabled() and zo:
cs = ui.c_chn.currentText(); ac = "NOVO" if ui.c_pst.currentIndex() == 0 else "ATUALIZAR"; tit = ui.e_tit.text()
ui.pb.setFormat(f"📡 API: Enviando..."); QtWidgets.QApplication.processEvents(); time.sleep(1.5)
msg = f"Sincronizado!\n\n📡 Canal: {cs}\n📝 Ação: {ac}\n🏷️ Titulo: {tit}\n\n📦 ZIP: {os.path.basename(oz)}\n📸 Thumb: {'Sim' if to else 'Não'}\n🎵 Áudio: {'Sim' if hm else 'Não'}"
QtWidgets.QMessageBox.information(ui, "Cloud API", msg)
else:
msg = f"Exportado:\n{og}";
if zo: msg += f"\n\n🗜️ ZIP:\n{oz}"
if to: msg += f"\n\n📸 Thumb:\n{tp}"
QtWidgets.QMessageBox.information(ui, "Sucesso", msg)
import mimetypes
boundary = '----WebKitFormBoundaryVR4PluginX1A'
data_parts = []
def add_text_field(name, value):
data_parts.append(f'--{boundary}\r\n'.encode('utf-8'))
data_parts.append(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode('utf-8'))
data_parts.append(f'{value}\r\n'.encode('utf-8'))
def add_file_field(name, filename, filepath):
try:
mime_type = mimetypes.guess_type(filepath)[0] or 'application/octet-stream'
with open(filepath, 'rb') as f:
file_data = f.read()
data_parts.append(f'--{boundary}\r\n'.encode('utf-8'))
data_parts.append(f'Content-Disposition: form-data; name="{name}"; filename="{filename}"\r\n'.encode('utf-8'))
data_parts.append(f'Content-Type: {mime_type}\r\n\r\n'.encode('utf-8'))
data_parts.append(file_data)
data_parts.append(b'\r\n')
except Exception as e:
print(f"[DEBUG UPLOAD] Erro ao ler arquivo {filepath}: {e}")
add_text_field('tx_hash_login_externo', hx_login)
add_text_field('tx_hash_canal', hx_canal)
add_text_field('tx_titulo', titulo)
if ui.rdo_edt.isChecked() and hx_postagem:
add_text_field('tx_hash_postagem', hx_postagem)
if not os.path.exists(tp):
ui.pb.setFormat("📸 Tirando Snapshot Automático (Thumbnail)..."); QtWidgets.QApplication.processEvents()
rt.execute(f"""( local c = getActiveCamera(); if c == undefined do ( local ac = for cam in cameras where (classof cam != TargetObject) collect cam; if ac.count > 0 do c = ac[1] ); try ( if c != undefined then ( render camera:c outputwidth:1280 outputheight:720 outputfile:@"{tp}" vfb:false quiet:true ) else ( render outputwidth:1280 outputheight:720 outputfile:@"{tp}" vfb:false quiet:true ) ) catch() )""")
if os.path.exists(tp):
add_file_field('thumbnail', os.path.basename(tp), tp)
if os.path.exists(oz):
add_file_field('arquivo', os.path.basename(oz), oz)
data_parts.append(f'--{boundary}--\r\n'.encode('utf-8'))
body = b''.join(data_parts)
url = "https://api.vr4life.com/public/savePost"
req = urllib.request.Request(url, data=body)
req.add_header('Content-Type', f'multipart/form-data; boundary={boundary}')
req.add_header('Content-Length', str(len(body)))
print("\n" + "="*50)
print("📡 [DEBUG API UPLOAD] DADOS ENVIADOS NO FORM-DATA:")
print(f" -> tx_hash_login_externo: {hx_login}")
print(f" -> tx_hash_canal : {hx_canal}")
print(f" -> tx_titulo : {titulo}")
# Se campo não existe, printa vazio pra entender o contexto
if ui.rdo_edt.isChecked() and hx_postagem:
print(f" -> tx_hash_postagem : {hx_postagem}")
print(f" -> thumbnail (ARQUIVO) : {tp if os.path.exists(tp) else 'NAO ENVIADO'}")
print(f" -> arquivo (ARQUIVO) : {oz if os.path.exists(oz) else 'NAO ENVIADO'}")
print(f"--------------------------------------------------")
print(f"Disparando pacote de {len(body)} bytes para {url} ...")
print("="*50 + "\n")
ui.b_up.setEnabled(False); ui.b_up.setText("Upload em andamento...")
QtWidgets.QApplication.processEvents()
try:
with urllib.request.urlopen(req, timeout=120) as response:
raw_res = response.read().decode('utf-8')
print(f"[DEBUG API UPLOAD] Sucesso HTTP 200:\n{raw_res}")
QtWidgets.QMessageBox.information(ui, "Sincronização Cloud Concluída", f"O projeto foi enviado com sucesso para a plataforma!\n\nDados Retornados da API:\n{raw_res}")
except urllib.error.HTTPError as e:
err_body = e.read().decode('utf-8')
print(f"[DEBUG API UPLOAD] Erro HTTP {e.code}:\n{err_body}")
QtWidgets.QMessageBox.critical(ui, "Sincronização Falhou", f"O PHP Recusou o Arquivo (HTTP {e.code}).\n\nMotivo:\n{err_body}")
except Exception as e:
print(f"[DEBUG API UPLOAD] Exceção: {e}")
QtWidgets.QMessageBox.warning(ui, "Aviso de Rede", f"Não foi possível completar o envio do arquivo Zip para o servidor.\n\nDetalhes:\n{e}")
finally:
ui.b_up.setEnabled(True); ui.b_up.setText("☁️ Enviar para Nuvem")
ui.pb.setFormat("Pronto"); ui.pb.setValue(0)

@ -396,6 +396,13 @@ def process_bake_logic_v19(ui, auto_export=False):
res_str = d['item'].text(3).replace("px", "")
sz = int(res_str) if res_str.isdigit() else res_val
expected_tex = os.path.join(p_bk, f"{obj_name}_Baked.jpg").replace("\\", "/")
if os.path.exists(expected_tex):
mprint(f"--- Ignorando {obj_name} (Ja Bakeado) ---")
d['item'].setText(1, "OK (Salvo)")
d['item'].setForeground(1, QtGui.QColor(0, 255, 0))
continue
mprint(f"--- Iniciando script V19 portado para: {obj_name} ---")
ms_v19_block = f"""(
@ -661,6 +668,27 @@ def export_glb_v19(ui):
res = rt.execute(ms_export)
if res == True:
mprint("✅ SUCESSO: GLB Gerado com Texturas Embutidas (Modo Silencioso)!")
# O Nome do arquivo destino completo e o GLB gerado:
full_path_glb = os.path.join(p_glb, f_name + ".glb").replace("\\", "/")
full_path_zip = os.path.join(p_glb, f_name + ".zip").replace("\\", "/")
full_path_tmb = os.path.join(p_glb, f_name + "_Thumb.jpg").replace("\\", "/") # Thumbnail adivinhada
if os.path.exists(full_path_glb):
mprint(f"🗜️ Empacotando GLB em Arquivo ZIP... ({full_path_zip})")
import zipfile
try:
with zipfile.ZipFile(full_path_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.write(full_path_glb, os.path.basename(full_path_glb))
pass # Se quiséssemos embutir o Thumb no zip, fariamos: if os.path.exists(full_path_tmb): zf.write(full_path_tmb, os.path.basename(full_path_tmb))
mprint(f"📦 ZIP Criado com Sucesso!")
except Exception as e:
mprint(f"🚨 ERRO ao comprimir ZIP: {e}")
# Movemos para a tela de CLOUD e batemos a foto Thumbnail baseada no nome f_name
import vr4life_cloud as cld
cld.finalize_export(ui, p_glb, f_name)
else:
mprint("🚨 ERRO: Falha na geracao. Vefique o log.")

@ -22,7 +22,7 @@ class AutoBakeManager(QtWidgets.QDialog):
def __init__(self):
super(AutoBakeManager, self).__init__(get_max_window_safe())
self.setWindowTitle("VR4LIFE AUTO-BAKE V313 - MODULAR ENTERPRISE")
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint | QtCore.Qt.WindowCloseButtonHint)
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowStaysOnTopHint)
self.resize(570, 890); self.bake_items = []; self._is_cancelled = False
p = self.palette(); p.setColor(QtGui.QPalette.Window, QtGui.QColor(43, 43, 43)); self.setPalette(p); self.setAutoFillBackground(True)
self.init_ui()
@ -40,7 +40,7 @@ class AutoBakeManager(QtWidgets.QDialog):
# ABA 1
t_gen = QtWidgets.QWidget(); l_gen = QtWidgets.QVBoxLayout(t_gen); f_gen = QtWidgets.QFormLayout()
self.btn_ref = QtWidgets.QPushButton("🔄 RECARREGAR MAPAS"); self.btn_ref.setStyleSheet("background: #555; color: white; height: 35px;"); self.btn_ref.clicked.connect(lambda: eng.load_bake_elements(self))
self.btn_ref = QtWidgets.QPushButton("🔄 RECARREGAR MAPAS"); self.btn_ref.setStyleSheet("background: #444; color: white; border: 1px solid #222; font-weight: bold; height: 35px;"); self.btn_ref.clicked.connect(lambda: eng.load_bake_elements(self))
self.cmb_bake_elem = QtWidgets.QComboBox(); self.cmb_bake_elem.setStyleSheet("background: white; color: black; font-weight: bold;")
self.chk_denoise = QtWidgets.QCheckBox("✨ Ativar Denoiser IA"); self.chk_denoise.setStyleSheet("color: #00FFFF; font-weight: bold;"); self.chk_denoise.setChecked(True)
self.spn_light_boost = QtWidgets.QSpinBox(); self.spn_light_boost.setStyleSheet("background: white; color: black; font-weight: bold;"); self.spn_light_boost.setRange(0, 50); self.spn_light_boost.setSuffix("%"); self.spn_light_boost.setToolTip("Aumenta temporariamente a intensidade de todas as luzes da cena para o Bake.")
@ -89,18 +89,57 @@ class AutoBakeManager(QtWidgets.QDialog):
l_tex.addLayout(f_tex); l_tex.addStretch(); self.tabs.addTab(t_tex, "🎨 3. Textura")
t_cld = QtWidgets.QWidget(); l_cld = QtWidgets.QVBoxLayout(t_cld); f_cld = QtWidgets.QFormLayout()
self.config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "vr4_config.json")
saved_hash = ""
if os.path.exists(self.config_path):
try:
with open(self.config_path, "r", encoding="utf-8") as f: saved_hash = json.load(f).get("api_hash", "")
except: pass
self.e_hash = QtWidgets.QLineEdit(); self.e_hash.setStyleSheet("background: white; color: black; letter-spacing: 2px;"); self.e_hash.setEchoMode(QtWidgets.QLineEdit.Password)
self.b_conn = QtWidgets.QPushButton("🔄 Conectar CMS"); self.b_conn.setStyleSheet("background: #2E8B57; color: white; height: 30px;"); self.b_conn.clicked.connect(lambda: cld.mock_connect_api(self))
self.e_hash.setText(saved_hash)
def save_hash_config():
try:
with open(self.config_path, "w", encoding="utf-8") as f: json.dump({"api_hash": self.e_hash.text()}, f)
except: pass
self.e_hash.textChanged.connect(save_hash_config)
self.b_conn = QtWidgets.QPushButton("🔄 Conectar CMS"); self.b_conn.setStyleSheet("background: #2E8B57; color: white; font-weight: bold; border: 1px solid #1c5234; height: 30px;"); self.b_conn.clicked.connect(lambda: cld.mock_connect_api(self))
self.c_chn = QtWidgets.QComboBox(); self.c_chn.setStyleSheet("background: #CCC; color: black;"); self.c_chn.setEnabled(False)
self.c_pst = QtWidgets.QComboBox(); self.c_pst.setStyleSheet("background: #CCC; color: black;"); self.c_pst.setEnabled(False); self.c_pst.currentIndexChanged.connect(lambda idx: cld.on_post_changed(self, idx))
def ui_do_fetch(idx=None): cld.fetch_posts_for_channel(self)
self.c_chn.currentIndexChanged.connect(ui_do_fetch)
self.rdo_add = QtWidgets.QRadioButton("Novo Projeto"); self.rdo_add.setStyleSheet("color: white; font-weight: bold;"); self.rdo_add.setChecked(True); self.rdo_add.setEnabled(False)
self.rdo_edt = QtWidgets.QRadioButton("Atualizar Antigo"); self.rdo_edt.setStyleSheet("color: white; font-weight: bold;"); self.rdo_edt.setEnabled(False)
def ui_do_post_change(*args): cld.on_post_changed(self)
def ui_do_fetch_edt(checked):
if checked: cld.fetch_posts_for_channel(self)
self.rdo_add.toggled.connect(ui_do_post_change)
self.rdo_edt.toggled.connect(ui_do_post_change)
self.rdo_edt.toggled.connect(ui_do_fetch_edt)
h_act = QtWidgets.QHBoxLayout(); h_act.addWidget(self.rdo_add); h_act.addWidget(self.rdo_edt)
self.c_pst = QtWidgets.QComboBox(); self.c_pst.setStyleSheet("background: #CCC; color: black;"); self.c_pst.setEnabled(False)
self.c_pst.currentIndexChanged.connect(ui_do_post_change)
self.e_tit = QtWidgets.QLineEdit(); self.e_tit.setStyleSheet("background: #CCC; color: black;"); self.e_tit.setEnabled(False)
self.e_mp3 = QtWidgets.QLineEdit(); self.e_mp3.setStyleSheet("background: #CCC; color: black;"); self.e_mp3.setEnabled(False)
self.b_mp3 = QtWidgets.QPushButton("🎵 MP3"); self.b_mp3.setStyleSheet("background: #555; color: white;"); self.b_mp3.setEnabled(False); self.b_mp3.clicked.connect(self.browse_mp3)
h_mp3 = QtWidgets.QHBoxLayout(); h_mp3.addWidget(self.e_mp3); h_mp3.addWidget(self.b_mp3)
self.chk_tmb = QtWidgets.QCheckBox("📸 Thumbnail"); self.chk_tmb.setStyleSheet("color: gray; font-weight: bold;"); self.chk_tmb.setEnabled(False)
self.chk_up = QtWidgets.QCheckBox("🚀 Upload Multipart Automático"); self.chk_up.setStyleSheet("color: gray; font-weight: bold;"); self.chk_up.setEnabled(False)
f_cld.addRow("Chave:", self.e_hash); f_cld.addRow("", self.b_conn); f_cld.addRow("Canal:", self.c_chn); f_cld.addRow("Ação:", self.c_pst); f_cld.addRow("Título:", self.e_tit); f_cld.addRow("Som:", h_mp3)
f_cld.addRow("", self.chk_tmb); f_cld.addRow("", self.chk_up)
self.b_tmb = QtWidgets.QPushButton("📸 Gerar Thumbnail"); self.b_tmb.setStyleSheet("background: #FF8C00; color: white; font-weight: bold; height: 35px; border-radius: 4px;"); self.b_tmb.setEnabled(False)
self.b_tmb.clicked.connect(lambda: cld.generate_thumbnail(self))
self.b_up = QtWidgets.QPushButton("☁️ Enviar para Nuvem"); self.b_up.setStyleSheet("background: #0078D7; color: white; font-weight: bold; height: 35px; border-radius: 4px;"); self.b_up.setEnabled(False)
self.b_up.clicked.connect(lambda: cld.upload_to_cloud(self))
h_bot = QtWidgets.QHBoxLayout(); h_bot.addWidget(self.b_tmb); h_bot.addWidget(self.b_up)
f_cld.addRow("Chave:", self.e_hash); f_cld.addRow("", self.b_conn); f_cld.addRow("Canal:", self.c_chn)
f_cld.addRow("Ação:", h_act); f_cld.addRow("Projetos:", self.c_pst)
f_cld.addRow("Título:", self.e_tit); f_cld.addRow("Som:", h_mp3)
f_cld.addRow("", h_bot)
l_cld.addLayout(f_cld); l_cld.addStretch(); self.tabs.addTab(t_cld, "☁️ 4. API Cloud")
layout.addWidget(self.tabs)
@ -148,10 +187,9 @@ class AutoBakeManager(QtWidgets.QDialog):
self.b5 = QtWidgets.QPushButton("P5: UV"); self.b5.setStyleSheet("background: #E0E0E0; color: black; font-weight: bold; height: 35px; border-radius: 4px;"); self.b5.clicked.connect(lambda: eng.prepare_mesh_v19(self))
self.b6 = QtWidgets.QPushButton("P6: Bake"); self.b6.setStyleSheet("background: #E0E0E0; color: black; font-weight: bold; height: 35px; border-radius: 4px;"); self.b6.clicked.connect(lambda: eng.process_bake_logic_v19(self, False))
self.b7 = QtWidgets.QPushButton("P7: Exportar"); self.b7.setStyleSheet("background: #FFD700; color: black; font-weight: bold; height: 35px; border-radius: 4px;"); self.b7.clicked.connect(lambda: eng.export_glb_v19(self))
self.b8 = QtWidgets.QPushButton("P8: Inspec."); self.b8.setStyleSheet("background: #9370DB; color: white; font-weight: bold; height: 35px; border-radius: 4px;"); self.b8.clicked.connect(lambda: eng.inspect_uv(self))
self.b_clear = QtWidgets.QPushButton("🗑️ Limpar"); self.b_clear.setStyleSheet("background: #666; color: white; font-weight: bold; height: 35px; border-radius: 4px;"); self.b_clear.clicked.connect(self.clear_list)
for b in [self.b1, self.b3, self.b5, self.b6, self.b7, self.b8, self.b_clear]: h_l.addWidget(b)
for b in [self.b_clear, self.b1, self.b3, self.b5, self.b6, self.b7]: h_l.addWidget(b)
layout.addLayout(h_l)
self.btn_cancel = QtWidgets.QPushButton("CANCELAR OPERAÇÃO"); self.btn_cancel.setStyleSheet("background: #FF0000; color: white; font-weight: bold; height: 40px; border-radius: 8px; margin-top: 10px;"); self.btn_cancel.clicked.connect(self.cancel_all)

Loading…
Cancel
Save