@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.
Files changed (2) hide show
  1. package/app/main.py +22 -18
  2. 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
- source = st.text_input("URL, handle ou ID do canal", placeholder="https://youtube.com/@seucanal", key="youtube_channel_source")
2672
- lookup_cols = st.columns(2)
2671
+ lookup_cols = st.columns([2.2, 1.0], gap="medium")
2673
2672
  with lookup_cols[0]:
2674
- lookup_mode = st.radio("Método de consulta", ["Página pública — sem API Key", "YouTube Data API — API Key opcional"], horizontal=True, key="youtube_channel_lookup_mode")
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.caption("Os canais cadastrados são apresentados apenas em lista.")
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
- if st.session_state.get("new_video_resume_loaded_id") == resume_id:
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["new_video_resume_sections"] = missing_setting_sections(generation_settings)
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"new_video_resume_{suffix}"] = generation_settings[suffix]
3262
- st.session_state["new_video_resume_loaded_id"] = resume_id
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="new_video_resume_selected",
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"resume_preview_script_{selected_id}")
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="new_video_resume_sections",
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="new_video_resume_channel",
3381
+ key=f"{prefix}_resume_channel",
3378
3382
  )
3379
3383
 
3380
3384
  settings_from_form = render_video_generation_settings(
3381
- "new_video_resume",
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="new_video_resume_submit", icon=":material/movie:"):
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.7.03",
3
+ "version": "0.7.05",
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",