fix(photo): source finish photo from forced timelapse + cleanup (#1397)

Bambu's end-gcode lowers the bed at gcode_state=FINISH. Bambuddy's
  live-camera grab captured the bed already dropped, ruining the photo
  framing. Source the photo from a brief Bambu timelapse instead —
  firmware stops timelapse recording AFTER toolhead parks but BEFORE
  bed-drop runs, so the last frame frames the finished print correctly.

  When capture_finish_photo is on AND the user did not opt in to
  timelapse for this print, force timelapse=True at dispatch + mark the
  new PrintArchive.bambuddy_forced_timelapse column. After extraction
  (success or failure), cleanup deletes the locally-attached file,
  clears archive.timelapse_path, and walks the four scanner directories
  (/timelapse, /timelapse/video, /record, /recording) trying FTP DELE
  against the original filename. User-opted-in timelapses pass through
  unchanged.

  Resolver lives at services/background_dispatch.py::resolve_effective_timelapse
  (module-level so the print queue can reuse it). Both dispatch paths
  wired: background_dispatch.py (Print Now / Reprint) AND
  print_scheduler.py:_start_print (the queue). Field testing caught the
  scheduler gap on the first round — AST regression test now asserts
  start_print(timelapse=...) references effective_timelapse, not the raw
  item.timelapse, so a future refactor can't silently drop it.

  Extractor: ffmpeg -i input.mp4 -update 1 -q:v 2 out.jpg. Decoded
  frames overwrite the same output file, so the file left on disk is the
  literal last frame regardless of duration. Bambu records one frame per
  layer-change, so a 16-layer cube produces a 0.6 s timelapse — the
  original -sseof -1.0 approach seeked before the start of the file and
  returned frame 0 (empty bed). Decoding every frame is fine; Bambu
  timelapses are short by construction even on hours-long prints.

  Migration adds bambuddy_forced_timelapse branched on is_sqlite()
  (DEFAULT 0 / DEFAULT FALSE — PG rejects DEFAULT 0 for BOOLEAN).
  Verified live on postgres:16-alpine.

  Photo-task wait_for budget extends 45s -> 75s when timelapse_was_active
  so the notification carries the bed-up photo instead of falling back
  to the live-cam grab on slow links.

  Scope limit, documented in the camera wiki: prints started directly
  on the printer touchscreen / Bambu Handy / Bambu Studio Send bypass
  both dispatch paths, so the override doesn't fire there. Future
  option: mid-print M981 S1 P20000 MQTT toggle in on_print_start.

  Setting description rewritten in all 11 locales to drop the "only
  works when timelapse enabled" caveat (Bambuddy now forces it) and
  explain the kept-or-deleted behaviour.
This commit is contained in:
maziggy
2026-06-05 13:53:26 +02:00
parent eb418a5b37
commit 12d17bfbe7
28 changed files with 1238 additions and 74 deletions
+1 -1
View File
@@ -2156,7 +2156,7 @@ export default {
autoArchivePrints: 'Drucke automatisch archivieren',
autoArchiveDescription: '3MF-Dateien automatisch speichern, wenn Drucke abgeschlossen sind',
saveThumbnailsDescription: 'Vorschaubilder aus 3MF-Dateien extrahieren und speichern',
captureFinishPhotoDescription: 'Foto von der Druckerkamera aufnehmen, wenn der Druck abgeschlossen ist',
captureFinishPhotoDescription: 'Foto von der Druckerkamera aufnehmen, wenn der Druck abgeschlossen ist. Bambuddy zeichnet während des Drucks einen kurzen Zeitraffer auf, damit das Foto aus dem Moment vor dem Absenken der Druckplatte stammen kann. Die Zeitraffer-Datei bleibt erhalten, wenn du den Zeitraffer für diesen Druck aktiviert hast, andernfalls wird sie nach Aufnahme des Fotos automatisch gelöscht.',
ffmpegNotInstalled: 'ffmpeg nicht installiert',
ffmpegRequired: 'Kameraaufnahme benötigt ffmpeg. Installieren über <brew>brew install ffmpeg</brew> (macOS) oder <apt>apt install ffmpeg</apt> (Linux).',
// Camera
+1 -1
View File
@@ -2159,7 +2159,7 @@ export default {
autoArchivePrints: 'Auto-archive prints',
autoArchiveDescription: 'Automatically save 3MF files when prints complete',
saveThumbnailsDescription: 'Extract and save preview images from 3MF files',
captureFinishPhotoDescription: 'Take a photo from printer camera when print completes',
captureFinishPhotoDescription: 'Take a photo from printer camera when print completes. Bambuddy records a brief timelapse during the print so the photo can be sourced from the moment before the bed drops; the timelapse file is kept if you enabled timelapse for this print, otherwise it is deleted automatically after the photo is captured.',
ffmpegNotInstalled: 'ffmpeg not installed',
ffmpegRequired: 'Camera capture requires ffmpeg. Install it via <brew>brew install ffmpeg</brew> (macOS) or <apt>apt install ffmpeg</apt> (Linux).',
// Camera
+1 -1
View File
@@ -2159,7 +2159,7 @@ export default {
autoArchivePrints: 'Archivar impresiones automáticamente',
autoArchiveDescription: 'Guardar automáticamente los archivos 3MF cuando se completan las impresiones',
saveThumbnailsDescription: 'Extraer y guardar imágenes de vista previa de los archivos 3MF',
captureFinishPhotoDescription: 'Tomar una foto desde la cámara de la impresora cuando se completa la impresión',
captureFinishPhotoDescription: 'Tomar una foto desde la cámara de la impresora cuando se completa la impresión. Bambuddy graba un breve timelapse durante la impresión para que la foto pueda obtenerse del momento previo al descenso de la cama; el archivo del timelapse se conserva si activaste el timelapse para esta impresión, de lo contrario se elimina automáticamente tras capturar la foto.',
ffmpegNotInstalled: 'ffmpeg no instalado',
ffmpegRequired: 'La captura de cámara requiere ffmpeg. Instálelo mediante <brew>brew install ffmpeg</brew> (macOS) o <apt>apt install ffmpeg</apt> (Linux).',
// Camera
+1 -1
View File
@@ -2110,7 +2110,7 @@ export default {
autoArchivePrints: 'Archiver automatiquement les impressions',
autoArchiveDescription: 'Sauvegarder automatiquement les fichiers 3MF à la fin des impressions',
saveThumbnailsDescription: 'Extraire et sauvegarder les images d\'aperçu des fichiers 3MF',
captureFinishPhotoDescription: 'Prendre une photo avec la caméra de l\'imprimante à la fin de l\'impression',
captureFinishPhotoDescription: 'Prendre une photo avec la caméra de l\'imprimante à la fin de l\'impression. Bambuddy enregistre un court timelapse pendant l\'impression afin que la photo puisse provenir du moment précédant l\'abaissement du plateau ; le fichier du timelapse est conservé si vous avez activé le timelapse pour cette impression, sinon il est supprimé automatiquement après la capture de la photo.',
ffmpegNotInstalled: 'ffmpeg non installé',
ffmpegRequired: 'La capture caméra nécessite ffmpeg. Installez-le via <brew>brew install ffmpeg</brew> (macOS) ou <apt>apt install ffmpeg</apt> (Linux).',
camera: 'Caméra',
+1 -1
View File
@@ -2109,7 +2109,7 @@ export default {
autoArchivePrints: 'Archiviazione automatica stampe',
autoArchiveDescription: 'Salva automaticamente i file 3MF al completamento delle stampe',
saveThumbnailsDescription: 'Estrai e salva le immagini di anteprima dai file 3MF',
captureFinishPhotoDescription: 'Scatta una foto dalla fotocamera della stampante al completamento della stampa',
captureFinishPhotoDescription: 'Scatta una foto dalla fotocamera della stampante al completamento della stampa. Bambuddy registra un breve timelapse durante la stampa in modo che la foto possa essere ricavata dal momento precedente all\'abbassamento del piatto; il file del timelapse viene mantenuto se hai abilitato il timelapse per questa stampa, altrimenti viene eliminato automaticamente dopo l\'acquisizione della foto.',
ffmpegNotInstalled: 'ffmpeg non installato',
ffmpegRequired: 'L\'acquisizione dalla fotocamera richiede ffmpeg. Installalo tramite <brew>brew install ffmpeg</brew> (macOS) o <apt>apt install ffmpeg</apt> (Linux).',
camera: 'Fotocamera',
+1 -1
View File
@@ -2155,7 +2155,7 @@ export default {
autoArchivePrints: '印刷を自動アーカイブ',
autoArchiveDescription: '印刷完了時に3MFファイルを自動保存',
saveThumbnailsDescription: '3MFファイルからプレビュー画像を抽出して保存',
captureFinishPhotoDescription: '印刷完了時にプリンターカメラから写真を撮影',
captureFinishPhotoDescription: '印刷完了時にプリンターカメラから写真を撮影します。Bambuddy は印刷中に短いタイムラプスを記録し、ベッドが下がる前の瞬間から写真を取得できるようにします。この印刷でタイムラプスを有効にしていた場合はタイムラプスファイルが保存され、それ以外の場合は写真の取得後に自動的に削除されます。',
ffmpegNotInstalled: 'ffmpegがインストールされていません',
ffmpegRequired: 'カメラ撮影にはffmpegが必要です。<brew>brew install ffmpeg</brew>(macOS)または<apt>apt install ffmpeg</apt>(Linux)でインストールしてください。',
// Camera
+1 -1
View File
@@ -2026,7 +2026,7 @@ export default {
autoArchivePrints: '인쇄 자동 아카이브',
autoArchiveDescription: '인쇄 완료 시 3MF 파일 자동 저장',
saveThumbnailsDescription: '3MF 파일에서 미리보기 이미지 추출 및 저장',
captureFinishPhotoDescription: '인쇄 완료 시 프린터 카메라로 사진 촬영',
captureFinishPhotoDescription: '인쇄 완료 시 프린터 카메라로 사진 촬영. Bambuddy는 인쇄 중 짧은 타임랩스를 기록하여 베드가 내려가기 전 순간에서 사진을 가져올 수 있도록 합니다. 이 인쇄에 대해 타임랩스를 활성화한 경우 타임랩스 파일이 보관되며, 그렇지 않으면 사진 촬영 후 자동으로 삭제됩니다.',
ffmpegNotInstalled: 'ffmpeg 미설치',
ffmpegRequired: '카메라 캡처에 ffmpeg가 필요합니다. macOS에서는 <brew>brew install ffmpeg</brew>, Linux에서는 <apt>apt install ffmpeg</apt>로 설치하세요.',
camera: '카메라',
+1 -1
View File
@@ -2109,7 +2109,7 @@ export default {
autoArchivePrints: 'Arquivar impressões automaticamente',
autoArchiveDescription: 'Salvar automaticamente arquivos 3MF quando impressões forem concluídas',
saveThumbnailsDescription: 'Extrair e salvar imagens de pré-visualização dos arquivos 3MF',
captureFinishPhotoDescription: 'Tirar foto da câmera da impressora quando a impressão for concluída',
captureFinishPhotoDescription: 'Tirar foto da câmera da impressora quando a impressão for concluída. Bambuddy grava um timelapse curto durante a impressão para que a foto possa ser obtida do momento antes da mesa descer; o arquivo do timelapse é mantido se você habilitou o timelapse para esta impressão, caso contrário ele é excluído automaticamente após a captura da foto.',
ffmpegNotInstalled: 'ffmpeg não instalado',
ffmpegRequired: 'A captura de câmera requer ffmpeg. Instale via <brew>brew install ffmpeg</brew> (macOS) ou <apt>apt install ffmpeg</apt> (Linux).',
camera: 'Câmera',
+1 -1
View File
@@ -2159,7 +2159,7 @@ export default {
autoArchivePrints: 'Baskıları otomatik arşivle',
autoArchiveDescription: 'Baskılar tamamlandığında 3MF dosyalarını otomatik olarak kaydet',
saveThumbnailsDescription: '3MF dosyalarından önizleme görüntülerini çıkar ve kaydet',
captureFinishPhotoDescription: 'Baskı tamamlandığında yazıcı kamerasından bir fotoğraf çek',
captureFinishPhotoDescription: 'Baskı tamamlandığında yazıcı kamerasından bir fotoğraf çek. Bambuddy, baskı sırasında kısa bir zaman atlamalı kayıt yapar, böylece fotoğraf tabla inmeden önceki andan alınabilir. Bu baskı için zaman atlamalı kaydı etkinleştirdiyseniz dosya saklanır, aksi takdirde fotoğraf çekildikten sonra otomatik olarak silinir.',
ffmpegNotInstalled: 'ffmpeg yüklü değil',
ffmpegRequired: 'Kamera yakalama ffmpeg gerektirir. <brew>brew install ffmpeg</brew> (macOS) veya <apt>apt install ffmpeg</apt> (Linux) ile yükleyin.',
// Kamera
+1 -1
View File
@@ -2154,7 +2154,7 @@ export default {
autoArchivePrints: '自动归档打印',
autoArchiveDescription: '打印完成时自动保存3MF文件',
saveThumbnailsDescription: '从3MF文件中提取并保存预览图像',
captureFinishPhotoDescription: '打印完成时从打印机摄像头拍照',
captureFinishPhotoDescription: '打印完成时从打印机摄像头拍照。Bambuddy 会在打印期间录制一段短延时摄影,以便从热床下降前的瞬间获取照片;如果您为本次打印启用了延时摄影,文件将保留,否则会在拍照完成后自动删除。',
ffmpegNotInstalled: '未安装ffmpeg',
ffmpegRequired: '摄像头捕获需要ffmpeg。通过 <brew>brew install ffmpeg</brew>(macOS)或 <apt>apt install ffmpeg</apt>(Linux)安装。',
camera: '摄像头',
+1 -1
View File
@@ -2154,7 +2154,7 @@ export default {
autoArchivePrints: '自動歸檔列印',
autoArchiveDescription: '列印完成時自動儲存3MF檔案',
saveThumbnailsDescription: '從3MF檔案中提取並儲存預覽影像',
captureFinishPhotoDescription: '列印完成時從印表機攝影機拍照',
captureFinishPhotoDescription: '列印完成時從印表機攝影機拍照。Bambuddy 會在列印期間錄製一段短縮時攝影,以便從熱床下降前的瞬間取得照片;如果您為本次列印啟用了縮時攝影,檔案將保留,否則會在拍照完成後自動刪除。',
ffmpegNotInstalled: '未安裝ffmpeg',
ffmpegRequired: '攝影機捕獲需要ffmpeg。透過 <brew>brew install ffmpeg</brew>(macOS)或 <apt>apt install ffmpeg</apt>(Linux)安裝。',
camera: '攝影機',