|
@@ -990,6 +990,108 @@ def get_domain_by_region(region):
|
|
|
def get_uuid():
|
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
+class GetSnapshotView(View):
|
|
|
+ def post(self, request):
|
|
|
+ logger = logging.getLogger('django')
|
|
|
+ try:
|
|
|
+ data = json.loads(request.body)
|
|
|
+ endpoint_id = data['endpoint_id']
|
|
|
+ access_token = data['access_token']
|
|
|
+ correlation_token = data.get('correlation_token', '')
|
|
|
+ region = data.get('region', 'US')
|
|
|
+ if not endpoint_id or not access_token:
|
|
|
+ raise ValueError("endpoint_id 或 access_token 为空")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"参数解析/校验失败: {e}")
|
|
|
+ return JsonResponse({'error': 'BAD_REQUEST'}, status=400)
|
|
|
+
|
|
|
+ try:
|
|
|
+ user = UserModel.objects.get(access_token=access_token)
|
|
|
+ except UserModel.DoesNotExist:
|
|
|
+ return JsonResponse({'error': 'INVALID_TOKEN'}, status=401)
|
|
|
+
|
|
|
+ # 验证设备归属
|
|
|
+ if not user.uid_rtsp.filter(uid=endpoint_id).exists():
|
|
|
+ return JsonResponse({'error': 'NO_SUCH_DEVICE'}, status=403)
|
|
|
+
|
|
|
+ # 获取设备信息
|
|
|
+ device = user.uid_rtsp.get(uid=endpoint_id)
|
|
|
+ password = device.password or ''
|
|
|
+ region = getattr(device, 'region', region)
|
|
|
+ rtsp_path = device.rtsp_url
|
|
|
+
|
|
|
+ rtsp_domain = get_domain_by_region(region).split('//')[-1]
|
|
|
+ rtsp_url = f'rtsp://{rtsp_domain}:8554/{rtsp_path}'
|
|
|
+
|
|
|
+ if '_' in endpoint_id:
|
|
|
+ uid_base, channel = endpoint_id.rsplit('_', 1)
|
|
|
+ else:
|
|
|
+ uid_base = endpoint_id
|
|
|
+ channel = '0'
|
|
|
+
|
|
|
+ logger.info(f"[GetSnapshot] uid={uid_base}, channel={channel}, region={region}")
|
|
|
+
|
|
|
+ domain_name = get_domain_by_region(region)
|
|
|
+
|
|
|
+ # 启动线程异步下发快照指令
|
|
|
+ threading.Thread(
|
|
|
+ target=self.send_snapshot_command,
|
|
|
+ args=(domain_name, uid_base, password, rtsp_url, channel, logger),
|
|
|
+ daemon=True
|
|
|
+ ).start()
|
|
|
+ logger.info(f"[GetSnapshot] 已启动快照指令线程 → domain={domain_name}, UID={uid_base}")
|
|
|
+
|
|
|
+ # 返回 DeferredResponse
|
|
|
+ return JsonResponse({
|
|
|
+ "event": {
|
|
|
+ "header": {
|
|
|
+ "namespace": "Alexa",
|
|
|
+ "name": "DeferredResponse",
|
|
|
+ "messageId": get_uuid(),
|
|
|
+ "correlationToken": correlation_token,
|
|
|
+ "payloadVersion": "3"
|
|
|
+ },
|
|
|
+ "endpoint": {
|
|
|
+ "endpointId": endpoint_id
|
|
|
+ },
|
|
|
+ "payload": {
|
|
|
+ "estimatedDeferralInSeconds": 7
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ def send_snapshot_command(self, domain_name, uid, password, rtsp_url, channel, logger):
|
|
|
+
|
|
|
+ url = f'{domain_name}/iot/requestPublishMessage'
|
|
|
+ payload = {
|
|
|
+ 'UID': uid,
|
|
|
+ 'rtsp': rtsp_url,
|
|
|
+ 'enable': '1',
|
|
|
+ 'channel': channel
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ resp = requests.post(url, data=payload, timeout=10)
|
|
|
+ if resp.status_code != 200:
|
|
|
+ logger.error(f"[send_snapshot] {url} 返回 HTTP {resp.status_code}")
|
|
|
+ return
|
|
|
+ data = resp.json()
|
|
|
+ logger.info(f"[send_snapshot] 响应({url}): {data}")
|
|
|
+ if data.get('result_code') != 0:
|
|
|
+ logger.error(f"[send_snapshot] 下发失败, result_code={data.get('result_code')}")
|
|
|
+ except Exception as ex:
|
|
|
+ logger.error(f"[send_snapshot] 请求异常: {ex}")
|
|
|
+
|
|
|
+def get_domain_by_region(region):
|
|
|
+ if region == 'EU':
|
|
|
+ return SERVER_PREFIX_EU
|
|
|
+ if region == 'CN':
|
|
|
+ return SERVER_PREFIX_TEST
|
|
|
+ return SERVER_PREFIX
|
|
|
+
|
|
|
+def get_uuid():
|
|
|
+ return str(uuid.uuid4())
|
|
|
+
|
|
|
|
|
|
def send_snapshot_command(domain_name, uid, password, rtsp_url, channel, logger):
|
|
|
|