Skip to content

Mobile fixes - #53

Open
dk307 wants to merge 3 commits into
mainfrom
mobile_fix2
Open

Mobile fixes#53
dk307 wants to merge 3 commits into
mainfrom
mobile_fix2

Conversation

@dk307

@dk307 dk307 commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Live camera views now adapt automatically to screen size, from single-column mobile layouts to four-column desktop layouts.
    • Stopped streaming services can start automatically when a live view is opened.
    • Camera pages use available screen space more effectively, with improved scrolling and panel layouts.
  • Bug Fixes

    • WebSocket connections now reconnect after unexpected or service-related closures while preserving clean disconnect behavior.
    • Failed stream startup now provides a controlled connection closure.
  • Documentation

    • Deployment instructions now cover prerequisites, commands, testing, synchronization, container rebuilding, and health checks.

dk307 added 2 commits August 9, 2026 21:45
- Live view fills remaining viewport height (object-contain, no crop) on
  desktop and mobile instead of a fixed 16:9 box.
- Move full-height flex off TabsContent so Radix's hidden attr works;
  inactive tab panels no longer leave empty space at the top of other tabs.
- deploy.sh resolves the venv python (fallback python3, PY override).
Live page: reactive viewport width with adaptive columns (1/2/3/4), manual layouts honored at any width, aspect-video tiles and gridAutoRows auto so overflowing cameras scroll instead of clipping. Live websocket: accept first then cold-boot idle-stopped go2rtc via asyncio.to_thread instead of surfacing an HTTP 403; surface non-1000 closes to the client retry path.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request improves go2rtc WebSocket startup and failure handling, updates client reconnect behavior, adds responsive live-view layouts, adjusts camera detail sizing, selects the deployment Python interpreter, and expands deployment and integration test coverage.

Changes

Live camera connectivity

Layer / File(s) Summary
go2rtc startup and failure flow
app/api/cameras.py, app/services/go2rtc.py, tests/integration/test_cameras_api.py, tests/unit/test_go2rtc.py
live_ws accepts connections before checking go2rtc, starts unavailable instances asynchronously, and closes failed connections with code 1013. go2rtc.start() prevents duplicate concurrent spawns and waits for readiness. Tests cover startup, failure, recovery, and process-death behavior.
WebSocket close-code handling
frontend/src/components/VideoStream.tsx, frontend/src/components/VideoStream.test.tsx
VideoStream reconnects for non-1000 close codes, including clean 1013 closures. Tests cover clean close events and retry behavior.

Responsive live layout

Layer / File(s) Summary
Responsive grid behavior
frontend/src/pages/Live.tsx, frontend/src/pages/Live.test.tsx
Automatic column counts now use viewport breakpoints. Fixed layouts remain active on mobile. Grid rows use content sizing, and tests cover viewport changes and layout modes.
Camera detail height management
frontend/src/pages/CameraDetail.tsx
Camera detail panels now use full-height flex layouts with independent overflow behavior for live, timeline, and details content.

Deployment interpreter selection

Layer / File(s) Summary
Deployment test interpreter
scripts/deploy.sh
Deployment tests use the configured interpreter, the project virtual environment, or python3. The script validates Python 3.14 or later.
Deployment documentation
README.md
Deployment prerequisites and deploy.sh actions are documented.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant live_ws
  participant go2rtc
  participant VideoStream
  Client->>live_ws: Open WebSocket
  live_ws->>go2rtc: Check availability
  alt go2rtc unavailable
    live_ws->>go2rtc: Start instance asynchronously
    go2rtc-->>live_ws: Readiness result
  end
  alt go2rtc available
    live_ws-->>Client: Proxy stream
  else startup or availability failure
    live_ws-->>Client: Close with code 1013
    Client->>VideoStream: Process close event
    VideoStream->>VideoStream: Schedule reconnect
  end
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the responsive mobile layout fixes, although it does not cover the WebSocket, deployment, and go2rtc changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mobile_fix2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
__init__.py00100% 
build_info.py50100% 
config.py240100% 
database.py23196%39
main.py821384%49–50, 58, 133–135, 137–142, 144
api
   __init__.py00100% 
   activity.py240100% 
   app_settings.py270100% 
   cameras.py3781696%234, 236, 238, 242, 524–527, 535–538, 542, 600, 630–631
   health.py34391%17–19
   locations.py380100% 
   logs.py280100% 
   recordings.py142199%88
   scanner.py280100% 
   storage.py60100% 
   system_info.py137199%33
   timeline.py290100% 
models
   __init__.py00100% 
   app_settings.py130100% 
   base.py80100% 
   camera.py320100% 
   download_event.py140100% 
   location.py90100% 
   purge_event.py140100% 
   recording.py38197%56
   scan_event.py140100% 
