@danhachuel/thunderbolt 0.3.45 → 0.3.46
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
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.3.
|
|
5
|
+
> **Versão deste manual:** 0.3.46
|
|
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 --prefer-online @danhachuel/thunderbolt@0.3.
|
|
103
|
+
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.46 install
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
Linux/macOS:
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
|
-
npx --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
109
|
+
npx --yes --prefer-online @danhachuel/thunderbolt@0.3.46 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.
|
|
@@ -114,7 +114,7 @@ A instalação normal é **segura para actualizações**: preserva `storage`, Bl
|
|
|
114
114
|
Se quiser apagar absolutamente tudo de forma intencional, use o comando destrutivo separado:
|
|
115
115
|
|
|
116
116
|
```powershell
|
|
117
|
-
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
117
|
+
npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.46 install --purge-data
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
O parâmetro `--purge-data` apaga Blueprints, Brandings, configurações, storage e artefactos locais. Não o use numa actualização normal.
|
|
@@ -363,11 +363,11 @@ Em **Configuração API > API Keys**, o expander **LLM — providers e modelos**
|
|
|
363
363
|
|
|
364
364
|
O bloco **Limite LLM NVIDIA NIM** permite ligar **Activar limitador NVIDIA NIM — 40 RPM**. O valor padrão é 40 pedidos numa janela de 60 segundos. O contador é persistido localmente por cartão/chave e aplica-se à criação manual, aos workers e às automações. A opção vem desligada para não alterar instalações existentes; ligue-a quando o endpoint activo for NVIDIA NIM e a sua conta tiver esse limite.
|
|
365
365
|
|
|
366
|
-
O expander **Imagem e Video** substitui o antigo bloco isolado da Nano Banana. Os cartões disponíveis são **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Em cada cartão, configure a API key/token, o modelo, a Base URL, o estado **Provider activo**, a participação no **Pool Imagem**, a participação no **Pool Vídeo** e a prioridade. O botão **Testar Chamada API** faz apenas uma verificação read-only; não inicia geração de imagem ou vídeo.
|
|
366
|
+
O expander **Imagem e Video** substitui o antigo bloco isolado da Nano Banana. Os cartões disponíveis são **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Em cada cartão, configure a API key/token, o modelo, a Base URL, o estado **Provider activo**, a participação no **Pool Imagem**, a participação no **Pool Vídeo** e a prioridade. **Image Size** e **Aspect Ratio** não aparecem nos cartões e não são editáveis: são defaults internos aplicados automaticamente no prompt da geração. O botão **Testar Chamada API** faz apenas uma verificação read-only; não inicia geração de imagem ou vídeo.
|
|
367
367
|
|
|
368
368
|
| Provider | Configuração mínima | Pool suportado |
|
|
369
369
|
|---|---|---|
|
|
370
|
-
| Nano Banana | API key
|
|
370
|
+
| Nano Banana | API key e modelo Gemini; composição aplicada internamente | Imagem |
|
|
371
371
|
| Pollinations.ai | API key/token, modelo e Base URL | Imagem e vídeo |
|
|
372
372
|
| Agnes AI | API key, modelo e Base URL | Imagem e vídeo |
|
|
373
373
|
| Hugging Face Inference API | Token, modelo e `https://router.huggingface.co/v1` | Imagem |
|
|
@@ -522,7 +522,7 @@ Ao abrir a página, o Thunderbolt não prepara dados públicos, não descarrega
|
|
|
522
522
|
|
|
523
523
|
Os parâmetros da UI são número de clusters entre 2 e 10, suporte mínimo entre 0,01 e 0,50, país, engagement, intervalo de datas e tags, todos dentro da área principal da aba. O núcleo normaliza os dados, calcula engagement, aplica filtros, faz transformação logarítmica e standardização, executa K-Means e calcula itemsets/regras com FP-Growth. Não são apresentados resultados até ao primeiro clique em **Analisar Nichos**; o mesmo botão aplica alterações posteriores aos filtros. Os resultados são DataFrames de clusters, itemsets frequentes, regras de associação e dados analisados; o gráfico de dispersão é criado nativamente com Plotly.
|
|
524
524
|
|
|
525
|
-
As dependências adicionais — `scikit-learn`, `mlxtend`, `plotly`, `seaborn`, `matplotlib` e `kagglehub` — são instaladas pelo procedimento normal de `npx`. Em instalações existentes, execute novamente `npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.
|
|
525
|
+
As dependências adicionais — `scikit-learn`, `mlxtend`, `plotly`, `seaborn`, `matplotlib` e `kagglehub` — são instaladas pelo procedimento normal de `npx`. Em instalações existentes, execute novamente `npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.46 install`; o instalador detecta e reutiliza o que já estiver válido.
|
|
526
526
|
|
|
527
527
|
### Niche Finder Apify
|
|
528
528
|
|
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ No topo da área principal da aplicação existe o menu nativo de idioma no padr
|
|
|
69
69
|
|
|
70
70
|
A UI suporta os temas **Dark** e **Light** através do menu nativo de três pontos do Streamlit, no local original do toolbar. Não existe um selector Theme adicional dentro da página. A configuração distribuída em `.streamlit/config.toml` disponibiliza as variantes nomeadas **Dark** e **Light**, e o menu nativo continua responsável por alternar entre os modos, seguindo o padrão do [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo). O CSS próprio do Thunderbolt usa cores semânticas, `currentColor` e `color-mix` para acompanhar o tema activo, sem alterar a posição nem a funcionalidade do toolbar, do botão Deploy e do menu principal.
|
|
71
71
|
|
|
72
|
-
## Navegação da UI 0.3.
|
|
72
|
+
## Navegação da UI 0.3.46
|
|
73
73
|
|
|
74
74
|
A barra lateral mantém os níveis principais, nesta ordem: **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. **Pipeline Vídeos** é expansível e contém **Criação de Vídeos**, **Backlog Vídeos**, **Roteiros**, **Thumbnails** e **Upload**. **Pipeline Música** é expansível e contém **Criação de Músicas** e **Upload Música**. **Automação** também é expansível e contém **Automação Youtube**. **Edição** é expansível e contém **Limpador de Metadados**, **Cortes**, **Editor Python** e **Download Mídia**, nessa ordem. **AI Influencers** é expansível e contém **Personagens**, **Redes Sociais**, **Tutorial Meta** e **Tutorial Supabase**, nessa ordem. **Niche Finder** é expansível e contém **Niche Finder Kaggle** e **Niche Finder Apify**. **Configurações** é expansível e contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Notificações**, **Logs** e **Configuração API**, nessa ordem. O Início reúne o dashboard e as filas do Pipeline, sem botões de acções rápidas.
|
|
75
75
|
|
|
@@ -85,7 +85,7 @@ Em **Configuração API > API Keys**, a subaba **Fontes de materiais** segue o p
|
|
|
85
85
|
|
|
86
86
|
A subaba **Serviços e modelos** mantém as credenciais de providers e serviços que não são fontes de materiais. A chave `gemini_image_api_key` da Nano Banana continua separada de `gemini_api_key` do LLM textual. `INNERTUBE_API_KEY` permanece exclusivamente em **Configurações > Contas Google**, no bloco de configuração global fora dos cartões, e não é duplicada em API Keys.
|
|
87
87
|
|
|
88
|
-
O expander **Imagem e Video** reúne cartões independentes para **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Cada cartão permite configurar API key/token quando necessário, modelo, Base URL, activar o provider, escolher participação no **Pool Imagem** e/ou **Pool Vídeo**, definir prioridade e executar **Testar Chamada API**. O InferencePort usa por defeito `http://localhost:8080/v1` e não exige chave; o Cloudflare requer Account ID; os restantes providers devem ser preenchidos de acordo com a conta e endpoint disponibilizados pelo serviço.
|
|
88
|
+
O expander **Imagem e Video** reúne cartões independentes para **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Cada cartão permite configurar API key/token quando necessário, modelo, Base URL, activar o provider, escolher participação no **Pool Imagem** e/ou **Pool Vídeo**, definir prioridade e executar **Testar Chamada API**. Os parâmetros técnicos de composição, como **Image Size** e **Aspect Ratio**, não são apresentados nem editáveis nos cartões: ficam como defaults internos e são acrescentados automaticamente ao prompt da geração de imagem e/ou vídeo. O InferencePort usa por defeito `http://localhost:8080/v1` e não exige chave; o Cloudflare requer Account ID; os restantes providers devem ser preenchidos de acordo com a conta e endpoint disponibilizados pelo serviço.
|
|
89
89
|
|
|
90
90
|
O router mantém três pools independentes: **LLM textual**, **imagem** e **vídeo**. O failover só passa ao cartão seguinte em quota, timeout, erro de transporte ou HTTP transitório; erros de credencial, endpoint/modelo inexistente ou payload inválido ficam registados e não são mascarados por um provider diferente. O pool de vídeo externo é opcional: quando desligado, o worker continua a usar o motor local MoneyPrinterTurbo; quando ligado, usa apenas os cartões marcados para Pool Vídeo.
|
|
91
91
|
|
|
@@ -16,7 +16,14 @@ from urllib.parse import urljoin
|
|
|
16
16
|
|
|
17
17
|
import requests
|
|
18
18
|
|
|
19
|
-
from .media_providers import
|
|
19
|
+
from .media_providers import (
|
|
20
|
+
INTERNAL_IMAGE_ASPECT_RATIO,
|
|
21
|
+
INTERNAL_IMAGE_SIZE,
|
|
22
|
+
INTERNAL_VIDEO_ASPECT_RATIO,
|
|
23
|
+
INTERNAL_VIDEO_SIZE,
|
|
24
|
+
media_cards_for_pool,
|
|
25
|
+
media_provider_definition,
|
|
26
|
+
)
|
|
20
27
|
from .provider_routing import (
|
|
21
28
|
POOL_IMAGE,
|
|
22
29
|
POOL_VIDEO,
|
|
@@ -52,6 +59,28 @@ def _headers(card: Mapping[str, Any], *, fal: bool = False) -> dict[str, str]:
|
|
|
52
59
|
return headers
|
|
53
60
|
|
|
54
61
|
|
|
62
|
+
def _append_generation_constraints(
|
|
63
|
+
prompt: str,
|
|
64
|
+
*,
|
|
65
|
+
kind: str,
|
|
66
|
+
aspect_ratio: str = "",
|
|
67
|
+
size: str = "",
|
|
68
|
+
) -> str:
|
|
69
|
+
"""Append controlled rendering constraints without exposing editable UI fields."""
|
|
70
|
+
clean = str(prompt or "").strip()
|
|
71
|
+
if kind == "image":
|
|
72
|
+
ratio = str(aspect_ratio or INTERNAL_IMAGE_ASPECT_RATIO).strip()
|
|
73
|
+
resolution = str(size or INTERNAL_IMAGE_SIZE).strip()
|
|
74
|
+
constraints = f"Target image composition: {ratio} aspect ratio, {resolution} resolution."
|
|
75
|
+
else:
|
|
76
|
+
ratio = str(aspect_ratio or INTERNAL_VIDEO_ASPECT_RATIO).strip()
|
|
77
|
+
resolution = str(size or INTERNAL_VIDEO_SIZE).strip()
|
|
78
|
+
constraints = f"Target video composition: {ratio} aspect ratio, {resolution} resolution."
|
|
79
|
+
if not clean:
|
|
80
|
+
return constraints
|
|
81
|
+
return f"{clean}\n\n{constraints}"
|
|
82
|
+
|
|
83
|
+
|
|
55
84
|
def _decode_data(value: Any) -> bytes | None:
|
|
56
85
|
if not isinstance(value, str) or not value.strip():
|
|
57
86
|
return None
|
|
@@ -142,14 +171,20 @@ def _image_request(card: dict[str, Any], prompt: str) -> Any:
|
|
|
142
171
|
provider = str(card.get("provider") or "").strip().lower()
|
|
143
172
|
style = str(card.get("api_style") or media_provider_definition(provider).api_style)
|
|
144
173
|
endpoint = _image_endpoint(card)
|
|
174
|
+
constrained_prompt = _append_generation_constraints(
|
|
175
|
+
prompt,
|
|
176
|
+
kind="image",
|
|
177
|
+
aspect_ratio=str(card.get("aspect_ratio") or ""),
|
|
178
|
+
size=str(card.get("image_size") or ""),
|
|
179
|
+
)
|
|
145
180
|
if style == "cloudflare":
|
|
146
|
-
return requests.post(endpoint, headers=_headers(card), json={"prompt":
|
|
181
|
+
return requests.post(endpoint, headers=_headers(card), json={"prompt": constrained_prompt}, timeout=180)
|
|
147
182
|
if style == "fal_queue":
|
|
148
|
-
return requests.post(endpoint, headers=_headers(card, fal=True), json={"prompt":
|
|
183
|
+
return requests.post(endpoint, headers=_headers(card, fal=True), json={"prompt": constrained_prompt, "num_images": 1}, timeout=180)
|
|
149
184
|
if style == "dashscope":
|
|
150
|
-
body = {"model": _model(card), "input": {"prompt":
|
|
185
|
+
body = {"model": _model(card), "input": {"prompt": constrained_prompt}, "parameters": {"n": 1}}
|
|
151
186
|
return requests.post(endpoint, headers=_headers(card), json=body, timeout=180)
|
|
152
|
-
body = {"model": _model(card), "prompt":
|
|
187
|
+
body = {"model": _model(card), "prompt": constrained_prompt, "n": 1, "response_format": "b64_json"}
|
|
153
188
|
return requests.post(endpoint, headers=_headers(card), json=body, timeout=180)
|
|
154
189
|
|
|
155
190
|
|
|
@@ -171,10 +206,17 @@ def generate_image_for_card(
|
|
|
171
206
|
merged = dict(settings)
|
|
172
207
|
merged["gemini_image_api_key"] = _api_key(card)
|
|
173
208
|
merged["gemini_image_model"] = _model(card) or merged.get("gemini_image_model") or "gemini-3.1-flash-image"
|
|
209
|
+
merged["gemini_image_aspect_ratio"] = str(card.get("aspect_ratio") or INTERNAL_IMAGE_ASPECT_RATIO)
|
|
210
|
+
merged["gemini_image_size"] = str(card.get("image_size") or INTERNAL_IMAGE_SIZE)
|
|
174
211
|
try:
|
|
175
212
|
return generate_thumbnail_image(
|
|
176
213
|
merged,
|
|
177
|
-
|
|
214
|
+
_append_generation_constraints(
|
|
215
|
+
prompt,
|
|
216
|
+
kind="image",
|
|
217
|
+
aspect_ratio=str(card.get("aspect_ratio") or ""),
|
|
218
|
+
size=str(card.get("image_size") or ""),
|
|
219
|
+
),
|
|
178
220
|
topic=topic,
|
|
179
221
|
variant_index=variant_index,
|
|
180
222
|
lettering_text=lettering_text,
|
|
@@ -260,7 +302,15 @@ def _video_endpoint(card: Mapping[str, Any]) -> str:
|
|
|
260
302
|
def _video_request(card: dict[str, Any], prompt: str, image_url: str = "") -> Any:
|
|
261
303
|
style = str(card.get("api_style") or media_provider_definition(card.get("provider")).api_style)
|
|
262
304
|
endpoint = _video_endpoint(card)
|
|
263
|
-
body: dict[str, Any] = {
|
|
305
|
+
body: dict[str, Any] = {
|
|
306
|
+
"model": _model(card),
|
|
307
|
+
"prompt": _append_generation_constraints(
|
|
308
|
+
prompt,
|
|
309
|
+
kind="video",
|
|
310
|
+
aspect_ratio=str(card.get("aspect_ratio") or ""),
|
|
311
|
+
size=str(card.get("video_size") or ""),
|
|
312
|
+
),
|
|
313
|
+
}
|
|
264
314
|
if image_url:
|
|
265
315
|
body["image_url"] = image_url
|
|
266
316
|
if style == "fal_queue":
|
|
@@ -12,6 +12,12 @@ MEDIA_VIDEO_ACTIVE_CARD_KEY = "media_video_active_card_id"
|
|
|
12
12
|
MEDIA_IMAGE_ACTIVE_PROVIDER_KEY = "media_image_provider"
|
|
13
13
|
MEDIA_VIDEO_ACTIVE_PROVIDER_KEY = "media_video_provider"
|
|
14
14
|
|
|
15
|
+
# Mantidos internamente para preservar compatibilidade e controlar o prompt dos providers.
|
|
16
|
+
INTERNAL_IMAGE_ASPECT_RATIO = "16:9"
|
|
17
|
+
INTERNAL_IMAGE_SIZE = "1K"
|
|
18
|
+
INTERNAL_VIDEO_ASPECT_RATIO = "16:9"
|
|
19
|
+
INTERNAL_VIDEO_SIZE = "1080p"
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
@dataclass(frozen=True, slots=True)
|
|
17
23
|
class MediaProviderDefinition:
|
|
@@ -35,7 +41,6 @@ MEDIA_PROVIDER_CATALOG: tuple[MediaProviderDefinition, ...] = (
|
|
|
35
41
|
default_base_url="https://generativelanguage.googleapis.com/v1beta",
|
|
36
42
|
supports_image=True,
|
|
37
43
|
api_style="gemini_interactions",
|
|
38
|
-
extra_fields=("aspect_ratio", "image_size"),
|
|
39
44
|
description="Geração de imagem nativa Gemini para thumbnails e artes.",
|
|
40
45
|
),
|
|
41
46
|
MediaProviderDefinition(
|
|
@@ -170,6 +175,9 @@ def normalize_media_card(card: Any, index: int = 0) -> dict[str, Any]:
|
|
|
170
175
|
}
|
|
171
176
|
for field in definition.extra_fields:
|
|
172
177
|
result[field] = str(source.get(field) or "").strip()
|
|
178
|
+
if provider == "nano_banana":
|
|
179
|
+
result["aspect_ratio"] = str(source.get("aspect_ratio") or INTERNAL_IMAGE_ASPECT_RATIO).strip()
|
|
180
|
+
result["image_size"] = str(source.get("image_size") or INTERNAL_IMAGE_SIZE).strip()
|
|
173
181
|
test_result = source.get("test_result")
|
|
174
182
|
if isinstance(test_result, Mapping) and str(test_result.get("status") or "") in {"success", "error"}:
|
|
175
183
|
result["test_result"] = {
|
|
@@ -215,8 +223,8 @@ def ensure_media_provider_cards(settings: Mapping[str, Any]) -> tuple[dict[str,
|
|
|
215
223
|
"api_key": legacy_key,
|
|
216
224
|
"model": legacy_model,
|
|
217
225
|
"base_url": "https://generativelanguage.googleapis.com/v1beta",
|
|
218
|
-
"aspect_ratio": str(result.get("gemini_image_aspect_ratio") or
|
|
219
|
-
"image_size": str(result.get("gemini_image_size") or
|
|
226
|
+
"aspect_ratio": str(result.get("gemini_image_aspect_ratio") or INTERNAL_IMAGE_ASPECT_RATIO),
|
|
227
|
+
"image_size": str(result.get("gemini_image_size") or INTERNAL_IMAGE_SIZE),
|
|
220
228
|
"enabled": True,
|
|
221
229
|
},
|
|
222
230
|
0,
|
|
@@ -267,8 +275,8 @@ def apply_media_provider_cards_to_settings(settings: Mapping[str, Any], cards: l
|
|
|
267
275
|
if nano_card:
|
|
268
276
|
result["gemini_image_api_key"] = str(nano_card.get("api_key") or "")
|
|
269
277
|
result["gemini_image_model"] = str(nano_card.get("model") or "gemini-3.1-flash-image")
|
|
270
|
-
result["gemini_image_aspect_ratio"] = str(nano_card.get("aspect_ratio") or result.get("gemini_image_aspect_ratio") or
|
|
271
|
-
result["gemini_image_size"] = str(nano_card.get("image_size") or result.get("gemini_image_size") or
|
|
278
|
+
result["gemini_image_aspect_ratio"] = str(nano_card.get("aspect_ratio") or result.get("gemini_image_aspect_ratio") or INTERNAL_IMAGE_ASPECT_RATIO)
|
|
279
|
+
result["gemini_image_size"] = str(nano_card.get("image_size") or result.get("gemini_image_size") or INTERNAL_IMAGE_SIZE)
|
|
272
280
|
return result
|
|
273
281
|
|
|
274
282
|
|
|
@@ -17,7 +17,7 @@ from hermes_ui.script_documents import save_script_document
|
|
|
17
17
|
from hermes_ui.script_generation import generate_script_document
|
|
18
18
|
from hermes_ui.storage import STORAGE, ensure_storage, read_json, write_json
|
|
19
19
|
from hermes_ui.llm_providers import active_llm_card, provider_definition
|
|
20
|
-
from hermes_ui.media_generation import MediaGenerationError, generate_image_from_pool, generate_video_from_pool
|
|
20
|
+
from hermes_ui.media_generation import MediaGenerationError, _append_generation_constraints, generate_image_from_pool, generate_video_from_pool
|
|
21
21
|
from hermes_ui.media_providers import media_cards_for_pool
|
|
22
22
|
from hermes_ui.thumbnail_generation import ThumbnailGenerationError, generate_thumbnail_image
|
|
23
23
|
|
|
@@ -554,7 +554,10 @@ def _run_task(task: dict[str, Any]) -> dict[str, Any]:
|
|
|
554
554
|
video_cards = media_cards_for_pool(settings, "video")
|
|
555
555
|
if bool(settings.get("media_video_pool_enabled")) and video_cards:
|
|
556
556
|
try:
|
|
557
|
-
video_prompt =
|
|
557
|
+
video_prompt = _append_generation_constraints(
|
|
558
|
+
f"Título: {title}\n\nRoteiro:\n{str(script.get('content') or '')[:12000]}",
|
|
559
|
+
kind="video",
|
|
560
|
+
)
|
|
558
561
|
video_path = generate_video_from_pool(settings, video_prompt)
|
|
559
562
|
except MediaGenerationError as exc:
|
|
560
563
|
raise PipelineError(f"Pool de vídeo externo: {exc}") from exc
|
package/package.json
CHANGED