@danhachuel/thunderbolt 0.3.52 → 0.3.54

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
@@ -31,6 +31,7 @@ from app.modules.niche_finder.apify import ApifyError, DEFAULT_ACTOR_ID, abort_a
31
31
  from app.modules.niche_finder.core import NicheAnalysisError, run_niche_analysis
32
32
  from app.modules.niche_finder.data_loader import DatasetError, download_kaggle_dataset
33
33
  from app.modules.niche_finder.summarizer import summarize_items
34
+ from app.influencers_ui import render_ai_influencer_characters, render_ai_influencer_content, render_ai_influencer_motion_control, render_ai_influencers_api_status
34
35
  from hermes_ui.blueprints import create_blueprint_from_link, list_branding_files, save_generated_blueprint
35
36
  from hermes_ui.metadata_cleaner import build_description, clean_video_metadata, list_edit_records, metadata_manifest, normalize_tags, save_edit_record, store_external_video
36
37
  from hermes_ui.python_editor import AUDIO_EXTENSIONS, VIDEO_EXTENSIONS, PythonEditorError, change_speed, editor_manifest, extract_audio, list_edit_records as list_python_editor_records, list_generated_videos, list_scripts, list_video_files, read_script, remove_audio, replace_audio, resize_video, save_edit_record as save_python_editor_record, save_script, store_uploaded_asset, trim_video
@@ -39,13 +40,14 @@ from hermes_ui.mcp import detect_local_service, install_skill_locally, load_inte
39
40
  from hermes_ui.mcp_server import server_status, start_server, stop_server
40
41
  from hermes_ui.material_sources import apply_material_source_cards_to_settings, ensure_material_source_cards, material_source_catalog, material_source_definition, new_material_card, normalize_material_card, selected_material_source
41
42
  from hermes_ui.llm_providers import LLM_CARDS_KEY, LLM_PROVIDER_CATALOG, apply_llm_cards_to_settings, ensure_llm_provider_cards, new_llm_card, normalize_llm_card, provider_definition, test_llm_provider_card, stamp_test_result
42
- from hermes_ui.media_providers import MEDIA_CARDS_KEY, MEDIA_IMAGE_ACTIVE_CARD_KEY, MEDIA_VIDEO_ACTIVE_CARD_KEY, apply_media_provider_cards_to_settings, ensure_media_provider_cards, media_provider_catalog, media_provider_definition, new_media_card, normalize_media_card
43
+ from hermes_ui.media_providers import MEDIA_CARDS_KEY, MEDIA_IMAGE_ACTIVE_CARD_KEY, MEDIA_VIDEO_ACTIVE_CARD_KEY, apply_media_provider_cards_to_settings, ensure_media_provider_cards, media_cards_for_pool, media_provider_catalog, media_provider_definition, new_media_card, normalize_media_card
43
44
  from hermes_ui.music import list_music_files, materialize_suno_audio, request_suno_generation, store_music_file, store_voiceover_file
44
45
  from hermes_ui.media_downloader import AUDIO_FORMATS, VIDEO_CONTAINERS, VIDEO_QUALITY_OPTIONS, MediaDownloadError, build_download_options, clear_media_download_history, dependency_status, download_media, list_media_downloads, media_download_file
45
46
  from hermes_ui.notifications import clear_notifications, list_notifications, mark_all_notifications_read, mark_notification_read, notification_event_catalog, notification_preferences, record_notification, reconcile_persisted_notifications, save_notification_preferences, unread_notification_count
47
+ from hermes_ui.influencers import BACKEND_OPTIONS, DOCUMENT_EXTENSIONS, IMAGE_EXTENSIONS, backend_name, backend_status, get_repository, test_backend
46
48
  from hermes_ui.logs import list_logs, logs_to_rows
47
49
  from hermes_ui.languages import LANGUAGE_CODES, VIDEO_LANGUAGE_CODES, LANGUAGE_FLAG_DATA_URIS, language_code, language_label, ui_language_menu_label, ui_text, video_language_label, video_language_options
48
- from hermes_ui.api_key_tests import test_apify_credentials, test_kaggle_credentials, test_material_source_credentials, test_media_provider_card, test_nano_banana_credentials, test_postiz_credentials, test_telegram_credentials, test_tiktok_credentials, test_upload_post_credentials, test_voice_provider
50
+ from hermes_ui.api_key_tests import test_apify_credentials, test_influencer_database, test_kaggle_credentials, test_material_source_credentials, test_media_provider_card, test_nano_banana_credentials, test_postiz_credentials, test_telegram_credentials, test_tiktok_credentials, test_upload_post_credentials, test_voice_provider
49
51
  from hermes_ui.tutorials import tutorial_body, tutorial_caption, tutorial_title
50
52
 
51
53
  from hermes_ui.script_documents import list_script_documents, read_script_document, save_script_document, script_storage_path
@@ -63,6 +65,7 @@ from hermes_ui.thumbnails import (
63
65
  )
