@danhachuel/thunderbolt 0.5.30 → 0.5.33

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/app/main.py CHANGED
@@ -656,6 +656,19 @@ def render_channel_thumbnail_blueprint_panel(channel: dict, *, compact: bool = F
656
656
  st.info(f"**Thumbnail Blueprint utilizado pelo canal:** {name} · apenas leitura")
657
657
 
658
658
 
659
+ def render_tiktok_prompt_master_panel(channel: dict, *, compact: bool = False) -> None:
660
+ """Mostrar a configuração editorial TikTok sem exigir um Blueprint YouTube."""
661
+ prompt_id = str(channel.get("default_prompt_master") or channel.get("prompt_master") or "").strip()
662
+ _prompt_ids, prompt_labels = _tiktok_prompt_options()
663
+ prompt_name = prompt_labels.get(prompt_id, prompt_id)
664
+ if not prompt_id:
665
+ st.warning("**SEM PROMPT MASTER CONFIGURADO** · configure um Prompt Master padrão na aba Canais Tiktok.")
666
+ elif compact:
667
+ st.caption(f"**Prompt Master TikTok:** {prompt_name}")
668
+ else:
669
+ st.info(f"**Prompt Master utilizado pelo canal TikTok:** {prompt_name} · `{prompt_id}`")
670
+
671
+
659
672
  def creative_payload_from_result(channel: dict, topic: str, creative: dict, topic_source: str = "manual") -> dict[str, Any]:
660
673
  variant = creative.get("thumbnail_variant") or {}
661
674
  return {
@@ -3039,9 +3052,12 @@ def render_new_video(page_title: str = "Criação de Vídeos", prefix: str = "ne
3039
3052
  else:
3040
3053
  selected_one = st.selectbox("Canal", active_channels, format_func=lambda c: c["name"], key=f"{prefix}_channel")
3041
3054
  selected = [selected_one["id"]]
3042
- # Intentionally sits between Canal and the generation settings, as requested.
3043
- render_channel_blueprint_panel(selected_one)
3044
- render_channel_thumbnail_blueprint_panel(selected_one)
3055
+ # YouTube uses Blueprints; TikTok Shorts use the channel Prompt Master.
3056
+ if channel_platform == "tiktok":
3057
+ render_tiktok_prompt_master_panel(selected_one)
3058
+ else:
3059
+ render_channel_blueprint_panel(selected_one)
3060
+ render_channel_thumbnail_blueprint_panel(selected_one)
3045
3061
  generation_settings = render_video_generation_settings(
3046
3062
  prefix,
3047
3063
  current_language=str(st.session_state.get("video_language") or ""),
@@ -7089,6 +7105,26 @@ def _fetch_media_models(card: dict[str, Any]) -> list[str]:
7089
7105
  provider = str(card.get("provider") or "").strip().lower()
7090
7106
  if provider == "kie_ai":
7091
7107
  return list(KIE_MEDIA_MODEL_CATALOG)
7108
+ if provider == "fal_ai":
7109
+ api_key = str(card.get("api_key") or "").strip()
7110
+ if not api_key:
7111
+ raise ValueError("Introduza a API key FAL AI antes de consultar os modelos.")
7112
+ response = requests.get(
7113
+ "https://api.fal.ai/v1/models",
7114
+ headers={"Accept": "application/json", "Authorization": f"Key {api_key}"},
7115
+ timeout=12,
7116
+ )
7117
+ response.raise_for_status()
7118
+ payload = response.json()
7119
+ entries = payload.get("models") if isinstance(payload, dict) else []
7120
+ models = {
7121
+ str(item.get("endpoint_id") or "").strip()
7122
+ for item in entries
7123
+ if isinstance(item, dict) and str(item.get("endpoint_id") or "").strip()
7124
+ }
7125
+ if not models:
7126
+ raise ValueError("A FAL AI não devolveu modelos disponíveis.")
7127
+ return sorted(models, key=str.casefold)
7092
7128
  if provider == "openrouter":
7093
7129
  base_url = str(card.get("base_url") or "https://openrouter.ai/api/v1").rstrip("/")
7094
7130
  headers = {"Accept": "application/json", "Authorization": f"Bearer {str(card.get('api_key') or '').strip()}"}
@@ -177,6 +177,8 @@ def test_media_provider_card(card: Mapping[str, Any]) -> dict[str, Any]:
177
177
  return _get(f"{base_url}/v3/users/me", headers={"X-Api-Key": api_key})
178
178
  if provider == "kie_ai":
179
179
  return _get(f"{base_url}/chat/credit", headers={"Authorization": f"Bearer {api_key}"})
180
+ if provider == "fal_ai":
181
+ return _get("https://api.fal.ai/v1/models", headers={"Authorization": f"Key {api_key}"})
180
182
  if not model and provider not in {"inferenceport", "cloudflare_workers_ai", "heygen"}:
181
183
  return _result("missing", "Complete o modelo antes de testar.")
182
184
  if provider == "openrouter":
@@ -194,8 +196,6 @@ def test_media_provider_card(card: Mapping[str, Any]) -> dict[str, Any]:
194
196
  return _get(endpoint, headers=headers, params={"search": model or "stable-diffusion"})
195
197
  if api_style in {"openai_compatible", "huggingface", "agnes", "kie"} or provider in {"pollinations", "huggingface", "agnes", "inferenceport"}:
196
198
  endpoint = _models_endpoint(base_url)
197
- elif provider == "fal_ai":
198
- endpoint = f"{base_url}/models" if base_url.endswith("/v1") else base_url
199
199
  else:
200
200
  endpoint = base_url
201
201
  return _get(endpoint, headers=headers)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.30",
3
+ "version": "0.5.33",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",