@danhachuel/thunderbolt 0.2.24 → 0.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Este manual descreve a instalação local da UI Thunderbolt, baseada no MoneyPrinterTurbo, utilizando o pacote npm `@danhachuel/thunderbolt`. O fluxo recomendado instala automaticamente o ambiente Python, as dependências da aplicação, as dependências do MoneyPrinterTurbo, o Streamlit e o suporte FFmpeg através de `imageio-ffmpeg`.
4
4
 
5
- > **Versão deste manual:** 0.2.24
5
+ > **Versão deste manual:** 0.2.26
6
6
  > **Pacote npm:** `@danhachuel/thunderbolt`
7
7
  > **Porta padrão da UI:** `localhost:3030`
8
8
  > **Repositório:** [github.com/DanHachuel/thunderbolt](https://github.com/DanHachuel/thunderbolt)
@@ -100,13 +100,13 @@ Execute:
100
100
  Windows PowerShell ou MobaXterm:
101
101
 
102
102
  ```powershell
103
- npx.cmd --yes @danhachuel/thunderbolt@0.2.24 install
103
+ npx.cmd --yes @danhachuel/thunderbolt@0.2.26 install
104
104
  ```
105
105
 
106
106
  Linux/macOS:
107
107
 
108
108
  ```bash
109
- npx --yes @danhachuel/thunderbolt@0.2.24 install
109
+ npx --yes @danhachuel/thunderbolt@0.2.26 install
110
110
  ```
111
111
 
112
112
  A instalação normal é **segura para actualizações**: preserva `storage`, Blueprints, Brandings, configurações e artefactos do utilizador. Remove apenas `.venv`, o clone técnico do MoneyPrinterTurbo e dependências que serão recriadas. Uma pasta antiga sem dados do utilizador, como `C:\Users\<utilizador>\AppData\Local\hermes` da tentativa incompleta, pode ser removida; uma pasta antiga que contenha Blueprints, Brandings ou storage é preservada e apenas avisada no terminal. Feche processos Python, Node, Streamlit e MobaXterm que estejam a usar as pastas antes de executar.
@@ -380,7 +380,7 @@ O modo `Pexels/Pixabay` representa materiais de stock. Ao seleccionar `full_ia`,
380
380
 
381
381
  A aba **Automação** lista os canais e vídeos, permite escolher e guardar **Blueprint padrão** e **Voz padrão** por canal, guarda **Automação ON** e valida horários diários no formato `HH:MM`. Os valores ficam sincronizados com o editor existente no cartão da aba **Canais** e são copiados para novas tarefas. É apenas uma camada de UI nesta versão, sem execução de worker.
382
382
 
383
- A área **Teste de vozes**, dentro de Configurações, é isolada da pipeline. O preview pode ser reproduzido e descarregado de `storage/voice_previews/`.
383
+ A área **Teste de vozes**, dentro de Configurações, é isolada da pipeline. O preview pode ser reproduzido e descarregado de `storage/voice_previews/`. Caminhos vazios, directórios, ficheiros vazios e previews sem permissões de leitura são ignorados com uma mensagem clara, sem traceback. Se uma instalação antiga mostrar que `edge-tts` está em falta, execute `npx.cmd --yes @danhachuel/thunderbolt install`; o detector reinstala apenas a dependência ausente.
384
384
 
385
385
  ## Upload directo
386
386
 
package/README.md CHANGED
@@ -104,9 +104,9 @@ thunderbolt
104
104
  No Windows PowerShell, se `npx` for bloqueado por `npx.ps1`, use directamente `npx.cmd`:
105
105
 
106
106
  ```powershell
107
- npx.cmd --yes @danhachuel/thunderbolt@0.2.24 install
108
- npx.cmd --yes @danhachuel/thunderbolt@0.2.24 doctor
109
- npx.cmd --yes @danhachuel/thunderbolt@0.2.24
107
+ npx.cmd --yes @danhachuel/thunderbolt@0.2.26 install
108
+ npx.cmd --yes @danhachuel/thunderbolt@0.2.26 doctor
109
+ npx.cmd --yes @danhachuel/thunderbolt@0.2.26
110
110
  ```
111
111
 
112
112
  Como alternativa, pode permitir scripts para o seu utilizador:
@@ -189,7 +189,7 @@ O agente musical guarda os ficheiros em `storage/music/`, aceita formatos de áu
189
189
 
190
190
  ## Teste de vozes
191
191
 
192
- A área **Configurações > Teste de vozes** é isolada da pipeline. Permite escolher Edge/Azure Speech ou um provider HTTP configurado, seleccionar voz/ID, alterar a velocidade, sintetizar uma amostra, reproduzi-la e descarregá-la. O áudio é guardado em `storage/voice_previews/` e nunca altera vídeos ou tarefas.
192
+ A área **Configurações > Teste de vozes** é isolada da pipeline. Permite escolher Edge/Azure Speech ou um provider HTTP configurado, seleccionar voz/ID, alterar a velocidade, sintetizar uma amostra, reproduzi-la e descarregá-la. O áudio é guardado em `storage/voice_previews/` e nunca altera vídeos ou tarefas. Se uma instalação antiga não tiver `edge-tts`, execute `npx.cmd --yes @danhachuel/thunderbolt install`; o instalador detecta a dependência em falta sem reinstalar componentes válidos.
193
193
 
194
194
  ## MCP e integrações externas opcionais
195
195
 
package/app/main.py CHANGED
@@ -22,7 +22,7 @@ from hermes_ui.blueprints import create_blueprint_from_link, list_branding_files
22
22
  from hermes_ui.metadata_cleaner import build_description, clean_video_metadata, list_edit_records, metadata_manifest, normalize_tags, save_edit_record, store_external_video
23
23
  from hermes_ui.mcp import detect_local_service, install_skill_locally, load_integrations, read_packaged_skill, update_integration
24
24
  from hermes_ui.music import list_music_files, materialize_suno_audio, request_suno_generation, store_music_file
25
- from hermes_ui.voice_preview import DEFAULT_SAMPLE, synthesize_preview
25
+ from hermes_ui.voice_preview import DEFAULT_SAMPLE, load_preview_file, synthesize_preview
26
26
  from integrations.platforms import IntegrationResult, TikTokAdapter, YouTubeAdapter
27
27
  from integrations.youtube_direct_upload import YouTubeDirectUploader
28
28
  from integrations.local_runtime import MoneyPrinterRuntime
@@ -129,9 +129,18 @@ st.markdown("""
129
129
  .content-value { color:#f4f8fb; font-size:1.8rem; font-weight:700; margin-top:.3rem; }
130
130
  .stage { border-left:3px solid #35a7ff; padding:.65rem .8rem; margin:.4rem 0; background:#101d2a; border-radius:8px; }
131
131
  .small-muted { color:#8ba6bb; font-size:.85rem; }
132
- /* Cores dos chips BaseWeb dentro do mesmo campo, identificadas pelo destino e não por posição. */
132
+ /* Cores dos chips por identidade da plataforma, sem depender da ordem de selecção. */
133
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag] { color:#ffffff !important; border:0 !important; font-weight:700 !important; }
134
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag] span[title],
135
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag] button,
136
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag] svg { color:#ffffff !important; fill:#ffffff !important; }
137
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag][aria-label="YouTube"] { background:#ff0000 !important; }
138
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag][aria-label="TikTok"] { background:#000000 !important; }
139
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag][aria-label="Instagram"] { background:#e1306c !important; }
140
+ [data-testid="stMultiSelectTagsContainer"] span[data-tag][aria-label="Facebook Pages"] { background:#1877f2 !important; }
141
+ /* Compatibilidade com versões BaseWeb que usam data-baseweb=tag. */
133
142
  [data-testid="stMultiSelect"] [data-baseweb="tag"] { color:#ffffff !important; border:0 !important; font-weight:700 !important; }
134
- [data-testid="stMultiSelect"] [data-baseweb="tag"]:has(button[aria-label*="YouTube"]) { background:#ff4b4b !important; }
143
+ [data-testid="stMultiSelect"] [data-baseweb="tag"]:has(button[aria-label*="YouTube"]) { background:#ff0000 !important; }
135
144
  [data-testid="stMultiSelect"] [data-baseweb="tag"]:has(button[aria-label*="TikTok"]) { background:#000000 !important; }
136
145
  [data-testid="stMultiSelect"] [data-baseweb="tag"]:has(button[aria-label*="Instagram"]) { background:#e1306c !important; }
137
146
  [data-testid="stMultiSelect"] [data-baseweb="tag"]:has(button[aria-label*="Facebook Pages"]) { background:#1877f2 !important; }
@@ -1048,16 +1057,22 @@ def render_settings():
1048
1057
  preview_rate = st.selectbox("Velocidade", ["-20%", "-10%", "+0%", "+10%", "+20%"], index=2, key="voice_preview_rate")
1049
1058
  preview_text = st.text_area("Texto de teste", value=DEFAULT_SAMPLE, max_chars=1000, height=110, key="voice_preview_text")
1050
1059
  if st.button("Testar voz", type="primary", key="voice_preview_generate"):
1060
+ st.session_state.pop("voice_preview_path", None)
1051
1061
  try:
1052
1062
  preview_path = synthesize_preview(preview_text, preview_provider, preview_voice, settings, preview_rate)
1053
1063
  st.session_state["voice_preview_path"] = str(preview_path)
1054
1064
  st.success("Amostra de voz gerada. Este ficheiro é apenas um preview.")
1055
1065
  except Exception as exc:
1056
1066
  st.error(f"Não foi possível gerar o preview: {exc}")
1057
- preview_path = Path(st.session_state.get("voice_preview_path", ""))
1058
- if preview_path.exists():
1059
- st.audio(preview_path.read_bytes(), format="audio/mpeg")
1060
- st.download_button("Descarregar amostra", data=preview_path.read_bytes(), file_name=preview_path.name, mime="audio/mpeg", key="voice_preview_download")
1067
+ preview_value = str(st.session_state.get("voice_preview_path", "") or "").strip()
1068
+ loaded_preview = load_preview_file(preview_value)
1069
+ if loaded_preview:
1070
+ preview_path, preview_data = loaded_preview
1071
+ st.audio(preview_data, format="audio/mpeg")
1072
+ st.download_button("Descarregar amostra", data=preview_data, file_name=preview_path.name, mime="audio/mpeg", key="voice_preview_download")
1073
+ elif preview_value:
1074
+ st.session_state.pop("voice_preview_path", None)
1075
+ st.warning("A amostra de voz anterior não é um ficheiro de áudio legível e foi removida do estado local. Teste a voz novamente.")
1061
1076
 
1062
1077
 
1063
1078
  def render_mcp():
@@ -26,7 +26,10 @@ def _edge_voice_name(value: str) -> str:
26
26
 
27
27
 
28
28
  async def _save_edge_async(text: str, voice: str, output: Path, rate: str) -> None:
29
- import edge_tts
29
+ try:
30
+ import edge_tts
31
+ except ModuleNotFoundError as exc:
32
+ raise RuntimeError("A dependência edge-tts não está instalada. Execute `npx.cmd --yes @danhachuel/thunderbolt install` para completar a instalação.") from exc
30
33
 
31
34
  kwargs: dict[str, Any] = {"rate": rate}
32
35
  if "boundary" in inspect.signature(edge_tts.Communicate).parameters:
@@ -97,6 +100,23 @@ def _save_openai_compatible(text: str, voice: str, output: Path, settings: dict[
97
100
  output.write_bytes(response.content)
98
101
 
99
102
 
103
+ def load_preview_file(path_value: str | Path) -> tuple[Path, bytes] | None:
104
+ """Return a readable non-empty preview file, never a directory or empty path."""
105
+ raw_path = str(path_value or "").strip()
106
+ if not raw_path:
107
+ return None
108
+ candidate = Path(raw_path)
109
+ try:
110
+ if not candidate.is_file():
111
+ return None
112
+ data = candidate.read_bytes()
113
+ except (OSError, PermissionError):
114
+ return None
115
+ if not data:
116
+ return None
117
+ return candidate, data
118
+
119
+
100
120
  def synthesize_preview(text: str, provider: str, voice: str, settings: dict[str, Any], rate: str = "+0%") -> Path:
101
121
  text = (text or "").strip()
102
122
  if not text:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.2.24",
3
+ "version": "0.2.26",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "main": "scripts/cli.mjs",
6
6
  "type": "module",