import os, time, zipfile 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): 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;") # 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() hash_txt = ui.e_hash.text().strip() idx = ui.c_chn.currentIndex() has_posts = False 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'}) try: 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: 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) 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 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)