@danhachuel/thunderbolt 0.5.100 → 0.6.1

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
@@ -1311,6 +1311,45 @@ def render_channel_videos(channel: dict) -> None:
1311
1311
  render_channel_video_editor(video, channel_id)
1312
1312
 
1313
1313
 
1314
+ def render_channel_audio_subtitle_defaults(channel: dict) -> None:
1315
+ """Render and persist the audio/subtitle defaults used by channel automations."""
1316
+ channel_id = str(channel.get("id") or "")
1317
+ subtitle_font = str(channel.get("default_subtitle_font") or SUBTITLE_FONT_OPTIONS[0])
1318
+ subtitle_position = str(channel.get("default_subtitle_position") or SUBTITLE_POSITION_OPTIONS[0])
1319
+ music_source = str(channel.get("default_background_music_source") or "Sem música")
1320
+ music_volume = str(channel.get("default_background_music_volume") or "20%")
1321
+ with st.expander("Configurações de legendas", expanded=False):
1322
+ subtitle_cols = st.columns(2)
1323
+ with subtitle_cols[0]:
1324
+ enable_subtitles = st.checkbox("Enable Subtitles", value=bool(channel.get("default_enable_subtitles", True)), key=f"channel_default_subtitles_{channel_id}")
1325
+ selected_font = st.selectbox("Font", SUBTITLE_FONT_OPTIONS, index=SUBTITLE_FONT_OPTIONS.index(subtitle_font) if subtitle_font in SUBTITLE_FONT_OPTIONS else 0, key=f"channel_default_subtitle_font_{channel_id}")
1326
+ selected_position = st.selectbox("Position", SUBTITLE_POSITION_OPTIONS, index=SUBTITLE_POSITION_OPTIONS.index(subtitle_position) if subtitle_position in SUBTITLE_POSITION_OPTIONS else 0, key=f"channel_default_subtitle_position_{channel_id}")
1327
+ subtitle_color = st.color_picker("Color", str(channel.get("default_subtitle_color") or "#FFFFFF"), key=f"channel_default_subtitle_color_{channel_id}")
1328
+ subtitle_background = st.checkbox("Background", value=bool(channel.get("default_subtitle_background", True)), key=f"channel_default_subtitle_background_{channel_id}")
1329
+ subtitle_background_color = st.color_picker("Background Color", str(channel.get("default_subtitle_background_color") or "#000000"), key=f"channel_default_subtitle_background_color_{channel_id}")
1330
+ subtitle_rounded_background = st.checkbox("Rounded Background", value=bool(channel.get("default_subtitle_rounded_background", False)), key=f"channel_default_subtitle_rounded_background_{channel_id}")
1331
+ with subtitle_cols[1]:
1332
+ subtitle_font_size = st.slider("Font Size", min_value=12, max_value=96, value=int(channel.get("default_subtitle_font_size") or 60), key=f"channel_default_subtitle_font_size_{channel_id}")
1333
+ subtitle_outline = st.color_picker("Outline", str(channel.get("default_subtitle_outline") or "#000000"), key=f"channel_default_subtitle_outline_{channel_id}")
1334
+ subtitle_outline_width = st.slider("Outline Width", min_value=0.0, max_value=5.0, value=float(channel.get("default_subtitle_outline_width") or 1.5), step=0.25, key=f"channel_default_subtitle_outline_width_{channel_id}")
1335
+ if st.button("Guardar configurações de legendas", type="primary", key=f"save_channel_subtitle_defaults_{channel_id}", use_container_width=True):
1336
+ update_channel(channel_id, {
1337
+ "default_enable_subtitles": bool(enable_subtitles), "default_subtitle_font": selected_font, "default_subtitle_position": selected_position,
1338
+ "default_subtitle_color": subtitle_color, "default_subtitle_background": bool(subtitle_background), "default_subtitle_background_color": subtitle_background_color,
1339
+ "default_subtitle_rounded_background": bool(subtitle_rounded_background), "default_subtitle_font_size": int(subtitle_font_size),
1340
+ "default_subtitle_outline": subtitle_outline, "default_subtitle_outline_width": float(subtitle_outline_width),
1341
+ })
1342
+ st.success("Default de legendas guardado para este canal.")
1343
+ st.rerun()
1344
+ with st.expander("Configurações de Audio", expanded=False):
1345
+ selected_music_source = st.selectbox("Fonte da música de fundo", BACKGROUND_MUSIC_SOURCE_OPTIONS, index=BACKGROUND_MUSIC_SOURCE_OPTIONS.index(music_source) if music_source in BACKGROUND_MUSIC_SOURCE_OPTIONS else BACKGROUND_MUSIC_SOURCE_OPTIONS.index("Sem música"), key=f"channel_default_music_source_{channel_id}")
1346
+ selected_music_volume = st.selectbox("Volume da música de fundo", BACKGROUND_MUSIC_VOLUME_OPTIONS, index=BACKGROUND_MUSIC_VOLUME_OPTIONS.index(music_volume) if music_volume in BACKGROUND_MUSIC_VOLUME_OPTIONS else 2, key=f"channel_default_music_volume_{channel_id}")
1347
+ if st.button("Guardar configurações de Audio", type="primary", key=f"save_channel_audio_defaults_{channel_id}", use_container_width=True):
1348
+ update_channel(channel_id, {"default_background_music_source": selected_music_source, "default_background_music_volume": selected_music_volume})
1349
+ st.success("Default de Audio guardado para este canal.")
1350
+ st.rerun()
1351
+
1352
+
1314
1353
  def render_channel_edit_form(channel: dict, youtube_account_ids: list[str], youtube_account_labels: dict[str, str], youtube_accounts_by_id: dict[str, dict[str, Any]]) -> None:
