@danhachuel/thunderbolt 0.4.71 → 0.4.73
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 +27 -0
- package/hermes_ui/update_manager.py +4 -1
- package/package.json +1 -1
- package/scripts/cli.mjs +36 -10
package/app/main.py
CHANGED
|
@@ -4349,6 +4349,28 @@ def load_video_tasks_for_catalog() -> list[dict[str, Any]]:
|
|
|
4349
4349
|
return [task for task in saved if isinstance(task, dict) and str(task.get("id") or "").strip()]
|
|
4350
4350
|
|
|
4351
4351
|
|
|
4352
|
+
def _task_thumbnail_path(task: dict[str, Any]) -> Path | None:
|
|
4353
|
+
"""Resolve a generated thumbnail from current and legacy task fields."""
|
|
4354
|
+
artifacts = task.get("artifacts") if isinstance(task.get("artifacts"), dict) else {}
|
|
4355
|
+
candidates = [
|
|
4356
|
+
artifacts.get("thumbnail"),
|
|
4357
|
+
artifacts.get("cover"),
|
|
4358
|
+
task.get("thumbnail_path"),
|
|
4359
|
+
task.get("thumbnail_url"),
|
|
4360
|
+
]
|
|
4361
|
+
for candidate in candidates:
|
|
4362
|
+
raw = str(candidate or "").strip()
|
|
4363
|
+
if not raw or raw.startswith(("http://", "https://")):
|
|
4364
|
+
continue
|
|
4365
|
+
paths = [Path(raw)]
|
|
4366
|
+
if not Path(raw).is_absolute():
|
|
4367
|
+
paths.extend([STORAGE / raw, STORAGE / "videos" / raw])
|
|
4368
|
+
for path in paths:
|
|
4369
|
+
if path.is_file():
|
|
4370
|
+
return path
|
|
4371
|
+
return None
|
|
4372
|
+
|
|
4373
|
+
|
|
4352
4374
|
def _is_music_task(task: dict[str, Any]) -> bool:
|
|
4353
4375
|
"""Identify music pipeline tasks without conflating them with ordinary video tasks."""
|
|
4354
4376
|
return bool(task.get("music_mode")) or str(task.get("style_wide") or task.get("style") or "").strip().casefold() in {"music", "música"}
|
|
@@ -4901,6 +4923,11 @@ def render_automation():
|
|
|
4901
4923
|
with st.container(border=True):
|
|
4902
4924
|
task_cols = st.columns([2.25, 1.65, 1.15, 1.85])
|
|
4903
4925
|
with task_cols[0]:
|
|
4926
|
+
thumbnail_path = _task_thumbnail_path(task)
|
|
4927
|
+
if thumbnail_path:
|
|
4928
|
+
st.image(thumbnail_path, width=180, caption="Thumbnail")
|
|
4929
|
+
else:
|
|
4930
|
+
st.caption("Thumbnail ainda não pronta")
|
|
4904
4931
|
st.write(f"**{task.get('topic', 'Sem tópico')}**")
|
|
4905
4932
|
st.caption(f"{task.get('channel_name', 'Canal')} · {task.get('id', '')}")
|
|
4906
4933
|
with task_cols[1]:
|
|
@@ -18,6 +18,7 @@ import requests
|
|
|
18
18
|
PACKAGE_NAME = "@danhachuel/thunderbolt"
|
|
19
19
|
REGISTRY_URL = "https://registry.npmjs.org/@danhachuel/thunderbolt/latest"
|
|
20
20
|
UPDATE_TIMEOUT_SECONDS = 20 * 60
|
|
21
|
+
LAUNCHER_RESTART_EXIT_CODE = 75
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@dataclass(frozen=True)
|
|
@@ -44,7 +45,9 @@ class UpdateResult:
|
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
def restart_current_process(*, exec_fn: Callable[..., Any] = os.execv) -> None:
|
|
47
|
-
"""
|
|
48
|
+
"""Ask the launcher to restart Streamlit, or re-exec standalone callers."""
|
|
49
|
+
if os.environ.get("THUNDERBOLT_LAUNCHER_RESTART") == "1":
|
|
50
|
+
raise SystemExit(LAUNCHER_RESTART_EXIT_CODE)
|
|
48
51
|
executable = sys.executable
|
|
49
52
|
exec_fn(executable, [executable, *sys.argv])
|
|
50
53
|
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -242,23 +242,49 @@ const pipelineWorker = spawn(python, ["-m", "hermes_ui.pipeline_worker"], {
|
|
|
242
242
|
env: runtimeEnv,
|
|
243
243
|
windowsHide: false,
|
|
244
244
|
});
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
245
|
+
const restartExitCode = 75;
|
|
246
|
+
let shuttingDown = false;
|
|
247
|
+
let child;
|
|
248
|
+
|
|
249
|
+
function startStreamlit() {
|
|
250
|
+
child = spawn(python, ["-m", "streamlit", "run", main, "--server.port", String(backendPort), "--server.address", "127.0.0.1"], {
|
|
251
|
+
cwd: root,
|
|
252
|
+
stdio: "ignore",
|
|
253
|
+
env: { ...runtimeEnv, THUNDERBOLT_LAUNCHER_RESTART: "1" },
|
|
254
|
+
windowsHide: false,
|
|
255
|
+
});
|
|
256
|
+
child.on("exit", (code, signal) => {
|
|
257
|
+
if (shuttingDown) {
|
|
258
|
+
stopWorker();
|
|
259
|
+
process.exit(code ?? (signal ? 1 : 0));
|
|
260
|
+
}
|
|
261
|
+
if (code === restartExitCode) {
|
|
262
|
+
const executable = platform() === "win32" ? "npx.cmd" : "npx";
|
|
263
|
+
const replacement = spawn(executable, ["--yes", "--prefer-online", "@danhachuel/thunderbolt"], {
|
|
264
|
+
cwd: userHome,
|
|
265
|
+
env: process.env,
|
|
266
|
+
detached: true,
|
|
267
|
+
stdio: "ignore",
|
|
268
|
+
windowsHide: false,
|
|
269
|
+
});
|
|
270
|
+
replacement.unref();
|
|
271
|
+
stopWorker();
|
|
272
|
+
process.exit(0);
|
|
273
|
+
}
|
|
274
|
+
stopWorker();
|
|
275
|
+
process.exit(code ?? (signal ? 1 : 0));
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
startStreamlit();
|
|
251
280
|
|
|
252
281
|
const stopWorker = () => {
|
|
282
|
+
shuttingDown = true;
|
|
253
283
|
proxy.close();
|
|
254
284
|
if (!worker.killed) worker.kill();
|
|
255
285
|
if (!pipelineWorker.killed) pipelineWorker.kill();
|
|
256
286
|
};
|
|
257
287
|
process.on("SIGINT", stopWorker);
|
|
258
288
|
process.on("SIGTERM", stopWorker);
|
|
259
|
-
child.on("exit", (code, signal) => {
|
|
260
|
-
stopWorker();
|
|
261
|
-
process.exit(code ?? (signal ? 1 : 0));
|
|
262
|
-
});
|
|
263
289
|
worker.on("error", (error) => console.error(`Thunderbolt worker: ${error.message}`));
|
|
264
290
|
pipelineWorker.on("error", (error) => console.error(`Thunderbolt pipeline worker: ${error.message}`));
|