Add BetterCam engine, UI polish, and bug fixes

- Add BetterCam capture engine (DXGI Desktop Duplication, priority 4)
- Fix missing picture_stream_id in get_device endpoint
- Fix template delete validation to check streams instead of devices
- Add description field to capture engine template UI
- Default template name changed to "Default" with descriptive text
- Display picker highlights selected display instead of primary
- Fix modals closing when dragging text selection outside dialog
- Rename "Engine Configuration" to "Configuration", hide when empty
- Rename "Run Test" to "Run" across all test buttons
- Always reserve space for vertical scrollbar
- Redesign Stream Settings info panel with pill-style props
- Fix processed stream showing internal ID instead of stream name
- Update en/ru locale files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 23:28:35 +03:00
parent 9ae93497a6
commit ebec1bd16e
13 changed files with 417 additions and 100 deletions

View File

@@ -389,6 +389,7 @@ async def get_device(
),
calibration=CalibrationSchema(**calibration_to_dict(device.calibration)),
capture_template_id=device.capture_template_id,
picture_stream_id=device.picture_stream_id,
created_at=device.created_at,
updated_at=device.updated_at,
)
@@ -918,25 +919,25 @@ async def delete_template(
template_id: str,
_auth: AuthRequired,
template_store: TemplateStore = Depends(get_template_store),
device_store: DeviceStore = Depends(get_device_store),
stream_store: PictureStreamStore = Depends(get_picture_stream_store),
):
"""Delete a template.
Validates that no devices are currently using this template before deletion.
Validates that no streams are currently using this template before deletion.
"""
try:
# Check if any devices are using this template
devices_using_template = []
for device in device_store.get_all_devices():
if device.capture_template_id == template_id:
devices_using_template.append(device.name)
# Check if any streams are using this template
streams_using_template = []
for stream in stream_store.get_all_streams():
if stream.capture_template_id == template_id:
streams_using_template.append(stream.name)
if devices_using_template:
device_list = ", ".join(devices_using_template)
if streams_using_template:
stream_list = ", ".join(streams_using_template)
raise HTTPException(
status_code=409,
detail=f"Cannot delete template: it is currently assigned to the following device(s): {device_list}. "
f"Please reassign these devices to a different template before deleting."
detail=f"Cannot delete template: it is used by the following stream(s): {stream_list}. "
f"Please reassign these streams to a different template before deleting."
)
# Proceed with deletion
@@ -1036,6 +1037,10 @@ async def test_template(
screen_capture = engine.capture_display(test_request.display_index)
capture_elapsed = time.perf_counter() - capture_start
# Skip if no new frame (screen unchanged)
if screen_capture is None:
continue
total_capture_time += capture_elapsed
frame_count += 1
last_frame = screen_capture
@@ -1354,6 +1359,9 @@ async def test_pp_template(
screen_capture = engine.capture_display(display_index)
capture_elapsed = time.perf_counter() - capture_start
if screen_capture is None:
continue
total_capture_time += capture_elapsed
frame_count += 1
last_frame = screen_capture
@@ -1739,6 +1747,9 @@ async def test_picture_stream(
screen_capture = engine.capture_display(display_index)
capture_elapsed = time.perf_counter() - capture_start
if screen_capture is None:
continue
total_capture_time += capture_elapsed
frame_count += 1
last_frame = screen_capture