64
66
  from hermes_ui.draft_video import DRAFT_SETTING_SECTIONS, missing_content_fields, missing_setting_sections, normalise_saved_script, setting_widget_suffixes
65
67
  from hermes_ui.creative_generation import CreativeGenerationError, generate_creative_package, generate_thumbnail_prompt, generate_title_and_keywords, generate_topic_for_channel, generate_video_keywords
68
+ from hermes_ui.media_generation import MediaGenerationError, generate_image_for_card, generate_video_for_card
66
69
  from integrations.platforms import IntegrationResult, TikTokAdapter, YouTubeAdapter, fetch_channel_videos_public
67
70
  from integrations.tiktok_public import fetch_public_tiktok_profile, normalize_tiktok_reference
68
71
  from integrations.postiz import PostizAdapter
@@ -5383,7 +5386,7 @@ def render_settings():
5383
5386
  key=f"settings_{key}",
5384
5387
  )
5385
5388
 
5386
- api_keys_tab, google_accounts_tab, material_sources_tab, voice_test_tab = render_localized_tabs(["API Keys", "Contas Google", "Fontes de Materiais", "Teste de Voz"])
5389
+ api_keys_tab, google_accounts_tab, material_sources_tab, ai_influencers_tab, voice_test_tab = render_localized_tabs(["API Keys", "Contas Google", "Fontes de Materiais", "AI Influencers", "Teste de Voz"])
5387
5390
 
5388
5391
  with api_keys_tab:
5389
5392
  with st.container(border=True):
@@ -5434,6 +5437,21 @@ def render_settings():
5434
5437
 
5435
5438
  render_media_provider_cards(settings, embedded=True)
5436
5439
 
5440
+ with st.expander("Banco de Dados Influencers", expanded=False):
5441
+ st.caption("Escolha um backend para personagens e conteúdos. SQLite local funciona sem credenciais externas; Supabase é opcional para quem quiser usar uma base remota. O teste é read-only e não cria nem elimina registos.")
5442
+ configured_backend = backend_name(settings)
5443
+ influencer_db_backend = st.selectbox("Backend da base de dados de AI Influencers", list(BACKEND_OPTIONS), index=list(BACKEND_OPTIONS).index(configured_backend), key="settings_influencer_db_backend")
5444
+ db_cols = st.columns(2)
5445
+ with db_cols[0]:
5446
+ influencer_supabase_url = text_setting("Supabase Project URL", "influencer_supabase_url", help_text="URL do projecto, por exemplo https://project-id.supabase.co")
5447
+ influencer_supabase_key = text_setting("Supabase API key", "influencer_supabase_key", secret=True, help_text="Use uma chave com as permissões RLS adequadas. Nunca é colocada no GitHub ou nos logs.")
5448
+ influencer_supabase_bucket = text_setting("Supabase Storage bucket", "influencer_supabase_bucket", help_text="Bucket usado para imagens e documentos, por defeito ai-influencers.")
5449
+ with db_cols[1]:
5450
+ influencer_sqlite_path = text_setting("SQLite ficheiro local", "influencer_sqlite_path", help_text="Caminho absoluto ou relativo ao projecto. Por defeito: storage/state/ai_influencers.db")
5451
+ st.caption("SQLite usa a biblioteca standard do Python e cria o schema automaticamente ao testar ou usar o backend.")
5452
+ db_test_clicked = st.form_submit_button("Testar ligação do backend", use_container_width=True, key="api_test_influencer_database")
5453
+ st.caption("Para Supabase, aplique primeiro seed/references/ai_influencers_schema.sql no SQL Editor e configure o bucket/políticas RLS.")
5454
+
5437
5455
  with st.expander("Voz, TTS e música — Azure Speech, restantes serviços e Suno", expanded=False):
5438
5456
  st.caption("Cada serviço está separado no seu próprio cartão. Os botões de teste ficam dentro do cartão correspondente e fazem apenas diagnóstico, sem gerar áudio ou música.")
5439
5457
 
@@ -5581,6 +5599,15 @@ def render_settings():
5581
5599
  widget_key="api_test_postiz",
5582
5600
  )
5583
5601
 
5602
+ if db_test_clicked:
5603
+ test_settings = dict(settings)
5604
+ test_settings.update({"influencer_db_backend": influencer_db_backend, "influencer_supabase_url": influencer_supabase_url, "influencer_supabase_key": influencer_supabase_key, "influencer_supabase_bucket": influencer_supabase_bucket, "influencer_sqlite_path": influencer_sqlite_path})
5605
+ db_result = test_backend(test_settings)
5606
+ if db_result.get("ok"):
5607
+ st.success(db_result.get("message") or "Backend disponível.")
5608
+ else:
5609
+ st.error(db_result.get("message") or "O backend não está disponível.")
5610
+
5584
5611
  save_all_settings = st.form_submit_button("Guardar configurações do Thunderbolt", type="primary")
