Fix browser recording playback and web management consistency

This commit is contained in:
Codex
2026-10-03 17:10:06 +08:00
parent d7058e8af7
commit 969dcabe99
9 changed files with 265 additions and 18 deletions
+42 -4
View File
@@ -26,6 +26,7 @@ import urllib.parse as url
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import onvif
import playback
ROOT = Path(__file__).resolve().parent
os.umask(0o077)
@@ -79,7 +80,8 @@ def set_setting(key, value):
def objects(table):
with LOCK:
return [dict(json.loads(r['body']), id=r['id']) for r in DB.execute('SELECT * FROM ' + table)]
rows = [dict(json.loads(r['body']), id=r['id']) for r in DB.execute('SELECT * FROM ' + table)]
return sorted(rows, key=lambda row: (row.get('name', ''), row['id']))
def get_object(table, key):
@@ -594,7 +596,7 @@ class Handler(BaseHTTPRequestHandler):
except Exception:
self.answer({'error': '操作未完成,请检查输入或服务状态'}, 500)
def proxy(self, port, path):
def proxy(self, port, path, download=False):
conn = http.client.HTTPConnection('127.0.0.1', port, timeout=55)
headers = {'Authorization': MEDIA_AUTH}
if self.headers.get('Range'):
@@ -615,6 +617,8 @@ class Handler(BaseHTTPRequestHandler):
raise Problem('视频服务返回了无效的播放跳转', 502)
location = '/media/live' + target.path + ('?' + target.query if target.query else '')
self.send_response(r.status)
if download:
self.send_header('Content-Disposition', 'attachment; filename="recording.mp4"')
if location:
self.send_header('Location', location)
for k in ('Content-Type', 'Content-Length', 'Content-Range', 'Accept-Ranges'):
@@ -635,6 +639,36 @@ class Handler(BaseHTTPRequestHandler):
finally:
conn.close()
def compatible_playback(self, path, duration):
started = False
old_timeout = self.connection.gettimeout()
try:
with playback.stream('http://127.0.0.1:' + str(PLAYBACK_PORT) + path, MEDIA_AUTH, duration) as output:
first = output.read1(65536)
if not first:
raise Problem('录像转换失败,请检查录像是否完整及服务器视频驱动', 503)
self.connection.settimeout(15)
self.send_response(200)
self.send_header('Content-Type', 'video/mp4')
self.send_header('Cache-Control', 'no-store')
self.send_header('Accept-Ranges', 'none')
self.send_header('X-Content-Type-Options', 'nosniff')
self.send_header('Connection', 'close')
self.end_headers()
started = True
self.close_connection = True
self.wfile.write(first)
while chunk := output.read1(65536):
self.wfile.write(chunk)
except playback.PlaybackError as exc:
if not started:
raise Problem(str(exc), 503)
except OSError:
if not started:
raise Problem('录像转换连接失败', 503)
finally:
self.connection.settimeout(old_timeout)
def do_GET(self):
try:
parsed = url.urlsplit(self.path)
@@ -681,9 +715,13 @@ class Handler(BaseHTTPRequestHandler):
raise ValueError()
except ValueError:
raise Problem('单次回放最多一小时')
download = query.get('download') == '1'
q = url.urlencode({'path': stream_name(c), 'start': start.isoformat(), 'duration': duration,
'format': 'mp4' if query.get('download') == '1' else 'fmp4'})
self.proxy(PLAYBACK_PORT, '/get?' + q)
'format': 'mp4' if download else 'fmp4'})
if download:
self.proxy(PLAYBACK_PORT, '/get?' + q, download=True)
else:
self.compatible_playback('/get?' + q, duration)
else:
raise Problem('接口不存在', 404)
else: