feat: provide on-demand H264 compatible browser previews
This commit is contained in:
@@ -14,9 +14,11 @@ import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import shlex
|
||||
import shutil
|
||||
import socket
|
||||
import sqlite3
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib.error
|
||||
@@ -266,6 +268,12 @@ def write_media_config():
|
||||
'record': bool(c['enabled'] and c['record'] and quality == 'main' and not STORAGE['paused']),
|
||||
'recordDeleteAfter': str(policy['retentionDays'] * 24) + 'h',
|
||||
}
|
||||
paths[stream_name(c, 'compat')] = {
|
||||
'source': 'publisher', 'record': False,
|
||||
'runOnDemand': shlex.join([sys.executable, str(ROOT / 'preview.py'), str(DATA)]) if c['enabled'] else '',
|
||||
'runOnDemandRestart': True,
|
||||
'runOnDemandStartTimeout': '45s', 'runOnDemandCloseAfter': '15s',
|
||||
}
|
||||
config = {
|
||||
'logLevel': 'warn', 'logDestinations': ['stdout'],
|
||||
'api': True, 'apiAddress': '127.0.0.1:' + str(API_PORT),
|
||||
@@ -278,7 +286,8 @@ def write_media_config():
|
||||
'authMethod': 'internal',
|
||||
'authInternalUsers': [{'user': 'vision', 'pass': setting('mediaSecret'),
|
||||
'ips': ['127.0.0.1', '::1'],
|
||||
'permissions': [{'action': a} for a in ('read', 'api', 'playback')]}],
|
||||
'permissions': [{'action': a} for a in ('read', 'api', 'playback')]
|
||||
+ [{'action': 'publish', 'path': '~^cam_[a-f0-9]{16}_compat$'}]}],
|
||||
'pathDefaults': {'recordFormat': 'fmp4', 'recordPath': str(RECORDINGS / '%path' / '%Y-%m-%d_%H-%M-%S-%f'),
|
||||
'recordSegmentDuration': '5m', 'recordPartDuration': '1s'},
|
||||
'paths': paths,
|
||||
@@ -585,7 +594,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.answer({'error': '操作未完成,请检查输入或服务状态'}, 500)
|
||||
|
||||
def proxy(self, port, path):
|
||||
conn = http.client.HTTPConnection('127.0.0.1', port, timeout=25)
|
||||
conn = http.client.HTTPConnection('127.0.0.1', port, timeout=55)
|
||||
headers = {'Authorization': MEDIA_AUTH}
|
||||
if self.headers.get('Range'):
|
||||
headers['Range'] = self.headers['Range']
|
||||
@@ -601,7 +610,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
# browser must keep that session query on subsequent playlists.
|
||||
target = url.urlsplit(url.urljoin(path, r.getheader('Location', '')))
|
||||
if (port != HLS_PORT or target.scheme or target.netloc
|
||||
or not re.fullmatch(r'/cam_[a-f0-9]{16}_(main|sub)/[a-zA-Z0-9_.-]+', target.path)):
|
||||
or not re.fullmatch(r'/cam_[a-f0-9]{16}_(main|sub|compat)/[a-zA-Z0-9_.-]+', target.path)):
|
||||
raise Problem('视频服务返回了无效的播放跳转', 502)
|
||||
location = '/media/live' + target.path + ('?' + target.query if target.query else '')
|
||||
self.send_response(r.status)
|
||||
@@ -657,7 +666,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.answer({'items': items})
|
||||
elif path.startswith('/media/live/'):
|
||||
suffix = path[len('/media/live/'):]
|
||||
if not re.fullmatch(r'cam_[a-f0-9]{16}_(main|sub)/[a-zA-Z0-9_.-]+', suffix):
|
||||
if not re.fullmatch(r'cam_[a-f0-9]{16}_(main|sub|compat)/[a-zA-Z0-9_.-]+', suffix):
|
||||
raise Problem('视频路径不正确')
|
||||
self.proxy(HLS_PORT, '/' + suffix + ('?' + parsed.query if parsed.query else ''))
|
||||
elif path == '/media/playback':
|
||||
|
||||
Reference in New Issue
Block a user