[PR #453] [CLOSED] WIP - feat(camera): Printer Page - Print farm camera grid #1046

Closed
opened 2026-05-06 12:34:41 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/maziggy/bambuddy/pull/453
Author: @aneopsy
Created: 2/19/2026
Status: Closed

Base: 0.2.1Head: printers-camera-grid


📝 Commits (5)

  • 182020d feat(camera): shared stream hub, canvas grid viewer, multiplexed streaming, and bandwidth optimization
  • bd5e56a feat(camera): add pause, resume, and stop controls to camera cardsEnhances camera grid with print controls
  • f252bcb feat(camera-grid): worker decoding, IntersectionObserver, reconnect, quality switching, and UX improvements
  • 2c5447a feat(camera-grid): HMS error notifications, ETA tooltip, control loading states, and backend hardening
  • 8ed9b2f .

📊 Changes

24 files changed (+10565 additions, -8985 deletions)

View changed files

📝 .gitignore (+2 -0)
📝 .pre-commit-config.yaml (+1 -1)
📝 backend/app/api/routes/camera.py (+708 -219)
📝 backend/app/main.py (+13 -2)
📝 backend/app/services/camera.py (+28 -4)
📝 backend/app/services/external_camera.py (+57 -51)
📝 frontend/src/components/Card.tsx (+4 -2)
📝 frontend/src/components/EmbeddedCameraViewer.tsx (+1 -1)
📝 frontend/src/components/HMSErrorModal.tsx (+14 -0)
📝 frontend/src/i18n/locales/de.ts (+17 -0)
📝 frontend/src/i18n/locales/en.ts (+17 -0)
📝 frontend/src/i18n/locales/fr.ts (+17 -0)
📝 frontend/src/i18n/locales/it.ts (+17 -0)
📝 frontend/src/i18n/locales/ja.ts (+17 -0)
📝 frontend/src/i18n/locales/pt-BR.ts (+17 -0)
📝 frontend/src/index.css (+5 -0)
📝 frontend/src/pages/PrintersPage.tsx (+871 -16)
frontend/src/workers/cameraGridDecoder.worker.ts (+69 -0)
static/assets/cameraGridDecoder.worker-JysGdQBP.js (+1 -0)
static/assets/index-Cmk7uc2U.css (+1 -0)

...and 4 more files

📄 Description

Description

  • Print farm dashboard with real-time CCTV-style camera grid:

    • Designed for print farm operators managing multiple printers at a glance.
    • Dashboard provides a CCTV-like camera grid showing all printers in real time.
    • Printers are sorted by progress so jobs finishing soon appear first.
    • Paused printers are visually accented to alert operators for immediate action.
    • Includes a quality selector (Low/Med/High) to reduce CPU and bandwidth usage on large print farms with many cameras.
  • The multiplexed stream sends all camera feeds over a single HTTP connection using binary framing ([4B printer_id][4B length][jpeg_data]). This solves the browser's 6-connection-per-origin limit — previously, opening 7+ cameras would saturate all connections and freeze the UI (no API calls, no WebSocket updates). Now 20 cameras use 1 connection, leaving 5 free for the rest of the app. (8 cameras on the grid, medium quality (5fps) use 1 mb/s bandwidth)

  • Canvas renders frames via createImageBitmap() (off-main-thread JPEG decode) with cached 2D contexts and dimension tracking, no per-frame React re-renders, no blob URLs, and bitmap.close() frees GPU memory immediately, material optimisation.

Changes

Backend - SharedStreamHub rewrite:

  • Replace ref-counted subscribe/unsubscribe with decoupled producer/viewer design
  • Viewers use simple polling loops with zero cleanup (no locks, no aclose chains)
  • Producers self-stop after 30s idle timeout instead of ref counting
  • Properly close source generators (ffmpeg/SSL) via aclose() in producer finally
  • Eliminates toggle off/on black screen and stuck backend issues caused by leaked
    ref counts, async generator lifecycle bugs, and lock contention

Backend - Stream quality/bandwidth controls:

  • Add quality (2-31) and scale (0.1-1.0) params to /camera/stream endpoint
  • Pass quality to ffmpeg -q:v and scale to -vf scale filter
  • Grid view uses fps=5, quality=15, scale=0.5 (~20-30x bandwidth reduction)
  • Single camera keeps fps=15, quality=5, scale=1.0 (full quality)

Backend - Multiplexed grid-stream endpoint:

  • Add binary-framed multiplexed endpoint to avoid browser 6-connection limit
  • Optimize with byte array buffers, time.monotonic(), and DRY producer helper

Frontend - CameraGridCard canvas renderer:

  • Replace img element with fetch() + AbortController + canvas drawing
  • AbortController.abort() reliably kills TCP connections on unmount
    (img.src='' does NOT close MJPEG connections, saturating browser 6-conn limit)
  • Parse JPEG frames from stream via SOI/EOI markers, draw with createImageBitmap
  • Canvas drawing creates zero network requests (no blob URL spam)
  • Add loading spinner and error state indicators

Frontend - Enhanced camera grid cards:

  • Add printer state overlay with progress details (time remaining, layers)
  • Show progress bar and time remaining when paused, yellow bar on pause state
  • Add camera quality selector (Low/Med/High)

Frontend - EmbeddedCameraViewer:

  • Add explicit quality=5&scale=1.0 params for full-quality single camera view

Frontend - Misc cleanup:

  • Add progress sort option (first printer to finish the job is show first - last to finish the job is last)

Fixes #451

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test addition or update

Screenshots

Frontend:

Screenshot 2026-02-19 at 11 24 07 PM image

Backend optimisation - CPU usage for camera stream:

Before:
image

After:
image

Testing

  • I have tested this on my local machine
  • I have tested with my printer model: X1C, H2S, H2D

Checklist

  • My code follows the project's coding style
  • I have commented my code where necessary
  • I have updated the documentation (if needed)
  • My changes generate no new warnings
  • I have tested my changes thoroughly

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/maziggy/bambuddy/pull/453 **Author:** [@aneopsy](https://github.com/aneopsy) **Created:** 2/19/2026 **Status:** ❌ Closed **Base:** `0.2.1` ← **Head:** `printers-camera-grid` --- ### 📝 Commits (5) - [`182020d`](https://github.com/maziggy/bambuddy/commit/182020df38fd722d02ea6fe4a1e05cdc0a976ccb) feat(camera): shared stream hub, canvas grid viewer, multiplexed streaming, and bandwidth optimization - [`bd5e56a`](https://github.com/maziggy/bambuddy/commit/bd5e56a55597882c47761d413e3e05b08d897393) feat(camera): add pause, resume, and stop controls to camera cardsEnhances camera grid with print controls - [`f252bcb`](https://github.com/maziggy/bambuddy/commit/f252bcbce220569ea2e5afa25124651aa75255bd) feat(camera-grid): worker decoding, IntersectionObserver, reconnect, quality switching, and UX improvements - [`2c5447a`](https://github.com/maziggy/bambuddy/commit/2c5447ae8444fb873a136b4f480402be45201feb) feat(camera-grid): HMS error notifications, ETA tooltip, control loading states, and backend hardening - [`8ed9b2f`](https://github.com/maziggy/bambuddy/commit/8ed9b2f44d7c244eddf318b477a5b0c0fe2ce2de) . ### 📊 Changes **24 files changed** (+10565 additions, -8985 deletions) <details> <summary>View changed files</summary> 📝 `.gitignore` (+2 -0) 📝 `.pre-commit-config.yaml` (+1 -1) 📝 `backend/app/api/routes/camera.py` (+708 -219) 📝 `backend/app/main.py` (+13 -2) 📝 `backend/app/services/camera.py` (+28 -4) 📝 `backend/app/services/external_camera.py` (+57 -51) 📝 `frontend/src/components/Card.tsx` (+4 -2) 📝 `frontend/src/components/EmbeddedCameraViewer.tsx` (+1 -1) 📝 `frontend/src/components/HMSErrorModal.tsx` (+14 -0) 📝 `frontend/src/i18n/locales/de.ts` (+17 -0) 📝 `frontend/src/i18n/locales/en.ts` (+17 -0) 📝 `frontend/src/i18n/locales/fr.ts` (+17 -0) 📝 `frontend/src/i18n/locales/it.ts` (+17 -0) 📝 `frontend/src/i18n/locales/ja.ts` (+17 -0) 📝 `frontend/src/i18n/locales/pt-BR.ts` (+17 -0) 📝 `frontend/src/index.css` (+5 -0) 📝 `frontend/src/pages/PrintersPage.tsx` (+871 -16) ➕ `frontend/src/workers/cameraGridDecoder.worker.ts` (+69 -0) ➕ `static/assets/cameraGridDecoder.worker-JysGdQBP.js` (+1 -0) ➕ `static/assets/index-Cmk7uc2U.css` (+1 -0) _...and 4 more files_ </details> ### 📄 Description ## Description - Print farm dashboard with real-time CCTV-style camera grid: - Designed for print farm operators managing multiple printers at a glance. - Dashboard provides a CCTV-like camera grid showing all printers in real time. - Printers are sorted by progress so jobs finishing soon appear first. - Paused printers are visually accented to alert operators for immediate action. - Includes a quality selector (Low/Med/High) to reduce CPU and bandwidth usage on large print farms with many cameras. - The multiplexed stream sends all camera feeds over a single HTTP connection using binary framing ([4B printer_id][4B length][jpeg_data]). This solves the browser's 6-connection-per-origin limit — previously, opening 7+ cameras would saturate all connections and freeze the UI (no API calls, no WebSocket updates). Now 20 cameras use 1 connection, leaving 5 free for the rest of the app. (8 cameras on the grid, medium quality (5fps) use 1 mb/s bandwidth) - Canvas renders frames via createImageBitmap() (off-main-thread JPEG decode) with cached 2D contexts and dimension tracking, no per-frame React re-renders, no blob URLs, and bitmap.close() frees GPU memory immediately, material optimisation. ## Changes Backend - SharedStreamHub rewrite: - Replace ref-counted subscribe/unsubscribe with decoupled producer/viewer design - Viewers use simple polling loops with zero cleanup (no locks, no aclose chains) - Producers self-stop after 30s idle timeout instead of ref counting - Properly close source generators (ffmpeg/SSL) via aclose() in producer finally - Eliminates toggle off/on black screen and stuck backend issues caused by leaked ref counts, async generator lifecycle bugs, and lock contention Backend - Stream quality/bandwidth controls: - Add quality (2-31) and scale (0.1-1.0) params to /camera/stream endpoint - Pass quality to ffmpeg -q:v and scale to -vf scale filter - Grid view uses fps=5, quality=15, scale=0.5 (~20-30x bandwidth reduction) - Single camera keeps fps=15, quality=5, scale=1.0 (full quality) Backend - Multiplexed grid-stream endpoint: - Add binary-framed multiplexed endpoint to avoid browser 6-connection limit - Optimize with byte array buffers, time.monotonic(), and DRY producer helper Frontend - CameraGridCard canvas renderer: - Replace img element with fetch() + AbortController + canvas drawing - AbortController.abort() reliably kills TCP connections on unmount (img.src='' does NOT close MJPEG connections, saturating browser 6-conn limit) - Parse JPEG frames from stream via SOI/EOI markers, draw with createImageBitmap - Canvas drawing creates zero network requests (no blob URL spam) - Add loading spinner and error state indicators Frontend - Enhanced camera grid cards: - Add printer state overlay with progress details (time remaining, layers) - Show progress bar and time remaining when paused, yellow bar on pause state - Add camera quality selector (Low/Med/High) Frontend - EmbeddedCameraViewer: - Add explicit quality=5&scale=1.0 params for full-quality single camera view Frontend - Misc cleanup: - Add progress sort option (first printer to finish the job is show first - last to finish the job is last) ## Related Issue Fixes #451 ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [x] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Code refactoring - [ ] Performance improvement - [ ] Test addition or update ## Screenshots Frontend: <img width="1659" height="949" alt="Screenshot 2026-02-19 at 11 24 07 PM" src="https://github.com/user-attachments/assets/de362877-013f-4b70-bb86-c9084f089b63" /> <img width="407" height="235" alt="image" src="https://github.com/user-attachments/assets/50d4f90c-8c76-4735-b43d-c4efaf8d2bf8" /> Backend optimisation - CPU usage for camera stream: Before: <img width="1264" height="73" alt="image" src="https://github.com/user-attachments/assets/a4fcf6bf-5207-4057-9c6c-876370540a97" /> After: <img width="611" height="35" alt="image" src="https://github.com/user-attachments/assets/bf5ba97e-8653-4ce5-9924-c84bbc84b12d" /> ## Testing - [x] I have tested this on my local machine - [x] I have tested with my printer model: X1C, H2S, H2D ## Checklist - [x] My code follows the project's coding style - [x] I have commented my code where necessary - [x] I have updated the documentation (if needed) - [x] My changes generate no new warnings - [x] I have tested my changes thoroughly --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 12:34:41 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/bambuddy#1046
No description provided.