fix: restore recording playback and default to 30-day retention
This commit is contained in:
+59
@@ -257,6 +257,7 @@ class WebTests(unittest.TestCase):
|
||||
self.assertEqual(command[command.index('-rc_mode') + 1], 'CQP')
|
||||
self.assertIn('h264_vaapi', command)
|
||||
self.assertIn('pipe:1', command)
|
||||
self.assertEqual(command[command.index('-analyzeduration') + 1], '1000000')
|
||||
|
||||
def test_ptz_requires_both_space_and_control_permission(self):
|
||||
app.save_object('sites', dict(id='permitted', name='Permitted'))
|
||||
@@ -378,6 +379,64 @@ class WebTests(unittest.TestCase):
|
||||
app.media_json('/list?path=example', playback=True)
|
||||
self.assertTrue(error.fp.closed)
|
||||
|
||||
def test_first_recording_missing_directory_is_empty_only_for_requested_camera(self):
|
||||
name = 'cam_' + 'a' * 16 + '_main'
|
||||
path = '/list?path=' + name
|
||||
expected = 'lstat ' + str(app.RECORDINGS / name) + ': no such file or directory'
|
||||
for message, empty in [(expected, True), (expected.replace(name, 'other'), False),
|
||||
(expected.replace('no such file or directory', 'permission denied'), False),
|
||||
('invalid start timestamp', False)]:
|
||||
error = app.urllib.error.HTTPError('http://localhost/list', 400, 'Bad Request', {},
|
||||
io.BytesIO(json.dumps({'error': message}).encode()))
|
||||
with self.subTest(message=message), patch('app.urllib.request.urlopen', side_effect=error):
|
||||
if empty:
|
||||
self.assertEqual(app.media_json(path, playback=True), [])
|
||||
else:
|
||||
with self.assertRaises(app.Problem):
|
||||
app.media_json(path, playback=True)
|
||||
self.assertTrue(error.fp.closed)
|
||||
|
||||
def test_playback_disables_reverse_proxy_buffering(self):
|
||||
with patch.object(playback, 'stream', return_value=io.BytesIO(b'test-mp4')):
|
||||
code, headers, body = self.request('/media/playback?camera='+'a'*16+'&start=2026-01-01T00:00:00Z&duration=10')
|
||||
self.assertEqual(code, 200)
|
||||
self.assertEqual(headers['X-Accel-Buffering'], 'no')
|
||||
self.assertEqual(headers['Cache-Control'], 'no-store')
|
||||
self.assertEqual(body, b'test-mp4')
|
||||
|
||||
def test_default_retention_is_30_days_and_keeps_space_guards(self):
|
||||
self.assertEqual(app.DEFAULT_STORAGE, dict(retentionDays=30, maxGB=40, reserveGB=8))
|
||||
camera = dict(id='e'*16, enabled=True, record=True)
|
||||
def setting(key, default=None):
|
||||
return 'test-secret' if key == 'mediaSecret' else default
|
||||
with tempfile.TemporaryDirectory() as folder, patch.object(app, 'CONFIG', app.Path(folder)/'media.json'), \
|
||||
patch.object(app, 'objects', return_value=[camera]), patch.object(app, 'setting', side_effect=setting), \
|
||||
patch.object(app, 'source', return_value='rtsp://test/stream'), patch.dict(app.STORAGE, paused=False):
|
||||
app.write_media_config()
|
||||
config=json.loads(app.CONFIG.read_text())
|
||||
main=config['paths'][app.stream_name(camera)]
|
||||
self.assertEqual(main['recordDeleteAfter'], '720h')
|
||||
self.assertTrue(main['record'])
|
||||
self.assertFalse(config['paths'][app.stream_name(camera, 'sub')]['record'])
|
||||
app.STORAGE['paused']=True
|
||||
app.write_media_config()
|
||||
self.assertFalse(json.loads(app.CONFIG.read_text())['paths'][app.stream_name(camera)]['record'])
|
||||
|
||||
def test_recording_status_requires_recent_writes_and_distinguishes_protection(self):
|
||||
camera=dict(id='e'*16, enabled=True, record=True)
|
||||
with patch.dict(app.STORAGE, paused=False), patch.dict(app.RECENT_RECORDS, {}, clear=True):
|
||||
self.assertEqual(app.recording_state(camera, True, True), 'waiting')
|
||||
app.RECENT_RECORDS[app.stream_name(camera)]=time.time()
|
||||
self.assertEqual(app.recording_state(camera, True, True), 'recording')
|
||||
self.assertEqual(app.recording_state(dict(camera,record=False), True, True), 'off')
|
||||
self.assertEqual(app.recording_state(dict(camera,enabled=False), True, True), 'disabled')
|
||||
self.assertEqual(app.recording_state(camera, False, True), 'offline')
|
||||
self.assertEqual(app.recording_state(camera, True, False), 'unavailable')
|
||||
app.RECENT_RECORDS[app.stream_name(camera)]=time.time()-60
|
||||
self.assertEqual(app.recording_state(camera, True, True), 'waiting')
|
||||
app.STORAGE['paused']=True
|
||||
self.assertEqual(app.recording_state(camera, True, True), 'paused')
|
||||
|
||||
def family_save(self, kind, data):
|
||||
return app.FAMILY.save(kind, dict(data, revision=app.setting('familyRevision', 0)), self.admin)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user