@danhachuel/thunderbolt 0.6.83 → 0.6.85
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/influencers_ui.py +38 -9
- package/package.json +1 -1
package/app/influencers_ui.py
CHANGED
|
@@ -115,6 +115,19 @@ def _provider_label(card: Mapping[str, Any]) -> str:
|
|
|
115
115
|
return f"{definition.label} · {model}"
|
|
116
116
|
|
|
117
117
|
|
|
118
|
+
def _ugc_duration_seconds(value: str) -> int:
|
|
119
|
+
"""Parse the UGC duration field as minutes:seconds with a safe default."""
|
|
120
|
+
try:
|
|
121
|
+
minutes_text, seconds_text = str(value or "1:20").strip().split(":", 1)
|
|
122
|
+
minutes = max(0, int(minutes_text))
|
|
123
|
+
seconds = int(seconds_text)
|
|
124
|
+
if 0 <= seconds < 60:
|
|
125
|
+
return max(1, min(3600, minutes * 60 + seconds))
|
|
126
|
+
except (TypeError, ValueError):
|
|
127
|
+
pass
|
|
128
|
+
return 80
|
|
129
|
+
|
|
130
|
+
|
|
118
131
|
def _content_metadata(item: Mapping[str, Any]) -> dict[str, Any]:
|
|
119
132
|
raw = item.get("metadata") if isinstance(item.get("metadata"), Mapping) else item.get("metadata_json")
|
|
120
133
|
if isinstance(raw, Mapping):
|
|
@@ -445,7 +458,7 @@ def render_motion_control(settings: dict[str, Any]) -> None:
|
|
|
445
458
|
def render_ugc_products(settings: dict[str, Any]) -> None:
|
|
446
459
|
"""Create portrait UGC photos or videos from a product and a saved character."""
|
|
447
460
|
st.title("UGC Products")
|
|
448
|
-
st.caption("Crie fotos ou vídeos UGC em formato vertical 9:16 para TikTok e Instagram usando um produto e um personagem
|
|
461
|
+
st.caption("Crie fotos ou vídeos UGC em formato vertical 9:16 para TikTok e Instagram usando um produto e um personagem cadastrado.")
|
|
449
462
|
repository = _repository(settings)
|
|
450
463
|
if repository is None:
|
|
451
464
|
return
|
|
@@ -457,15 +470,21 @@ def render_ugc_products(settings: dict[str, Any]) -> None:
|
|
|
457
470
|
|
|
458
471
|
with media_tab:
|
|
459
472
|
if not influencers:
|
|
460
|
-
influencers = [{"id": "", "name": "
|
|
473
|
+
influencers = [{"id": "", "name": "", "bio": ""}]
|
|
461
474
|
influencer_options = _influencer_options(influencers)
|
|
475
|
+
if not influencer_options:
|
|
476
|
+
influencer_options = [""]
|
|
462
477
|
selected_influencer = st.selectbox(
|
|
463
|
-
"Personagem
|
|
478
|
+
"Personagem",
|
|
464
479
|
influencer_options,
|
|
465
480
|
format_func=lambda value: _influencer_label(influencers, value),
|
|
466
481
|
key="ugc_products_character",
|
|
467
482
|
)
|
|
468
|
-
selected_character = next(
|
|
483
|
+
selected_character = next(
|
|
484
|
+
(item for item in influencers if str(item.get("id") or "") == str(selected_influencer or "")),
|
|
485
|
+
influencers[0],
|
|
486
|
+
)
|
|
487
|
+
selected_influencer = str(selected_character.get("id") or "")
|
|
469
488
|
try:
|
|
470
489
|
character_assets = repository.list_assets(selected_influencer)
|
|
471
490
|
except Exception:
|
|
@@ -480,16 +499,26 @@ def render_ugc_products(settings: dict[str, Any]) -> None:
|
|
|
480
499
|
key="ugc_products_inputs",
|
|
481
500
|
help="Anexe uma ou mais referências do produto. Para geração, a primeira imagem será usada como referência visual principal.",
|
|
482
501
|
)
|
|
483
|
-
|
|
484
|
-
|
|
502
|
+
generation_col, duration_col = st.columns([2, 1])
|
|
503
|
+
with generation_col:
|
|
504
|
+
generation_mode = st.selectbox("Tipo de geração", ["Gerar Foto", "Gerar Video"], key="ugc_products_generation_mode")
|
|
505
|
+
duration_text = "1:20"
|
|
506
|
+
if generation_mode == "Gerar Video":
|
|
507
|
+
with duration_col:
|
|
508
|
+
duration_text = st.text_input("Duração de cada clip (MM:SS)", value="1:20", max_chars=5, key="ugc_products_duration_text", width=120)
|
|
509
|
+
duration = _ugc_duration_seconds(duration_text)
|
|
485
510
|
image_cards, image_options = _provider_options(settings, "image")
|
|
486
511
|
video_cards = _workflow_provider_cards(settings, provider=None)
|
|
487
512
|
video_options = [str(card.get("id") or "") for card in video_cards]
|
|
488
|
-
image_provider_id =
|
|
489
|
-
video_provider_id =
|
|
513
|
+
image_provider_id = ""
|
|
514
|
+
video_provider_id = ""
|
|
515
|
+
if generation_mode == "Gerar Foto":
|
|
516
|
+
image_provider_id = st.selectbox("Provider / modelo de imagem", image_options, format_func=lambda value: _provider_label(next(card for card in image_cards if str(card.get("id")) == value)), key="ugc_products_image_provider") if image_cards else ""
|
|
517
|
+
else:
|
|
518
|
+
video_provider_id = st.selectbox("Provider / modelo de vídeo", video_options, format_func=lambda value: _provider_label(next(card for card in video_cards if str(card.get("id")) == value)), key="ugc_products_video_provider") if video_cards else ""
|
|
490
519
|
script = st.text_area("Roteiro de vídeo (UGC)", height=150, key="ugc_products_script", placeholder="O roteiro gerado pela IA aparecerá aqui; também pode editá-lo antes de gerar.")
|
|
491
520
|
generate_script = st.button("Gerar roteiro com IA", use_container_width=True, key="ugc_products_generate_script")
|
|
492
|
-
generate = st.button("Gerar
|
|
521
|
+
generate = st.button("Gerar Mídia (Foto/Vídeo)", type="primary", use_container_width=True, key="ugc_products_generate")
|
|
493
522
|
if generate_script:
|
|
494
523
|
context = "\n".join(
|
|
495
524
|
item for item in [
|
package/package.json
CHANGED