fix: restore recording playback and default to 30-day retention
This commit is contained in:
@@ -73,6 +73,7 @@ SCAN = {'running': False, 'items': [], 'error': '', 'network': '', 'at': None}
|
||||
ATTEMPTS = {}
|
||||
API_PORT, HLS_PORT, PLAYBACK_PORT = 19997, 18888, 19996
|
||||
MEDIA_AUTH = None
|
||||
DEFAULT_STORAGE = {'retentionDays': 30, 'maxGB': 40, 'reserveGB': 8}
|
||||
|
||||
|
||||
class Problem(Exception):
|
||||
@@ -329,7 +330,7 @@ def source(c, quality):
|
||||
def write_media_config():
|
||||
with LOCK:
|
||||
paths = {}
|
||||
policy = setting('storage', {'retentionDays': 7, 'maxGB': 40, 'reserveGB': 8})
|
||||
policy = setting('storage', DEFAULT_STORAGE)
|
||||
for c in objects('cameras'):
|
||||
for quality in ('main', 'sub'):
|
||||
paths[stream_name(c, quality)] = {
|
||||
@@ -381,16 +382,24 @@ def media_json(path, playback=False):
|
||||
return json.load(r)
|
||||
except urllib.error.HTTPError as exc:
|
||||
with exc:
|
||||
if playback and path.startswith('/list?') and exc.code == 404:
|
||||
if playback and path.startswith('/list?') and exc.code in (400, 404):
|
||||
try:
|
||||
detail = json.loads(exc.read(4096))
|
||||
if detail.get('error') == 'no recording segments found':
|
||||
message = detail.get('error')
|
||||
if exc.code == 404 and message == 'no recording segments found':
|
||||
return []
|
||||
# MediaMTX 1.21 returns 400 before this camera's first recording.
|
||||
# Match only the requested camera directory, never permissions,
|
||||
# another path, a corrupt file, or an unavailable service.
|
||||
name = url.parse_qs(url.urlsplit(path).query).get('path', [''])[0]
|
||||
if (exc.code == 400 and re.fullmatch(r'cam_[a-f0-9]{16}_main', name)
|
||||
and message == 'lstat ' + str(RECORDINGS / name) + ': no such file or directory'):
|
||||
return []
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
raise Problem('视频服务暂不可用,或该时段尚无可回放录像', 503)
|
||||
raise Problem('录像服务暂不可用,请稍后重试或联系管理员检查服务', 503)
|
||||
except Exception:
|
||||
raise Problem('视频服务暂不可用,或该时段尚无可回放录像', 503)
|
||||
raise Problem('录像服务暂不可用,请稍后重试或联系管理员检查服务', 503)
|
||||
|
||||
|
||||
def media_config_watch():
|
||||
@@ -417,6 +426,22 @@ def media_config_watch():
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
def recording_state(camera, ready, media_online):
|
||||
if not camera.get('record'):
|
||||
return 'off'
|
||||
if not camera.get('enabled'):
|
||||
return 'disabled'
|
||||
if STORAGE['paused']:
|
||||
return 'paused'
|
||||
if not media_online:
|
||||
return 'unavailable'
|
||||
if not ready:
|
||||
return 'offline'
|
||||
if time.time() - RECENT_RECORDS.get(stream_name(camera), 0) < 45:
|
||||
return 'recording'
|
||||
return 'waiting'
|
||||
|
||||
|
||||
def status():
|
||||
try:
|
||||
data = media_json('/v3/paths/list?itemsPerPage=200')
|
||||
@@ -432,10 +457,10 @@ def status():
|
||||
if endpoint:
|
||||
c = dict(c, host=endpoint['host'], port=endpoint['port'])
|
||||
main = ready.get(stream_name(c), {})
|
||||
recording = recording_state(c, main.get('ready', False), media)
|
||||
cameras.append({**public_camera(c), 'ready': main.get('ready', False), 'tracks': main.get('tracks', []),
|
||||
'recordingRequested': c['enabled'] and c['record'] and not STORAGE['paused'],
|
||||
'recordingActive': bool(c['enabled'] and c['record'] and not STORAGE['paused'] and main.get('ready', False)
|
||||
and time.time() - RECENT_RECORDS.get(stream_name(c), 0) < 45)})
|
||||
'recordingState': recording, 'recordingActive': recording == 'recording'})
|
||||
with LOCK:
|
||||
storage = dict(STORAGE)
|
||||
scan = dict(SCAN)
|
||||
@@ -867,6 +892,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.connection.settimeout(15)
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'video/mp4')
|
||||
self.send_header('X-Accel-Buffering', 'no')
|
||||
self.send_header('Cache-Control', 'no-store')
|
||||
self.send_header('Accept-Ranges', 'none')
|
||||
self.send_header('X-Content-Type-Options', 'nosniff')
|
||||
@@ -1107,7 +1133,7 @@ def initialize():
|
||||
HERITAGE.initialize()
|
||||
JOURNAL.initialize()
|
||||
if not setting('storage'):
|
||||
set_setting('storage', {'retentionDays': 7, 'maxGB': 40, 'reserveGB': 8})
|
||||
set_setting('storage', DEFAULT_STORAGE)
|
||||
if not setting('mediaSecret'):
|
||||
set_setting('mediaSecret', secrets.token_hex(32))
|
||||
MEDIA_AUTH = 'Basic ' + base64.b64encode(('vision:' + setting('mediaSecret')).encode()).decode()
|
||||
|
||||
Reference in New Issue
Block a user