@danhachuel/thunderbolt 0.5.94 → 0.5.95

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/hermes_ui/cuts.py CHANGED
@@ -277,9 +277,11 @@ def build_clip_command(source: Path, output: Path, segment: dict[str, float], ou
277
277
  "-vf",
278
278
  _video_filter(output_format),
279
279
  "-c:v",
280
- "libx264",
280
+ "h264_nvenc",
281
281
  "-preset",
282
- "veryfast",
282
+ "p1",
283
+ "-pix_fmt",
284
+ "yuv420p",
283
285
  "-crf",
284
286
  "22",
285
287
  "-c:a",
@@ -295,12 +297,19 @@ def _run_ffmpeg(command: list[str], output: Path, *, source: Path, segment: dict
295
297
  completed = subprocess.run(command, capture_output=True, text=True, check=False)
296
298
  except OSError as exc:
297
299
  raise CutsError(f"Não foi possível iniciar FFmpeg: {exc}") from exc
300
+ executed_command = list(command)
301
+ if completed.returncode != 0 and "h264_nvenc" in executed_command:
302
+ fallback = ["libx264" if item == "h264_nvenc" else "veryfast" if item == "p1" else item for item in executed_command]
303
+ if output.exists():
304
+ output.unlink()
305
+ completed = subprocess.run(fallback, capture_output=True, text=True, check=False)
306
+ executed_command = fallback
298
307
  if completed.returncode != 0 or not output.is_file() or output.stat().st_size == 0:
299
308
  if output.exists():
300
309
  output.unlink()
301
310
  detail = (completed.stderr or completed.stdout or "erro desconhecido").strip()
302
311
  raise CutsError(f"O clip não pôde ser gerado: {detail[-1200:]}")
303
- return {"source": str(source), "output": str(output), "command": command, "segment": segment, "created_at": _now()}
312
+ return {"source": str(source), "output": str(output), "command": executed_command, "segment": segment, "created_at": _now()}
304
313
 
305
314
 
306
315
  def _write_manifest(record: dict[str, Any], run_dir: Path) -> Path:
@@ -150,10 +150,17 @@ def store_uploaded_asset(name: str, content: bytes, *, audio: bool = False) -> P
150
150
 
151
151
  def _run(command: list[str], output: Path, operation: str, source: Path, *, extra: dict[str, Any] | None = None) -> tuple[Path, dict[str, Any]]:
152
152
  output.parent.mkdir(parents=True, exist_ok=True)
153
+ executed_command = list(command)
153
154
  try:
154
- completed = subprocess.run(command, capture_output=True, text=True, check=False)
155
+ completed = subprocess.run(executed_command, capture_output=True, text=True, check=False)
155
156
  except OSError as exc:
156
157
  raise PythonEditorError(f"Não foi possível iniciar FFmpeg: {exc}") from exc
158
+ if completed.returncode != 0 and "h264_nvenc" in executed_command:
159
+ fallback = ["libx264" if item == "h264_nvenc" else "veryfast" if item == "p1" else item for item in executed_command]
160
+ if output.exists():
161
+ output.unlink()
162
+ completed = subprocess.run(fallback, capture_output=True, text=True, check=False)
163
+ executed_command = fallback
157
164
  if completed.returncode != 0 or not output.is_file() or output.stat().st_size == 0:
158
165
  if output.exists():
159
166
  output.unlink()
@@ -163,7 +170,7 @@ def _run(command: list[str], output: Path, operation: str, source: Path, *, extr
163
170
  "operation": operation,
164
171
  "source": str(source),
165
172
  "output": str(output),
166
- "command": command,
173
+ "command": executed_command,
167
174
  "created_at": _now(),
168
175
  }
169
176
  if extra:
@@ -224,7 +231,7 @@ def resize_video(source: Path, width: int, height: int, *, ffmpeg_path: str | No
224
231
  raise PythonEditorError("As dimensões do vídeo devem ser pelo menos 16x16.")
225
232
  output = _output_path(source, "redimensionado")
226
233
  scale = f"scale={width}:{height}:force_original_aspect_ratio=decrease,pad={width}:{height}:(ow-iw)/2:(oh-ih)/2"
227
- command = [_ffmpeg(ffmpeg_path), "-y", "-hide_banner", "-loglevel", "error", "-i", str(source), "-vf", scale, "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-c:a", "aac", "-movflags", "+faststart", str(output)]
234
+ command = [_ffmpeg(ffmpeg_path), "-y", "-hide_banner", "-loglevel", "error", "-i", str(source), "-vf", scale, "-c:v", "h264_nvenc", "-preset", "p1", "-pix_fmt", "yuv420p", "-c:a", "aac", "-movflags", "+faststart", str(output)]
228
235
  return _run(command, output, "redimensionamento", source, extra={"width": width, "height": height})
229
236
 
230
237
 
@@ -234,7 +241,7 @@ def change_speed(source: Path, speed: float, *, ffmpeg_path: str | None = None)
234
241
  if speed < 0.25 or speed > 4.0:
235
242
  raise PythonEditorError("A velocidade deve estar entre 0,25x e 4x.")
236
243
  output = _output_path(source, "velocidade")
237
- command = [_ffmpeg(ffmpeg_path), "-y", "-hide_banner", "-loglevel", "error", "-i", str(source), "-filter:v", f"setpts=PTS/{speed:.6f}", "-filter:a", _atempo_chain(speed), "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-c:a", "aac", "-movflags", "+faststart", str(output)]
244
+ command = [_ffmpeg(ffmpeg_path), "-y", "-hide_banner", "-loglevel", "error", "-i", str(source), "-filter:v", f"setpts=PTS/{speed:.6f}", "-filter:a", _atempo_chain(speed), "-c:v", "h264_nvenc", "-preset", "p1", "-pix_fmt", "yuv420p", "-c:a", "aac", "-movflags", "+faststart", str(output)]
238
245
  return _run(command, output, "alteração de velocidade", source, extra={"speed": speed})
239
246
 
240
247
 
@@ -265,7 +265,7 @@ DEFAULTS: dict[str, Any] = {
265
265
  "sonilo_api_key": "",
266
266
  "subtitle_provider": "edge",
267
267
  "ffmpeg_path": "",
268
- "video_codec": "",
268
+ "video_codec": "h264_nvenc",
269
269
  "material_directory": "",
270
270
  "whisper_model_size": "large-v3",
271
271
  "whisper_device": "cpu",
@@ -8,6 +8,8 @@ from typing import Any
8
8
  from hermes_ui.languages import language_code
9
9
  from hermes_ui.material_sources import all_material_api_keys, selected_material_source
10
10
 
11
+ NVENC_VIDEO_CODEC = "h264_nvenc"
12
+
11
13
  try:
12
14
  import toml
13
15
  except ImportError: # pragma: no cover - installation fallback
@@ -118,6 +120,10 @@ def build_moneyprinter_config(settings: dict[str, Any], existing: dict[str, Any]
118
120
  for settings_key, config_key in app_map.items():
119
121
  if settings_key in settings:
120
122
  app[config_key] = settings[settings_key]
123
+ # The bundled video pipeline is required to use NVIDIA NVENC. The
124
+ # MoneyPrinterTurbo runtime falls back to libx264 when this encoder is not
125
+ # available on the host, preserving a playable output instead of failing.
126
+ app["video_codec"] = NVENC_VIDEO_CODEC
121
127
  # Edge TTS V1 is retained as a keyless fallback, but its default 30-second
122
128
  # stream deadline is too short for longer scripts or slow networks.
123
129
  try:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.94",
3
+ "version": "0.5.95",
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",