1315
1354
  channel_id = str(channel["id"])
1316
1355
  blueprint_ids, blueprint_labels, current_blueprint, voice_options, current_voice = channel_default_options(channel)
@@ -2341,6 +2380,7 @@ def render_tiktok_channels():
2341
2380
  update_channel(channel_id, {"name": edit_name.strip(), "url": edit_url.strip(), "handle": edit_handle.strip(), "language": edit_language, "niche": edit_niche.strip(), "reference_channels": [edit_niche.strip()] if edit_niche.strip() else [], "default_voice": edit_voice, "voice": edit_voice, "style_wide": channel_video_source_storage(edit_source), "video_aspect_ratio": edit_aspect, "automation_on": edit_automation, "automation_time": edit_time.strip(), "description": edit_description.strip(), "platform": "tiktok"})
2342
2381
  st.session_state.pop(f"tiktok_edit_{channel_id}", None)
2343
2382
  st.rerun()
2383
+ render_channel_audio_subtitle_defaults(channel)
2344
2384
  with st.expander("Últimos 10 vídeos publicados", expanded=False):
2345
2385
  recent_videos = channel_videos_for(channel, limit=10)
2346
2386
  if not recent_videos:
@@ -3032,6 +3072,7 @@ def render_channels():
3032
3072
  st.success("Conta Google e DELEGATED_SESSION_ID individual do canal guardados.")
3033
3073
  st.rerun()
3034
3074
 
3075
+ render_channel_audio_subtitle_defaults(channel)
3035
3076
  render_channel_videos(channel)
3036
3077
 
3037
3078
 
@@ -53,6 +53,18 @@ def create_channel(name: str, url: str = "", metadata: dict[str, Any] | None = N
53
53
  "style_wide": "pexels",
54
54
  "voice": "",
55
55
  "default_voice": "",
56
+ "default_enable_subtitles": True,
57
+ "default_subtitle_font": "MicrosoftYaHeiBold.ttc",
58
+ "default_subtitle_position": "Bottom (Recommended)",
59
+ "default_subtitle_color": "#FFFFFF",
60
+ "default_subtitle_background": True,
61
+ "default_subtitle_background_color": "#000000",
62
+ "default_subtitle_rounded_background": False,
63
+ "default_subtitle_font_size": 60,
64
+ "default_subtitle_outline": "#000000",
65
+ "default_subtitle_outline_width": 1.5,
66
+ "default_background_music_source": "Sem música",
67
+ "default_background_music_volume": "20%",
56
68
  "delegated_session_id": "",
57
69
  "automation_on": False,
58
70
  "automation_time": "00:00",
@@ -164,6 +176,19 @@ def create_tasks_for_batch(batch: dict[str, Any]) -> list[dict[str, Any]]:
164
176
  if thumbnail_path:
165
177
  artifacts.setdefault("thumbnail", thumbnail_path)
166
178
  initial_stage = "topic" if pending_creative else "script"
179
+ channel_generation_defaults = {
180
+ key.removeprefix("default_"): channel[key]
181
+ for key in (
182
+ "default_enable_subtitles", "default_subtitle_font", "default_subtitle_position", "default_subtitle_color",
183
+ "default_subtitle_background", "default_subtitle_background_color", "default_subtitle_rounded_background",
184
+ "default_subtitle_font_size", "default_subtitle_outline", "default_subtitle_outline_width",
185
+ "default_background_music_source", "default_background_music_volume",
186
+ )
187
+ if key in channel
188
+ }
189
+ configured_generation_settings = options.get("generation_settings") if isinstance(options.get("generation_settings"), dict) else {}
190
+ payload_generation_settings = payload.get("generation_settings") if isinstance(payload.get("generation_settings"), dict) else {}
191
+ generation_settings = {**channel_generation_defaults, **configured_generation_settings, **payload_generation_settings}
167
192
  task = {
168
193
  "id": make_id("video"),
169
194
  "batch_id": batch["id"],
@@ -183,7 +208,7 @@ def create_tasks_for_batch(batch: dict[str, Any]) -> list[dict[str, Any]]:
183
208
  "music_path": payload.get("music_path", options.get("music_path", "")),
184
209
  "music_source": payload.get("music_source", options.get("music_source", "")),
185
210
  "background_mode": payload.get("background_mode", options.get("background_mode", "stock")),
186
- "generation_settings": payload.get("generation_settings", options.get("generation_settings", {})),
211
+ "generation_settings": generation_settings,
187
212
  "blueprint_id": payload.get("blueprint_id") or channel.get("default_blueprint_id") or channel.get("blueprint_id", ""),
188
213
  "blueprint_name": payload.get("blueprint_name", ""),
189
214
  "thumbnail_blueprint_id": payload.get("thumbnail_blueprint_id") or channel.get("default_thumbnail_blueprint_id") or channel.get("thumbnail_blueprint_id") or GENERIC_THUMBNAIL_BLUEPRINT_ID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.100",
3
+ "version": "0.6.01",
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",