@danhachuel/thunderbolt 0.7.67 → 0.7.68

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 +45 -11
  2. package/package.json +1 -1
package/app/main.py CHANGED
@@ -44,6 +44,20 @@ if str(ROOT) not in sys.path:
44
44
  sys.path.insert(0, str(ROOT))
45
45
 
46
46
 
47
+ @st.cache_data(show_spinner=False, max_entries=256)
48
+ def _cached_file_bytes(path_string: str, modified_ns: int, size: int) -> bytes:
49
+ """Cache card/download payloads while invalidating automatically after file changes."""
50
+ del modified_ns, size
51
+ return Path(path_string).read_bytes()
52
+
53
+
54
+ def _file_bytes(path: Path | None) -> bytes:
55
+ if not path or not path.is_file():
56
+ return b""
57
+ stat = path.stat()
58
+ return _cached_file_bytes(str(path.resolve()), int(stat.st_mtime_ns), int(stat.st_size))
59
+
60
+
47
61
  def _load_local_env() -> None:
48
62
  for env_path in (ROOT / ".env", Path.cwd() / ".env"):
49
63
  try:
@@ -5957,7 +5971,7 @@ def render_thumbnails():
5957
5971
  # deprecation warning can be emitted after the bootstrap
5958
5972
  # stream has been closed, which masks the real page with
5959
5973
  # ``ValueError: I/O operation on closed file``.
5960
- image_bytes = image_path.read_bytes()
5974
+ image_bytes = _file_bytes(image_path)
5961
5975
  st.image(image_bytes)