schemas
   __init__.py00100% 
   app_settings.py60100% 
   camera.py990100% 
   location.py110100% 
   recording.py80100% 
services
   __init__.py00100% 
   downloader.py156299%324–325
   go2rtc.py286299%203, 238
   hikvision.py175299%154–155
   log_buffer.py63297%77–78
   purger.py1400100% 
   reconcile.py170100% 
   scanner.py273399%347, 373, 435
   storage.py220100% 
   tz.py540100% 
workers
   __init__.py00100% 
   scheduler.py1230100% 
TOTAL25804798% 

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.44262% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.88%. Comparing base (7e13e44) to head (36e9324).

Files with missing lines Patch % Lines
app/services/go2rtc.py 91.30% 2 Missing ⚠️
frontend/src/pages/CameraDetail.tsx 93.33% 1 Missing ⚠️
frontend/src/pages/Live.tsx 93.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #53      +/-   ##
==========================================
- Coverage   92.89%   92.88%   -0.02%     
==========================================
  Files          81       81              
  Lines        7377     7394      +17     
  Branches      751      753       +2     
==========================================
+ Hits         6853     6868      +15     
- Misses        490      492       +2     
  Partials       34       34              
Flag Coverage Δ
backend 98.17% <93.33%> (-0.11%) ⬇️
frontend 90.04% <93.54%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
frontend/src/pages/Live.test.tsx (1)

159-173: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test the resize listener without remounting.

Lines 167-173 validate a new initial viewport after remount. They do not validate the resize listener added in Live.tsx. Change the viewport while the first wall remains mounted. Then dispatch resize and assert that the existing grid updates.

Proposed test update
-import { screen, waitFor } from "`@testing-library/react`";
+import { fireEvent, screen, waitFor } from "`@testing-library/react`";

-    const { container, unmount } = renderLive();
+    const { container } = renderLive();
     await waitFor(() => expect(screen.getAllByTestId("stream")).toHaveLength(3));
     const grid = container.querySelector<HTMLElement>("[style*='grid-template-columns']")!;
     expect(grid.style.gridTemplateColumns).toBe("repeat(1, minmax(0, 1fr))");

-    unmount();
     setWidth(1100); // tablet
