Repository: blakeblackshear/frigate

Issue:
Recordings UI fails when no recordings exist
The current recordings UI doesn't gracefully handle when there are no recordings. It seems to get stuck loading recordings in an infinite loop.

![image](https://user-images.githubusercontent.com/569905/120902095-06bab400-c604-11eb-8bfb-99b45c50a2f8.png)

@hunterjm 


Discussion:

Resolving patch:
diff --git a/frigate/http.py b/frigate/http.py
--- a/frigate/http.py
+++ b/frigate/http.py
@@ -456,7 +456,7 @@ def recordings(camera_name):
     files = glob.glob(f"{RECORD_DIR}/*/*/*/{camera_name}")
 
     if len(files) == 0:
-        return "No recordings found.", 404
+        return jsonify([])
 
     files.sort()
 


Test patch:
diff --git a/web/src/__tests__/Sidebar.test.jsx b/web/src/__tests__/Sidebar.test.jsx
--- a/web/src/__tests__/Sidebar.test.jsx
+++ b/web/src/__tests__/Sidebar.test.jsx
@@ -9,8 +9,8 @@ describe('Sidebar', () => {
     jest.spyOn(Api, 'useConfig').mockImplementation(() => ({
       data: {
         cameras: {
-          front: { name: 'front', objects: { track: ['taco', 'cat', 'dog'] } },
-          side: { name: 'side', objects: { track: ['taco', 'cat', 'dog'] } },
+          front: { name: 'front', objects: { track: ['taco', 'cat', 'dog'] }, record: { enabled: true } },
+          side: { name: 'side', objects: { track: ['taco', 'cat', 'dog'] }, record: { enabled: false } },
         },
       },
     }));
@@ -30,4 +30,11 @@ describe('Sidebar', () => {
     expect(screen.queryByRole('link', { name: 'front' })).toBeInTheDocument();
     expect(screen.queryByRole('link', { name: 'side' })).toBeInTheDocument();
   });
+
+  test('render cameras if in record route', async () => {
+    window.history.replaceState({}, 'Front Recordings', '/recording/front');
+    render(<Sidebar />);
+    expect(screen.queryByRole('link', { name: 'front' })).toBeInTheDocument();
+    expect(screen.queryByRole('link', { name: 'side' })).not.toBeInTheDocument();
+  });
 });
diff --git a/web/src/routes/__tests__/Cameras.test.jsx b/web/src/routes/__tests__/Cameras.test.jsx
--- a/web/src/routes/__tests__/Cameras.test.jsx
+++ b/web/src/routes/__tests__/Cameras.test.jsx
@@ -12,8 +12,8 @@ describe('Cameras Route', () => {
     useConfigMock = jest.spyOn(Api, 'useConfig').mockImplementation(() => ({
       data: {
         cameras: {
-          front: { name: 'front', objects: { track: ['taco', 'cat', 'dog'] } },
-          side: { name: 'side', objects: { track: ['taco', 'cat', 'dog'] } },
+          front: { name: 'front', objects: { track: ['taco', 'cat', 'dog'] }, record: { enabled: true } },
+          side: { name: 'side', objects: { track: ['taco', 'cat', 'dog'] }, record: { enabled: false } },
         },
       },
       status: 'loaded',
@@ -41,6 +41,14 @@ describe('Cameras Route', () => {
     expect(screen.queryByText('side').closest('a')).toHaveAttribute('href', '/cameras/side');
   });
 
+  test('shows recordings link', async () => {
+    render(<Cameras />);
+
+    expect(screen.queryByLabelText('Loading…')).not.toBeInTheDocument();
+
+    expect(screen.queryAllByText('Recordings')).toHaveLength(1);
+  });
+
   test('buttons toggle detect, clips, and snapshots', async () => {
     const sendDetect = jest.fn();
     const sendClips = jest.fn();