5962
5976
  st.download_button(
5963
5977
  "Descarregar thumbnail",
@@ -6163,7 +6177,7 @@ def _render_tiktok_automation_cards():
6163
6177
  thumbnail_prompt = str(task.get("thumbnail_prompt") or "").strip()
6164
6178
  with task_cols[0]:
6165
6179
  if thumbnail_path:
6166
- st.image(str(thumbnail_path), width=180, caption="Thumbnail")
6180
+ st.image(_file_bytes(thumbnail_path), width=180, caption="Thumbnail")
6167
6181
  else:
6168
6182
  st.caption("Thumbnail ainda não pronta")
6169
6183
  st.write(f"**{task.get('title') or task.get('topic') or 'Vídeo TikTok'}**")
@@ -6181,7 +6195,7 @@ def _render_tiktok_automation_cards():
6181
6195
  with thumbnail_download_col:
6182
6196
  st.download_button(
6183
6197
  "Baixar Thumbnail 9:16",
6184
- data=thumbnail_path.read_bytes() if thumbnail_path else b"",
6198
+ data=_file_bytes(thumbnail_path),
6185
6199
  file_name=_automation_download_name("Thumbnail9:16", task, thumbnail_path, ".png"),
6186
6200
  mime="image/png",
6187
6201
  key=f"tiktok_automation_download_thumbnail_{task_id}",
@@ -6220,7 +6234,7 @@ def _render_tiktok_automation_cards():
6220
6234
  with script_download_col:
6221
6235
  st.download_button(
6222
6236
  "Baixar Roteiro",
6223
- data=script_path.read_bytes() if script_path else b"",
6237
+ data=_file_bytes(script_path),
6224
6238
  file_name=_automation_download_name("Script", task, script_path, ".md"),
6225
6239
  mime="text/markdown",
6226
6240
  key=f"tiktok_automation_download_script_{task_id}",
@@ -6230,7 +6244,7 @@ def _render_tiktok_automation_cards():
6230
6244
  with video_download_col:
6231
6245
  st.download_button(
6232
6246
  "Baixar Vídeo9:16",
6233
- data=video_path.read_bytes() if video_path else b"",
6247
+ data=_file_bytes(video_path),
6234
6248
  file_name=_automation_download_name("Vídeo9:16", task, video_path, ".mp4"),
6235
6249
  mime="video/mp4",
6236
6250
  key=f"tiktok_automation_download_video_{task_id}",
@@ -6345,7 +6359,27 @@ def _render_youtube_automation_cards():
6345
6359
  tasks = load_automation_tasks_for_platform("youtube")
6346
6360
  if not tasks:
6347
6361
  st.info("Ainda não existem vídeos cadastrados.")
6348
- for task in tasks:
6362
+ page_size = 12
6363
+ page_count = max(1, (len(tasks) + page_size - 1) // page_size)
6364
+ current_page = st.session_state.get("youtube_automation_page", 1)
6365
+ try:
6366
+ current_page = min(max(int(current_page), 1), page_count)
6367
+ except (TypeError, ValueError):
6368
+ current_page = 1
6369
+ if page_count > 1:
6370
+ current_page = st.selectbox(
6371
+ "Página de vídeos cadastrados",
6372
+ options=list(range(1, page_count + 1)),
6373
+ index=current_page - 1,
6374
+ format_func=lambda value: f"Página {value} de {page_count}",
6375
+ key="youtube_automation_page",
6376
+ )
6377
+ start = (current_page - 1) * page_size
6378
+ visible_tasks = tasks[start : start + page_size]
6379
+ st.caption(f"A mostrar {start + 1}–{min(start + page_size, len(tasks))} de {len(tasks)} vídeos.")
6380
+ else:
6381
+ visible_tasks = tasks
6382
+ for task in visible_tasks:
6349
6383
  with st.container(border=True):
6350
6384
  task_cols = st.columns([2.25, 1.55, 1.05, 2.15], gap="small")
6351
6385
  script_path = _task_artifact_path(task, "script")
@@ -6355,7 +6389,7 @@ def _render_youtube_automation_cards():
6355
6389
  thumbnail_prompt = str(task.get("thumbnail_prompt") or "").strip()
6356
6390
  with task_cols[0]:
6357
6391
  if thumbnail_path:
6358
- st.image(str(thumbnail_path), width=180, caption="Thumbnail")
6392
+ st.image(_file_bytes(thumbnail_path), width=180, caption="Thumbnail")
6359
6393
  else:
6360
6394
  st.caption("Thumbnail ainda não pronta")
6361
6395
  st.write(f"**{task.get('topic', 'Sem tópico')}**")
@@ -6364,7 +6398,7 @@ def _render_youtube_automation_cards():
6364
6398
  with thumbnail_download_col:
6365
6399
  st.download_button(
6366
6400
  "Baixar Thumbnail",
6367
- data=thumbnail_path.read_bytes() if thumbnail_path else b"",
6401
+ data=_file_bytes(thumbnail_path),
6368
6402
  file_name=_automation_download_name("Thumbnail", task, thumbnail_path, ".png"),
6369
6403
  mime="image/png",
6370
6404
  key=f"automation_download_thumbnail_{task['id']}",
@@ -6402,7 +6436,7 @@ def _render_youtube_automation_cards():
6402
6436
  with script_download_col:
6403
6437
  st.download_button(
6404
6438
  "Baixar Roteiro",
6405
- data=script_path.read_bytes() if script_path else b"",
6439
+ data=_file_bytes(script_path),
6406
6440
  file_name=_automation_download_name("Script", task, script_path, ".md"),
6407
6441
  mime="text/markdown",
6408
6442
  key=f"automation_download_script_{task['id']}",
@@ -6412,7 +6446,7 @@ def _render_youtube_automation_cards():
6412
6446
  with video_download_col:
6413
6447
  st.download_button(
6414
6448
  "Baixar Vídeo",
6415
- data=video_path.read_bytes() if video_path else b"",
6449
+ data=_file_bytes(video_path),
6416
6450
  file_name=_automation_download_name("Vídeo", task, video_path, ".mp4"),
6417
6451
  mime="video/mp4",
6418
6452
  key=f"automation_download_video_{task['id']}",
@@ -6482,7 +6516,7 @@ def _render_facebook_automation_cards() -> None:
6482
6516
  image_path = Path(str(image.get("captioned_path") or image.get("path") or ""))
6483
6517
  with image_col:
6484
6518
  if image_path.is_file():
6485
- st.image(str(image_path), use_container_width=True)
6519
+ st.image(_file_bytes(image_path), use_container_width=True)
6486
6520
  st.caption(str(image.get("status") or "pendente"))
6487
6521
  with cols[1]:
6488
6522
  st.caption("Estado")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.7.67",
3
+ "version": "0.7.68",
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",