@danhachuel/thunderbolt 0.3.93 → 0.3.94

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 +76 -1
  2. package/package.json +1 -1
package/app/main.py CHANGED
@@ -3576,6 +3576,21 @@ def load_video_tasks_for_catalog() -> list[dict[str, Any]]:
3576
3576
  return [task for task in saved if isinstance(task, dict) and str(task.get("id") or "").strip()]
3577
3577
 
3578
3578
 
3579
+ def _is_music_task(task: dict[str, Any]) -> bool:
3580
+ """Identify music pipeline tasks without conflating them with ordinary video tasks."""
3581
+ return bool(task.get("music_mode")) or str(task.get("style_wide") or task.get("style") or "").strip().casefold() in {"music", "música"}
3582
+
3583
+
3584
+ def load_music_tasks_for_catalog() -> list[dict[str, Any]]:
3585
+ """Return persisted tasks that were explicitly created through a music route."""
3586
+ return [task for task in load_video_tasks_for_catalog() if _is_music_task(task)]
3587
+
3588
+
3589
+ def load_standard_video_tasks_for_catalog() -> list[dict[str, Any]]:
3590
+ """Keep video backlog free of tasks that belong to the dedicated music queue."""
3591
+ return [task for task in load_video_tasks_for_catalog() if not _is_music_task(task)]
3592
+
3593
+
3579
3594
  def _video_task_format(task: dict[str, Any]) -> str:
3580
3595
  value = task.get("format") or task.get("style_wide") or task.get("style") or "wide"
3581
3596
  return str(value).strip() or "wide"
@@ -3622,7 +3637,7 @@ def render_videos():
3622
3637
  st.caption("Acompanhamento dos vídeos criados, estados da pipeline e controlos de execução.")
3623
3638
  st.caption(f"Os vídeos são guardados em `{STORAGE / 'videos'}`.")
3624
3639
  _render_pipeline_progress_panel()
3625
- tasks = load_video_tasks_for_catalog()
3640
+ tasks = load_standard_video_tasks_for_catalog()
3626
3641
  if not tasks:
3627
3642
  st.info("Nenhum vídeo criado.")
3628
3643
  return
@@ -3681,6 +3696,64 @@ def render_videos():
3681
3696
  st.rerun()
3682
3697
 
3683
3698
 
3699
+ def render_music_backlog() -> None:
3700
+ """Render the music-only counterpart of Backlog Videos with the same controls."""
3701
+ st.subheader("Music Backlog")
3702
+ st.caption("Acompanhamento das músicas criadas, estados da pipeline e controlos de execução.")
3703
+ st.caption(f"As músicas são guardadas em `{STORAGE / 'music'}`.")
3704
+ _render_pipeline_progress_panel()
3705
+ tasks = load_music_tasks_for_catalog()
3706
+ if not tasks:
3707
+ st.info("Nenhuma música criada.")
3708
+ return
3709
+ known_states = ["to_do", "doing", "blocked", "done", "failed", "cancelled"]
3710
+ extra_states = sorted({str(task.get("state") or "unknown") for task in tasks if str(task.get("state") or "unknown") not in known_states})
3711
+ state_filter = st.selectbox("Filtrar por estado", ["Todos", *known_states, *extra_states], key="music_backlog_state_filter")
3712
+ for task in tasks:
3713
+ if state_filter != "Todos" and task.get("state") != state_filter:
3714
+ continue
3715
+ with st.container(border=True):
3716
+ cols = st.columns([2.2, 1, 1, 1.2, 1.8])
3717
+ with cols[0]:
3718
+ st.write(f"**{task.get('title') or task.get('topic', 'Sem título')}**")
3719
+ st.caption(f"Música: {task.get('music_source') or 'rota musical'}")
3720
+ st.caption(f"{task.get('channel_name')} · {task.get('id')}")
3721
+ music_path = str(task.get("music_path") or (task.get("artifacts") or {}).get("music") or "").strip()
3722
+ if music_path and Path(music_path).is_file():
3723
+ music_file = Path(music_path)
3724
+ st.success("Música pronta; pode continuar para o destino configurado.")
3725
+ st.download_button(
3726
+ "Descarregar música pronta",
3727
+ data=music_file.read_bytes(),
3728
+ file_name=music_file.name,
3729
+ mime="audio/mpeg",
3730
+ key=f"music_backlog_download_{task['id']}",
3731
+ use_container_width=True,
3732
+ )
3733
+ elif music_path:
3734
+ st.caption(f"Música registada: {music_path}")
3735
+ else:
3736
+ st.caption("A música será disponibilizada quando a etapa de geração terminar.")
3737
+ with cols[1]:
3738
+ st.caption("Formato")
3739
+ st.write(_video_task_format(task))
3740
+ with cols[2]:
3741
+ st.write(_pipeline_stage_label(task))
3742
+ with cols[3]:
3743
+ _render_video_task_state(task)
3744
+ with cols[4]:
3745
+ state = str(task.get("state") or "")
3746
+ start_col, stop_col = st.columns(2)
3747
+ with start_col:
3748
+ if st.button("Start", key=f"music_backlog_start_{task['id']}", use_container_width=True, disabled=state not in {"to_do", "blocked", "failed"}):
3749
+ transition_task(task["id"], "doing")
3750
+ st.rerun()
3751
+ with stop_col:
3752
+ if st.button("Stop", key=f"music_backlog_stop_{task['id']}", use_container_width=True, disabled=state != "doing"):
3753
+ transition_task(task["id"], "blocked")
3754
+ st.rerun()
3755
+
3756
+
3684
3757
  def _thumbnail_editor_context(record: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
3685
3758
  """Resolve the persisted task channel/Blueprint without requiring either one to remain registered."""
3686
3759
  channel_id = str(record.get("channel_id") or "").strip()
@@ -6745,6 +6818,7 @@ def main():
6745
6818
  ]
6746
6819
  music_items = [
6747
6820
  ("Criação de Músicas", ":material/music_note:", "Criação de Músicas"),
6821
+ ("Music Backlog", ":material/queue_music:", "Music Backlog"),
6748
6822
  ("Upload Música", ":material/library_music:", "Upload Música"),
6749
6823
  ]
6750
6824
  models_ai_items = [
@@ -6867,6 +6941,7 @@ def main():
6867
6941
  "Criação de Vídeos": render_new_video,
6868
6942
  "Backlog Vídeos": render_videos,
6869
6943
  "Criação de Músicas": render_music_creation,
6944
+ "Music Backlog": render_music_backlog,
6870
6945
  "Upload Música": render_music_upload,
6871
6946
  "Roteiros": render_scripts,
6872
6947
  "Thumbnails": render_thumbnails,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.3.93",
3
+ "version": "0.3.94",
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",