@danhachuel/thunderbolt 0.7.3 → 0.7.5
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 +22 -18
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -2668,12 +2668,15 @@ def render_channels():
|
|
|
2668
2668
|
|
|
2669
2669
|
with import_tab:
|
|
2670
2670
|
st.caption("A pesquisa pública funciona sem API Key. A Data API é opcional e fica disponível numa opção separada para métricas oficiais.")
|
|
2671
|
-
|
|
2672
|
-
lookup_cols = st.columns(2)
|
|
2671
|
+
lookup_cols = st.columns([2.2, 1.0], gap="medium")
|
|
2673
2672
|
with lookup_cols[0]:
|
|
2674
|
-
|
|
2673
|
+
source = st.text_input("URL, handle ou ID do canal", placeholder="https://youtube.com/@seucanal", key="youtube_channel_source")
|
|
2675
2674
|
with lookup_cols[1]:
|
|
2676
|
-
st.
|
|
2675
|
+
lookup_mode = st.selectbox(
|
|
2676
|
+
"Método de consulta",
|
|
2677
|
+
["Página pública — sem API Key", "YouTube Data API — API Key opcional"],
|
|
2678
|
+
key="youtube_channel_lookup_mode",
|
|
2679
|
+
)
|
|
2677
2680
|
col1, col2 = st.columns([1, 1])
|
|
2678
2681
|
with col1:
|
|
2679
2682
|
if st.button("Buscar no YouTube", type="primary", width="stretch", key="youtube_channel_lookup"):
|
|
@@ -3249,17 +3252,18 @@ def _saved_video_draft_records() -> list[dict[str, Any]]:
|
|
|
3249
3252
|
return records
|
|
3250
3253
|
|
|
3251
3254
|
|
|
3252
|
-
def _seed_resume_video_settings(record: dict[str, Any]) -> None:
|
|
3255
|
+
def _seed_resume_video_settings(record: dict[str, Any], prefix: str = "new_video") -> None:
|
|
3253
3256
|
"""Load one persisted record into the namespaced resume widgets."""
|
|
3254
3257
|
resume_id = str(record.get("resume_id") or "")
|
|
3255
|
-
|
|
3258
|
+
state_prefix = f"{prefix}_resume"
|
|
3259
|
+
if st.session_state.get(f"{state_prefix}_loaded_id") == resume_id:
|
|
3256
3260
|
return
|
|
3257
3261
|
generation_settings = record.get("generation_settings") if isinstance(record.get("generation_settings"), dict) else {}
|
|
3258
|
-
st.session_state["
|
|
3262
|
+
st.session_state[f"{state_prefix}_sections"] = missing_setting_sections(generation_settings)
|
|
3259
3263
|
for suffix in setting_widget_suffixes():
|
|
3260
3264
|
if suffix in generation_settings:
|
|
3261
|
-
st.session_state[f"
|
|
3262
|
-
st.session_state["
|
|
3265
|
+
st.session_state[f"{state_prefix}_{suffix}"] = generation_settings[suffix]
|
|
3266
|
+
st.session_state[f"{state_prefix}_loaded_id"] = resume_id
|
|
3263
3267
|
|
|
3264
3268
|
|
|
3265
3269
|
def _create_video_task_from_saved_script(record: dict[str, Any], channel: dict[str, Any], settings: dict[str, Any]) -> list[dict[str, Any]]:
|
|
@@ -3310,7 +3314,7 @@ def _create_video_task_from_saved_script(record: dict[str, Any], channel: dict[s
|
|
|
3310
3314
|
return create_tasks_for_batch(batch)
|
|
3311
3315
|
|
|
3312
3316
|
|
|
3313
|
-
def render_video_from_draft() -> None:
|
|
3317
|
+
def render_video_from_draft(prefix: str = "new_video") -> None:
|
|
3314
3318
|
"""Render the continuation flow for saved scripts and local pipeline drafts."""
|
|
3315
3319
|
st.subheader("Roteiros guardados")
|
|
3316
3320
|
st.caption("Seleccione um roteiro guardado para continuar a criação do vídeo sem perder o conteúdo já preparado.")
|
|
@@ -3324,15 +3328,15 @@ def render_video_from_draft() -> None:
|
|
|
3324
3328
|
"Seleccione um roteiro",
|
|
3325
3329
|
list(record_by_id),
|
|
3326
3330
|
format_func=lambda identifier: f"{record_by_id[identifier].get('title') or 'Roteiro sem título'} · {record_by_id[identifier].get('source_label') or 'Rascunho'}",
|
|
3327
|
-
key="
|
|
3331
|
+
key=f"{prefix}_resume_selected",
|
|
3328
3332
|
)
|
|
3329
3333
|
record = record_by_id[selected_id]
|
|
3330
|
-
_seed_resume_video_settings(record)
|
|
3334
|
+
_seed_resume_video_settings(record, prefix)
|
|
3331
3335
|
with st.container(border=True):
|
|
3332
3336
|
st.markdown(f"**Video Subject:** {record.get('video_subject') or '—'}")
|
|
3333
3337
|
st.caption(f"{record.get('source_label') or 'Rascunho'} · {record.get('channel_name') or 'Documento independente'} · Blueprint: {record.get('blueprint_name') or '—'}")
|
|
3334
3338
|
if record.get("video_script"):
|
|
3335
|
-
st.text_area("Video Script (Optional)", value=str(record["video_script"]), height=150, disabled=True, key=f"
|
|
3339
|
+
st.text_area("Video Script (Optional)", value=str(record["video_script"]), height=150, disabled=True, key=f"{prefix}_resume_preview_script_{selected_id}")
|
|
3336
3340
|
if record.get("video_keywords"):
|
|
3337
3341
|
st.caption(f"**Video Keywords:** {record['video_keywords']}")
|
|
3338
3342
|
|
|
@@ -3349,7 +3353,7 @@ def render_video_from_draft() -> None:
|
|
|
3349
3353
|
"Configurações a completar",
|
|
3350
3354
|
list(DRAFT_SETTING_SECTIONS),
|
|
3351
3355
|
default=missing_sections,
|
|
3352
|
-
key="
|
|
3356
|
+
key=f"{prefix}_resume_sections",
|
|
3353
3357
|
help="Seleccione uma ou mais áreas para completar antes de criar a tarefa.",
|
|
3354
3358
|
)
|
|
3355
3359
|
)
|
|
@@ -3374,11 +3378,11 @@ def render_video_from_draft() -> None:
|
|
|
3374
3378
|
selectable_channels,
|
|
3375
3379
|
index=channel_index,
|
|
3376
3380
|
format_func=lambda channel: str(channel.get("name") or "Canal sem nome"),
|
|
3377
|
-
key="
|
|
3381
|
+
key=f"{prefix}_resume_channel",
|
|
3378
3382
|
)
|
|
3379
3383
|
|
|
3380
3384
|
settings_from_form = render_video_generation_settings(
|
|
3381
|
-
"
|
|
3385
|
+
f"{prefix}_resume",
|
|
3382
3386
|
current_language=str(record.get("language") or "pt"),
|
|
3383
3387
|
channel=selected_channel,
|
|
3384
3388
|
sections=selected_sections,
|
|
@@ -3391,7 +3395,7 @@ def render_video_from_draft() -> None:
|
|
|
3391
3395
|
st.caption(f"Faltam: {', '.join(still_missing)}")
|
|
3392
3396
|
elif not missing_sections:
|
|
3393
3397
|
st.caption("Este roteiro será usado directamente, sem regenerar o conteúdo editorial guardado.")
|
|
3394
|
-
if st.button(action_label, type="primary", width="stretch", key="
|
|
3398
|
+
if st.button(action_label, type="primary", width="stretch", key=f"{prefix}_resume_submit", icon=":material/movie:"):
|
|
3395
3399
|
if still_missing:
|
|
3396
3400
|
st.error(f"Complete as configurações seleccionadas: {', '.join(still_missing)}.")
|
|
3397
3401
|
elif not selected_channel.get("id"):
|
|
@@ -3818,7 +3822,7 @@ def render_new_video(page_title: str = "Criação de Vídeos", prefix: str = "ne
|
|
|
3818
3822
|
_render_pipeline_progress_panel()
|
|
3819
3823
|
if draft_tab is not None:
|
|
3820
3824
|
with draft_tab:
|
|
3821
|
-
render_video_from_draft()
|
|
3825
|
+
render_video_from_draft(prefix)
|
|
3822
3826
|
|
|
3823
3827
|
|
|
3824
3828
|
def render_music_creation():
|
package/package.json
CHANGED