-    const { container: container2 } = renderLive();
-    await waitFor(() => expect(screen.getAllByTestId("stream")).toHaveLength(3));
-    const grid2 = container2.querySelector<HTMLElement>("[style*='grid-template-columns']")!;
-    expect(grid2.style.gridTemplateColumns).toBe("repeat(3, minmax(0, 1fr))");
+    fireEvent(window, new Event("resize"));
+    await waitFor(() =>
+      expect(grid.style.gridTemplateColumns).toBe("repeat(3, minmax(0, 1fr))"),
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/Live.test.tsx` around lines 159 - 173, Update the
auto-layout test in the existing viewport test case to keep the first render
mounted, change its width from phone to tablet, dispatch a resize event, and
assert that the same grid updates to three columns. Remove the unmount and
second render flow so the test exercises Live’s resize listener rather than only
initial layout calculation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/api/cameras.py`:
- Around line 570-576: Update the go2rtc startup path, including go2rtc.start
and its subprocess-spawn logic, to re-check _proc while holding _lock
immediately before calling subprocess.Popen(). Return or reuse the existing
process when another caller has already spawned it, ensuring concurrent starts
cannot create duplicate processes.

In `@README.md`:
- Around line 81-83: Update the README deployment description to replace the
incorrect `podman-compose up --build` reference with the actual
`scripts/deploy.sh` behavior: build the image with `podman build`, stop and
remove the existing container, then start it with `podman run`. Preserve the
surrounding local test, rsync, and health-verification details.
- Line 75: Update the deployment prerequisites in README.md to document the
DEPLOY_PASS authentication path, including that scripts/deploy.sh uses sshpass
for SSH and rsync and requires sshpass to be installed. Preserve the existing
SSH-key instructions; only remove the DEPLOY_PASS code path if the alternative
is intentionally not supported.

In `@scripts/deploy.sh`:
- Around line 13-14: Update the deployment script’s PY validation before the
pytest invocation to execute "$PY", verify the interpreter version is Python
3.14 or newer, and exit with a clear error for older or invalid interpreters.
Add the Python >=3.14 requirement to the deployment section of README.md.

In `@tests/integration/test_cameras_api.py`:
- Around line 941-960: The websocket test around client.websocket_connect must
capture the WebSocketDisconnect raised by ws.receive() and assert its code is
1013, rather than suppressing or broadly catching it. Preserve the existing
go2rtc availability/start patches and ensure the assertion validates the
server-side close contract.

---

Nitpick comments:
In `@frontend/src/pages/Live.test.tsx`:
- Around line 159-173: Update the auto-layout test in the existing viewport test
case to keep the first render mounted, change its width from phone to tablet,
dispatch a resize event, and assert that the same grid updates to three columns.
Remove the unmount and second render flow so the test exercises Live’s resize
listener rather than only initial layout calculation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cf427b74-ce07-4f9b-959c-f96be52ba49a

📥 Commits

Reviewing files that changed from the base of the PR and between 7e13e44 and 0c8900f.

📒 Files selected for processing (9)
  • README.md
  • app/api/cameras.py
  • frontend/src/components/VideoStream.test.tsx
  • frontend/src/components/VideoStream.tsx
  • frontend/src/pages/CameraDetail.tsx
  • frontend/src/pages/Live.test.tsx
  • frontend/src/pages/Live.tsx
  • scripts/deploy.sh
  • tests/integration/test_cameras_api.py

Comment thread app/api/cameras.py
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread scripts/deploy.sh
Comment thread tests/integration/test_cameras_api.py Outdated
go2rtc.start(): re-check the process handle under the lock before spawning so concurrent cold boots share one process instead of each spawning a duplicate. Assert the 1013 close contract in the live_ws integration tests. deploy.sh: gate on Python >=3.14 with a clear error. README: correct the podman-compose reference to podman build/stop/rm/run and document the sshpass/DEPLOY_PASS path. Live.test: exercise the resize listener instead of re-mounting.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/services/go2rtc.py`:
- Around line 227-232: Update the failed-start cleanup around
_clear_if_current() so readiness state is cleared only when the shared _proc
still references the failed spawned process. Preserve the replacement process
and its _api_ready state when another caller has installed it, while continuing
to terminate and reap the failed process as needed.

In `@tests/integration/test_cameras_api.py`:
- Around line 949-950: Update both tests in
tests/integration/test_cameras_api.py at lines 949-950 and 1012-1015: bind the
go2rtc.start patch to start_mock while retaining each test’s existing return or
raising behavior, then assert start_mock.assert_called_once_with() after
exercising live_ws. Ensure both cold-start paths verify exactly one start call.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 828f7afa-ea2c-4b96-83ef-b8853c081b16

📥 Commits

Reviewing files that changed from the base of the PR and between 0c8900f and 36e9324.

📒 Files selected for processing (6)
  • README.md
  • app/services/go2rtc.py
  • frontend/src/pages/Live.test.tsx
  • scripts/deploy.sh
  • tests/integration/test_cameras_api.py
  • tests/unit/test_go2rtc.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • frontend/src/pages/Live.test.tsx

Comment thread app/services/go2rtc.py
Comment on lines +227 to +232
# The API never came up (or the process died while starting). Terminate a
# still-alive process so it releases its ports, then drop the handle so a
# later start() can retry instead of waiting on a wedged process.
if spawned.poll() is None:
_terminate_and_reap(spawned)
_clear_if_current(spawned)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Keep readiness state owned by the current process.

A failed starter can reach Line 232 after another caller has installed and marked a replacement process ready. _clear_if_current() then clears _api_ready even when _proc is not spawned. Later callers wait until timeout although the replacement process is available.

Clear _api_ready only when _proc is proc.

Proposed fix
 def _clear_if_current(proc: subprocess.Popen) -> None:
     """Drop the process handle and readiness if *proc* is still the current one."""
     global _proc
     with _lock:
         if _proc is proc:
             _proc = None
-        _api_ready.clear()
+            _api_ready.clear()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/services/go2rtc.py` around lines 227 - 232, Update the failed-start
cleanup around _clear_if_current() so readiness state is cleared only when the
shared _proc still references the failed spawned process. Preserve the
replacement process and its _api_ready state when another caller has installed
it, while continuing to terminate and reap the failed process as needed.

Comment on lines +949 to +950
patch("app.services.go2rtc.is_available", return_value=False),
patch("app.services.go2rtc.start", return_value=False),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert each cold-start call.

Both tests can pass if live_ws skips go2rtc.start() and closes immediately because is_available() remains False. Bind each patch to a mock and assert start_mock.assert_called_once_with().

  • tests/integration/test_cameras_api.py#L949-L950: retain the failed-start mock and assert one cold-start call.
  • tests/integration/test_cameras_api.py#L1012-L1015: retain the raising-start mock and assert one cold-start call.
📍 Affects 1 file
  • tests/integration/test_cameras_api.py#L949-L950 (this comment)
  • tests/integration/test_cameras_api.py#L1012-L1015
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/test_cameras_api.py` around lines 949 - 950, Update both
tests in tests/integration/test_cameras_api.py at lines 949-950 and 1012-1015:
bind the go2rtc.start patch to start_mock while retaining each test’s existing
return or raising behavior, then assert start_mock.assert_called_once_with()
after exercising live_ws. Ensure both cold-start paths verify exactly one start
call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant