@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.
- package/MANUAL-INSTALACAO.md +4 -4
- package/README.md +4 -4
- package/app/main.py +22 -7
- package/hermes_ui/voice_preview.py +21 -1
- package/package.json +1 -1
package/MANUAL-INSTALACAO.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
108
|
-
npx.cmd --yes @danhachuel/thunderbolt@0.2.
|
|
109
|
-
npx.cmd --yes @danhachuel/thunderbolt@0.2.
|
|
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
|
|
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:#
|
|
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
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
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
|
-
|
|
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