5585
5612
  if save_all_settings:
5586
5613
  settings.update({
@@ -5588,6 +5615,7 @@ def render_settings():
5588
5615
  "kaggle_username": kaggle_username.strip(), "kaggle_api_key": kaggle_api_key.strip(), "kaggle_kernel_slug": kaggle_kernel_slug.strip() or "thunderbolt-niche-finder",
5589
5616
  "apify_api_token": apify_api_token.strip(), "apify_actor_id": apify_actor_id.strip() or DEFAULT_ACTOR_ID, "apify_poll_interval_seconds": int(apify_poll_interval), "apify_run_timeout_seconds": int(apify_run_timeout),
5590
5617
  "llm_rpm_limit_enabled": bool(llm_rpm_limit_enabled), "llm_rpm_limit": int(llm_rpm_limit), "llm_rpm_window_seconds": int(llm_rpm_window_seconds),
5618
+ "influencer_db_backend": influencer_db_backend, "influencer_supabase_url": influencer_supabase_url.strip(), "influencer_supabase_key": influencer_supabase_key.strip(), "influencer_supabase_bucket": influencer_supabase_bucket.strip() or "ai-influencers", "influencer_sqlite_path": influencer_sqlite_path.strip() or "storage/state/ai_influencers.db",
5591
5619
  "azure_speech_key": azure_speech_key, "azure_speech_region": azure_speech_region,
5592
5620
  "siliconflow_tts_api_key": siliconflow_tts_api_key, "minimax_tts_api_key": minimax_tts_api_key,
5593
5621
  "minimax_tts_base_url": minimax_tts_base_url, "minimax_tts_model_id": minimax_tts_model_id, "minimax_tts_voice_id": minimax_tts_voice_id,
@@ -5617,6 +5645,9 @@ def render_settings():
5617
5645
  with material_sources_tab:
5618
5646
  render_material_source_api_keys(settings)
5619
5647
 
5648
+ with ai_influencers_tab:
5649
+ render_ai_influencers_api_status(settings)
5650
+
5620
5651
  with voice_test_tab:
5621
5652
  st.subheader("Teste de Voz")
5622
5653
  st.caption("Este painel é exclusivamente um preview. O áudio gerado não altera vídeos, tarefas, Blueprints ou a configuração da pipeline.")
@@ -6340,9 +6371,9 @@ def main():
6340
6371
  "Editor Python": render_python_editor,
6341
6372
  "Download Mídia": render_media_download,
6342
6373
  "AI Influencers": lambda: render_edit_placeholder("AI Influencers", "Seleccione uma das abas AI Influencers no menu expansível."),
6343
- "Personagens": lambda: render_edit_placeholder("Personagens", "Área reservada para a futura funcionalidade de personagens."),
6344
- "Geração de Conteúdo IA": lambda: render_edit_placeholder("Geração de Conteúdo IA", ""),
6345
- "Motion Control": lambda: render_edit_placeholder("Motion Control", ""),
6374
+ "Personagens": lambda: render_ai_influencer_characters(read_json("settings.json", {})),
6375
+ "Geração de Conteúdo IA": lambda: render_ai_influencer_content(read_json("settings.json", {})),
6376
+ "Motion Control": render_ai_influencer_motion_control,
6346
6377
  "UGC Products": lambda: render_edit_placeholder("UGC Products", ""),
6347
6378
  "Redes Sociais": lambda: render_edit_placeholder("Redes Sociais", "Área reservada para a futura funcionalidade de redes sociais."),
6348
6379
  "Analista Growth Youtube": lambda: render_edit_placeholder("Analista Growth Youtube", ""),
@@ -272,6 +272,15 @@ def test_material_source_credentials(provider: str, api_key: str) -> dict[str, A
272
272
  return _unsupported("Este provider de materiais não expõe um endpoint de diagnóstico seguro no cartão actual.")
273
273
 
274
274
 
275
+ def test_influencer_database(settings: Mapping[str, Any]) -> dict[str, Any]:
276
+ """Verify the configured AI Influencers backend without generating or deleting data."""
277
+ try:
278
+ from hermes_ui.influencers import test_backend
279
+ return test_backend(settings)
280
+ except Exception:
281
+ return _result("error", "Não foi possível verificar o backend AI Influencers.")
282
+
283
+
275
284
  def test_voice_provider(provider: str, settings: dict[str, Any]) -> dict[str, Any]:
276
285
  """Dispatch a non-generating check for one provider in the voice/music group."""
277
286
  provider = str(provider or "").strip().lower()
@@ -297,6 +306,7 @@ __all__ = [
297
306
  "test_azure_speech_credentials",
298
307
  "test_elevenlabs_credentials",
299
308
  "test_kaggle_credentials",
309
+ "test_influencer_database",
300
310
  "test_material_source_credentials",
301
311
  "test_minimax_credentials",
302
312
  "test_nano_banana_credentials",