video_monitor/app/views/StreamView.py
2026-09-04 18:16:14 +08:00

1232 lines
51 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from app.views.ViewsBase import *
from app.models import *
from django.shortcuts import render, redirect
from app.utils.Utils import buildPageLabels, group_by_field, GB28181CodeUtils
from app.utils.UploadUtils import UploadUtils
from app.utils.LogUtils import LogUtils
from app.utils.Credentials import encrypt_credential
def _hide_stream_credentials(stream):
if not isinstance(stream, dict):
return stream
stream["has_pull_stream_username"] = bool(stream.get("pull_stream_username"))
stream["has_pull_stream_password"] = bool(stream.get("pull_stream_password"))
stream["pull_stream_username"] = ""
stream["pull_stream_password"] = ""
return stream
def online(request):
context = {}
# data = Camera.objects.all().order_by("-sort")
return render(request, 'app/stream/online.html', context)
def index(request):
context = {}
params = f_parseGetParams(request)
stream_code = params.get("code","").strip()
context["search_text"] = stream_code # v4.716新增
return render(request, 'app/stream/index.html', context)
def api_openAddContext(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
request_ip = f_parseRequestIp(request) # v4.725新增) 获取实时请求ip
stream_info = {}
if "GET" == request.method:
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parseGetParams(request)
g_logger.info("StreamView.openAddContext() request_ip:%s,params:%s" % (request_ip,str(params)))
code = GB28181CodeUtils().generate_by_time()
app = g_zlm.default_stream_app
name = code
camera_device_id = "default"
stream_info = {
"code": code,
"app": app,
"name": name,
"camera_device_id": camera_device_id,
"is_audio": 0,
"pull_stream_username": "",
"pull_stream_password": "",
"rtspUrl": g_zlm.get_rtspUrl(app=app,name=name,request_ip=request_ip),
"hlsUrl": g_zlm.get_hlsUrl(app=app,name=name,request_ip=request_ip),
"httpMp4Url": g_zlm.get_httpMp4Url(app=app,name=name,request_ip=request_ip),
"wsMp4Url": g_zlm.get_wsMp4Url(app=app,name=name,request_ip=request_ip),
"record_enable": 0,
}
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"stream": stream_info,
"pull_stream_types": g_pull_stream_types,
"audio_types": get_audio_types(f_parseRequestLang(request))
}
g_logger.info("StreamView.openAddContext() res:%s" % str(res))
return f_responseJson(res)
def api_openAdd(request):
__ret = False
__msg = LANG_VIEWS_T(request, "msg_unknown_error")
if "POST" == request.method:
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
try:
params = f_parsePostParams(request)
lang = f_parseRequestLang(request)
g_logger.info("StreamView.openAdd() params:%s" % str(params))
code = params.get("code", "").strip()
# app = params.get("app", "live").strip()
# name = params.get("name", "").strip()
nickname = params.get("nickname", "").strip()
pull_stream_type = int(params.get("pull_stream_type", 0))
pull_stream_url = params.get("pull_stream_url", "").strip()
pull_stream_ip = params.get("pull_stream_ip", "").strip()
pull_stream_port = int(params.get("pull_stream_port"))
camera_name = params.get("camera_name", "").strip()
camera_manufacturer = params.get("camera_manufacturer", "").strip()
camera_device_id = params.get("camera_device_id", "").strip()
remark = params.get("remark", "").strip()
onvif_username = params.get("onvif_username", "").strip()
onvif_password = params.get("onvif_password", "").strip()
is_audio = int(params.get("is_audio", 0))
record_enable = int(params.get("record_enable", 0))
if code == "":
raise Exception(LANG_VIEWS_T(request, "stream_code_invalid"))
stream = StreamModel.objects.filter(code=code).filter()
if stream:
raise Exception(LANG_VIEWS_T(request, "msg_code_already_exists"))
# if app == "":
# raise Exception(LANG_VIEWS_T(request, "stream_app_invalid"))
# if name == "":
# raise Exception(LANG_VIEWS_T(request, "stream_name_invalid"))
if nickname == "":
raise Exception(LANG_VIEWS_T(request, "stream_name_incorrect"))
else:
nickname_len = len(nickname)
if len(nickname) > 50:
raise Exception(LANG_VIEWS_T(request, "stream_name_max_length") % nickname_len)
if pull_stream_type == 21:
raise Exception(LANG_VIEWS_T(request, "stream_gb28181_hint"))
else:
if pull_stream_type == 1 or pull_stream_type == 2 or pull_stream_type == 3 or pull_stream_type == 4:
pass
else:
raise Exception(LANG_VIEWS_T(request, "stream_protocol_not_supported"))
if pull_stream_url == "":
raise Exception(LANG_VIEWS_T(request, "stream_url_incorrect"))
if camera_device_id == "":
camera_device_id = code
user_id = f_sessionReadUserId(request)
now_date = datetime.now()
stream = StreamModel()
stream.user_id = user_id
stream.sort = 0
stream.code = code
stream.app = APP_NAME_LIVE
stream.name = code
stream.pull_stream_url = pull_stream_url
stream.pull_stream_type = pull_stream_type # v4.405新增
stream.pull_stream_transfer_mode = 0 # v4.405新增
stream.pull_stream_ip = pull_stream_ip # v4.405新增
stream.pull_stream_port = pull_stream_port # v4.405新增
stream.pull_stream_username = encrypt_credential(onvif_username)
stream.pull_stream_password = encrypt_credential(onvif_password)
stream.nickname = nickname
stream.remark = remark
stream.forward_state = 0 # 默认未开启转发
stream.is_audio = is_audio # v4.405新增
stream.camera_name = camera_name
stream.camera_manufacturer = camera_manufacturer
stream.camera_device_id = camera_device_id
stream.record_enable = record_enable
stream.create_time = now_date
stream.last_update_time = now_date
stream.add_type = 0
stream.state = 0
stream.save()
# 添加日志
user_id = f_sessionReadUserId(request)
lang = f_parseRequestLang(request)
LogUtils.add_stream_log(user_id=user_id, stream_code=code,
log_type=LogUtils.LOG_TYPE_ADD, lang=lang)
__msg = LANG_VIEWS_T(request, "msg_add_success")
__ret = True
except Exception as e:
__msg = str(e)
else:
__msg = __check_msg
else:
__msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if __ret else 0,
"msg": __msg
}
g_logger.info("StreamView.openAdd() res:%s" % str(res))
return f_responseJson(res)
def api_openEditContext(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
request_ip = f_parseRequestIp(request) # v4.725新增) 获取实时请求ip
stream_info = {}
if "GET" == request.method:
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parseGetParams(request)
g_logger.info("StreamView.openEditContext() request_ip:%s,params:%s" % (request_ip,str(params)))
stream_code = params.get("code", "").strip()
stream = g_database.select("select * from av_stream where code=%s limit 1", [stream_code])
if len(stream) > 0:
stream_info = _hide_stream_credentials(stream[0])
camera_device_id = stream_info["camera_device_id"]
if camera_device_id is None or camera_device_id == "":
stream_info["camera_device_id"] = stream_code
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = LANG_VIEWS_T(request, "stream_not_found")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"stream": stream_info,
"pull_stream_types": g_pull_stream_types,
"audio_types": get_audio_types(f_parseRequestLang(request))
}
g_logger.info("StreamView.openEditContext() res:%s" % str(res))
return f_responseJson(res)
def api_openStreamByAppAndName(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
data = {}
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parseGetParams(request)
g_logger.info("StreamView.openStreamByAppAndName() params:%s" % str(params))
app = params.get("app", "").strip()
name = params.get("name", "").strip()
if app and name:
streams = g_database.select(
"select * from av_stream where app=%s and name=%s limit 1", [app, name])
if len(streams) > 0:
data = _hide_stream_credentials(streams[0])
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = LANG_VIEWS_T(request, "stream_not_found")
else:
msg = LANG_VIEWS_T(request, "stream_app_name_required")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"data": data
}
g_logger.info("StreamView.openStreamByAppAndName() res:%s" % str(res))
return f_responseJson(res)
def api_openEdit(request):
__ret = False
__msg = LANG_VIEWS_T(request, "msg_unknown_error")
if "POST" == request.method:
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
try:
params = f_parsePostParams(request)
g_logger.info("StreamView.openEdit() params:%s" % str(params))
lang = f_parseRequestLang(request)
code = params.get("code", "").strip()
nickname = params.get("nickname", "").strip()
pull_stream_url = params.get("pull_stream_url", "").strip()
pull_stream_ip = params.get("pull_stream_ip", "").strip()
pull_stream_port = int(params.get("pull_stream_port", 0))
camera_name = params.get("camera_name", "").strip()
camera_manufacturer = params.get("camera_manufacturer", "").strip()
camera_device_id = params.get("camera_device_id", "").strip()
remark = params.get("remark", "").strip()
is_audio = int(params.get("is_audio", 0))
record_enable = int(params.get("record_enable", 0))
if code == "":
raise Exception(LANG_VIEWS_T(request, "stream_code_invalid"))
if nickname == "":
raise Exception(LANG_VIEWS_T(request, "stream_name_incorrect"))
else:
nickname_len = len(nickname)
if len(nickname) > 50:
raise Exception(LANG_VIEWS_T(request, "stream_name_max_length") % nickname_len)
stream = StreamModel.objects.filter(code=code).first()
if stream:
if (stream.pull_stream_type == 1 or stream.pull_stream_type == 2 or
stream.pull_stream_type == 3 or stream.pull_stream_type == 4):
if pull_stream_url == "":
raise Exception(LANG_VIEWS_T(request, "stream_url_required"))
# 检测是否修改摄像头视频地址 start
if stream.pull_stream_url != pull_stream_url:
# 拉流地址不一致,则首先需要停止拉流代理
__del_ret, __del_msg = GlobalUtils.delStreamProxy(stream, lang=lang)
stream.forward_state = 0
stream.pull_stream_url = pull_stream_url
# 检测是否修改摄像头视频地址 end
if stream.pull_stream_type != 21:
# 非gb28181接入需要设置分组编号
stream.camera_device_id = camera_device_id
stream.pull_stream_ip = pull_stream_ip # v4.405新增
stream.pull_stream_port = pull_stream_port # v4.405新增
stream.is_audio = is_audio
stream.nickname = nickname
stream.remark = remark
stream.camera_name = camera_name
stream.camera_manufacturer = camera_manufacturer
old_record = stream.record_enable
stream.record_enable = record_enable
stream.last_update_time = datetime.now()
stream.save()
from app.views.NvrView import _sync_stream_recording
_sync_stream_recording(stream)
# 添加日志
user_id = f_sessionReadUserId(request)
lang = f_parseRequestLang(request)
LogUtils.add_stream_log(user_id=user_id, stream_code=code,
log_type=LogUtils.LOG_TYPE_EDIT, lang=lang)
__msg = LANG_VIEWS_T(request, "msg_edit_success")
__ret = True
else:
__msg = LANG_VIEWS_T(request, "stream_not_found")
except Exception as e:
__msg = str(e)
else:
__msg = __check_msg
else:
__msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if __ret else 0,
"msg": __msg
}
g_logger.info("StreamView.openEdit() res:%s" % str(res))
return f_responseJson(res)
def api_openPlayer(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
info = {}
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parseGetParams(request)
g_logger.info("StreamView.openPlayer() params:%s" % str(params))
request_ip = f_parseRequestIp(request) # v4.725新增) 获取实时请求ip
app = params.get("app","").strip()
name = params.get("name","").strip()
try:
if app and name:
media_info = g_zlm.getMediaInfo(app=app, name=name)
media_is_online = 1 if media_info else 0
# v4.706确认,返回结构需要保持当前现状
stream_info = {
"is_online": media_is_online,
"app": app,
# "produce_speed": produce_speed,
# "video": video_str,
"aliveSecond": media_info.get("aliveSecond", 0),
"totalReaderCount": media_info.get("totalReaderCount", 0),
"video_codec_name": media_info.get("video_codec_name", ""),
"video_width": media_info.get("video_width", 0),
"video_height": media_info.get("video_height", 0),
# "audio": audio_str,
# "originUrl": d.get("originUrl"), # 推流地址
# "originType": d.get("originType"), # 推流地址采用的推流协议类型
# "originTypeStr": d.get("originTypeStr"), # 推流地址采用的推流协议类型(字符串)
# "clients": d.get("totalReaderCount"), # 客户端总数量
# "schemas_clients": schemas_clients,
"videoUrl": g_zlm.get_wsMp4Url(app=app, name=name, request_ip=request_ip),
"wsHost": g_zlm.get_wsHost(request_ip=request_ip),
"wsMp4Url": g_zlm.get_wsMp4Url(app=app, name=name, request_ip=request_ip),
"wsFlvUrl": g_zlm.get_wsFlvUrl(app=app, name=name, request_ip=request_ip),
"httpMp4Url": g_zlm.get_httpMp4Url(app=app, name=name, request_ip=request_ip),
"httpFlvUrl": g_zlm.get_httpFlvUrl(app=app, name=name, request_ip=request_ip),
"rtspUrl": g_zlm.get_rtspUrl(app=app, name=name, request_ip=request_ip),
"rtmpUrl": g_zlm.get_rtmpUrl(app=app, name=name, request_ip=request_ip)
}
stream = StreamModel.objects.filter(app=app, name=name).first()
if stream:
stream_info["code"] = stream.code
stream_info["pull_stream_type"] = stream.pull_stream_type
else:
stream_info["code"] = name
stream_info["pull_stream_type"] = 0
else:
stream_info = {
"pull_stream_type": 0,
"is_online": 0,
}
except Exception as e:
g_logger.error("StreamView.openPlayer() e:%s" % str(e))
stream_info = {
"pull_stream_type": 0,
"is_online": 0,
}
info["stream"] = stream_info
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"info": info
}
g_logger.info("StreamView.openPlayer() res:%s" % str(res))
return f_responseJson(res)
def player(request):
context = {}
request_ip = f_parseRequestIp(request) # v4.725新增) 获取实时请求ip
params = f_parseGetParams(request)
g_logger.info("StreamView.player() request_ip:%s,params:%s" % (request_ip,str(params)))
app = params.get("app","").strip()
name = params.get("name","").strip()
try:
if app and name:
media_info = g_zlm.getMediaInfo(app=app, name=name)
media_is_online = 1 if media_info else 0
# v4.706确认,返回结构需要保持当前现状
stream_info = {
"is_online": media_is_online,
"app": app,
"name": name,
"aliveSecond": media_info.get("aliveSecond", 0),
"totalReaderCount": media_info.get("totalReaderCount", 0),
"video_codec_name": media_info.get("video_codec_name", ""),
"video_width": media_info.get("video_width", 0),
"video_height": media_info.get("video_height", 0),
"videoUrl": g_zlm.get_wsMp4Url(app=app, name=name, request_ip=request_ip),
"wsHost": g_zlm.get_wsHost(request_ip=request_ip),
"wsMp4Url": g_zlm.get_wsMp4Url(app=app, name=name, request_ip=request_ip),
"wsFlvUrl": g_zlm.get_wsFlvUrl(app=app, name=name, request_ip=request_ip),
"httpMp4Url": g_zlm.get_httpMp4Url(app=app, name=name, request_ip=request_ip),
"httpFlvUrl": g_zlm.get_httpFlvUrl(app=app, name=name, request_ip=request_ip),
"rtspUrl": g_zlm.get_rtspUrl(app=app, name=name, request_ip=request_ip),
"rtmpUrl": g_zlm.get_rtmpUrl(app=app, name=name, request_ip=request_ip)
}
# 优先:直接按 app+name 查找live 原始流、rtp 等内部流)
stream = StreamModel.objects.filter(app=app, name=name).first()
if stream:
stream_info["code"] = stream.code
stream_info["pull_stream_type"] = stream.pull_stream_type
else:
stream_info["code"] = name
stream_info["pull_stream_type"] = 0
else:
stream_info = {
"pull_stream_type": 0,
"is_online": 0,
}
except Exception as e:
g_logger.error("StreamView.player() e:%s" % str(e))
stream_info = {
"pull_stream_type": 0,
"is_online": 0,
}
# 用 json.dumps 序列化为合法 JSON 字符串True/False -> true/false
# 避免 Django 模板 {{ stream|safe }} 把 Python bool 渲染成 JS 标识符导致 "True/False is not defined"
context["stream_json"] = json.dumps(stream_info, ensure_ascii=False)
g_logger.info("StreamView.player() stream_info:%s" % str(stream_info))
return render(request, 'app/stream/player.html', context)
def api_openIndex(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
data = []
pageData = {}
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parseGetParams(request)
# g_logger.info("StreamView.openIndex() params:%s" % str(params))
# 同步数据库和在线流状态 start
medias = g_zlm.getMediaList()
if len(medias) == 0:
# 流媒体服务不在线,全部更新下线状态
g_database.execute("update av_stream set forward_state=0")
else:
media_dict = {}
for m in medias:
media_dict[m["an"]] = m
db_streams = f_dbReadStreamData()
for d in db_streams:
d_id = int(d.get("id", 0))
d_forward_state = d.get("forward_state", 0)
app_name = "{app}_{name}".format(app=d["app"], name=d["name"])
if media_dict.get(app_name):
if d_forward_state != 1:
g_database.execute("update av_stream set forward_state=1 where id=%s", [d_id])
else:
if d_forward_state != 0:
g_database.execute("update av_stream set forward_state=0 where id=%s", [d_id])
# 同步数据库和在线流状态 end
page = params.get('p', 1)
page_size = params.get('ps', 10)
search_text = str(params.get('search_text', "")).strip() # v4.638新增
search_status = int(params.get('search_status', -1)) # v4.716新增 转发状态筛选:-1全部 0未转发 1转发中
try:
page = int(page)
except:
page = 1
try:
page_size = int(page_size)
if page_size < 1:
page_size = 1
except:
page_size = 10
# 构建查询条件
conditions = []
query_params = []
if search_text:
conditions.append("(nickname like %s or code = %s)")
query_params.extend(["%%%s%%" % search_text, search_text])
if search_status != -1:
conditions.append("forward_state = %s")
query_params.append(search_status)
if search_text and search_status != -1:
sql = ("select * from av_stream where (nickname like %s or code = %s) "
"and forward_state = %s order by id desc")
elif search_text:
sql = "select * from av_stream where (nickname like %s or code = %s) order by id desc"
elif search_status != -1:
sql = "select * from av_stream where forward_state = %s order by id desc"
else:
sql = "select * from av_stream order by id desc"
skip = (page - 1) * page_size
data = g_database.select(sql, query_params)
rec_ids = set()
try:
from app.recording.manager import get_recording_manager
rec_ids = set(get_recording_manager().list_recording())
except Exception:
pass
for d in data:
_hide_stream_credentials(d)
camera_device_id = d.get("camera_device_id")
if camera_device_id is None or camera_device_id == "":
camera_device_id = d["code"]
d["camera_device_id"] = camera_device_id
sid = int(d.get("id", 0) or 0)
d["record_enable"] = int(d.get("record_enable", 0) or 0)
d["is_recording"] = 1 if sid in rec_ids else 0
grouped_data = group_by_field(data, field="camera_device_id")
count = len(grouped_data)
if count > 0:
grouped_data = grouped_data[skip:skip + page_size]
for i, group in enumerate(grouped_data):
# print(f"组 {i + 1}: {group}")
for d in group:
camera_name = d.get("camera_name","unknown name")
d["camera_name"] = camera_name
camera_manufacturer = d.get("camera_manufacturer","unknown manufacturer")
d["camera_manufacturer"] = camera_manufacturer
d["last_update_time"] = d.get("last_update_time").strftime("%Y/%m/%d %H:%M")
data = grouped_data
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
data = []
msg = LANG_VIEWS_T(request, "msg_empty")
page_num = int(count / page_size) # 总页数
if count % page_size > 0:
page_num += 1
pageData = {
"page": page,
"page_size": page_size,
"page_num": page_num,
"count": count,
"pageLabels": buildPageLabels(page=page, page_num=page_num, lang=f_parseRequestLang(request))
}
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"data": data,
"pageData": pageData,
"extra": {
"audioTypes": get_audio_types(f_parseRequestLang(request)),
"pullStreamTypes": g_pull_stream_types
}
}
return f_responseJson(res)
def api_openExportFile(request):
"""导出所有摄像头数据为Excel文件与导入模板格式一致"""
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
g_logger.info("StreamView.openExportFile()")
import io
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
# lang = f_parseRequestLang(request)
try:
# 查询所有摄像头数据
streams = StreamModel.objects.all().order_by("id")
# 创建工作簿
wb = Workbook()
ws = wb.active
ws.title = "摄像头数据"
# 定义表头(与导入模板完全一致)
headers = [
"摄像头编号", "摄像头昵称", "拉流地址", "IP地址", "端口",
"用户名", "密码", "备注", "是否音频", "分组编号"
]
# 表头样式
header_font = Font(name='微软雅黑', size=11, bold=True, color='FFFFFF')
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_align = Alignment(horizontal='center', vertical='center', wrap_text=True)
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 写入表头
for col_idx, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_idx, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_align
cell.border = thin_border
# 数据样式
data_font = Font(name='微软雅黑', size=10)
data_align = Alignment(horizontal='left', vertical='center', wrap_text=True)
# 写入数据行
for row_idx, stream in enumerate(streams, 2):
# 设备ID处理如果为空则使用code
camera_device_id = stream.camera_device_id if stream.camera_device_id else stream.code
row_data = [
stream.code or "",
stream.nickname or "",
stream.pull_stream_url or "",
stream.pull_stream_ip or "",
stream.pull_stream_port or 554,
"[REDACTED]" if stream.pull_stream_username else "",
"[REDACTED]" if stream.pull_stream_password else "",
stream.remark or "",
stream.is_audio or 0,
camera_device_id
]
for col_idx, value in enumerate(row_data, 1):
cell = ws.cell(row=row_idx, column=col_idx, value=value)
cell.font = data_font
cell.alignment = data_align
cell.border = thin_border
# 设置列宽
col_widths = [20, 20, 40, 18, 10, 18, 18, 30, 10, 20]
for col_idx, width in enumerate(col_widths, 1):
ws.column_dimensions[ws.cell(row=1, column=col_idx).column_letter].width = width
# 设置行高(表头)
ws.row_dimensions[1].height = 25
# 生成响应
buffer = io.BytesIO()
wb.save(buffer)
buffer.seek(0)
response = HttpResponse(buffer.getvalue(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
filename = "cameras_export_%s.xlsx" % datetime.now().strftime("%Y%m%d_%H%M%S")
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
except Exception as e:
msg = str(e)
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg
}
g_logger.info("StreamView.openExportFile() res:%s" % str(res))
return f_responseJson(res)
def api_openImportFile(request):
__ret = False
__msg = LANG_VIEWS_T(request, "msg_unknown_error")
if request.method == 'POST':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parsePostParams(request)
lang = f_parseRequestLang(request)
g_logger.info("StreamView.openImportFile() params:%s" % str(params))
try:
file = request.FILES.get("file")
importRemark = str(params.get("importRemark", "")).strip()
upload_utils = UploadUtils()
__upload_ret, __upload_msg, __upload_data = upload_utils.upload_camera_xlsx(file=file,
upload_dir=g_config.storageTempDir,
lang=lang)
if __upload_ret:
user_id = f_sessionReadUserId(request)
success_count = 0
error_count = 0
for d in __upload_data:
try:
stream_code = d["code"]
if not stream_code: # 未填写编号,则自动生成编号
stream_code = GB28181CodeUtils().generate_by_time()
nickname = d["nickname"]
pull_stream_url = d["pull_stream_url"]
pull_stream_ip = d["pull_stream_ip"]
pull_stream_port = d["pull_stream_port"]
username = d["username"]
password = d["password"]
camera_device_id = str(d.get("camera_device_id", "")).strip()
is_audio = int(d.get("is_audio", 0))
remark = str(d.get("remark", "")).strip()
remark = "remark=%s,importRemark=%s" % (remark, importRemark)
if camera_device_id is None or camera_device_id == "":
camera_device_id = stream_code
if pull_stream_url.startswith("rtsp://") or pull_stream_url.startswith("rtsps://"):
pull_stream_type = 1
elif pull_stream_url.startswith("rtmp://") or pull_stream_url.startswith("rtmps://"):
pull_stream_type = 2
elif pull_stream_url.startswith("http://") or pull_stream_url.startswith("https://"):
pull_stream_type = 3
else:
raise Exception(LANG_VIEWS_T(request, "stream_url_format_error"))
stream = StreamModel.objects.filter(code=stream_code).first()
if stream:
raise Exception(LANG_VIEWS_T(request, "stream_code_exists"))
else:
stream = StreamModel()
stream.user_id = user_id
stream.sort = 0
stream.code = stream_code
stream.app = g_zlm.default_stream_app
stream.name = stream_code
stream.pull_stream_url = pull_stream_url
stream.pull_stream_type = pull_stream_type # v4.405新增
stream.pull_stream_transfer_mode = 0 # v4.405新增
stream.pull_stream_ip = pull_stream_ip # v4.405新增
stream.pull_stream_port = pull_stream_port # v4.405新增
stream.pull_stream_username = encrypt_credential(username)
stream.pull_stream_password = encrypt_credential(password)
stream.nickname = nickname
stream.remark = remark
stream.forward_state = 0 # 默认未开启转发
stream.camera_device_id = camera_device_id # v4.638新增
stream.create_time = datetime.now()
stream.last_update_time = datetime.now()
stream.add_type = 1
stream.state = 0
stream.is_audio = is_audio # v4.405新增
stream.save()
success_count += 1
except Exception as e:
g_logger.error("StreamView.openImportFile() error: %s, d=%s" % (str(e),str(d)))
error_count += 1
__ret = True
__msg = LANG_VIEWS_T(request, "msg_batch_result") % (success_count, error_count)
else:
__msg = __upload_msg
except Exception as e:
__msg = str(e)
else:
__msg = __check_msg
else:
__msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if __ret else 0,
"msg": __msg
}
g_logger.info("StreamView.openImportFile() res:%s" % str(res))
return f_responseJson(res)
def api_openDel(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
lang = f_parseRequestLang(request)
if request.method == 'POST':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parsePostParams(request)
g_logger.info("StreamView.openDel() params:%s" % str(params))
handle = params.get("handle") # one删除一个视频流all删除全部视频流
stream_code = params.get("code")
if handle == "one":
stream = StreamModel.objects.filter(code=stream_code).first()
if stream:
__ret, __msg = GlobalUtils.delStreamProxy(stream, lang=lang)
if stream.delete():
# 同步清理内存里的该通道,避免 Catalog 刷新后自动恢复
g_gb28181SipServer.remove_channel(
stream_code,
device_id=stream.camera_device_id or None,
)
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = LANG_VIEWS_T(request, "stream_delete_failed")
else:
msg = LANG_VIEWS_T(request, "stream_not_found")
elif handle == "all":
success_count = 0
error_count = 0
streams = StreamModel.objects.all()
for stream in streams:
__ret, __msg = GlobalUtils.delStreamProxy(stream, lang=lang)
if stream.delete():
g_gb28181SipServer.remove_channel(stream.code, device_id=stream.camera_device_id or None)
success_count += 1
else:
error_count += 1
ret = True
msg = LANG_VIEWS_T(request, "msg_batch_result") % (success_count, error_count)
else:
msg = LANG_VIEWS_T(request, "msg_invalid_parameter")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg
}
g_logger.info("StreamView.openDel() res:%s" % str(res))
return f_responseJson(res)
def api_openHandleAllStreamProxy(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
lang = f_parseRequestLang(request)
if request.method == 'POST':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parsePostParams(request)
g_logger.info("StreamView.openHandleAllStreamProxy() params:%s" % str(params))
handle = params.get("handle") # add,del (add 开启流del 关闭流)
if handle == "add": # 全部开启转发
ret,msg = GlobalUtils.addAllStreamProxy(lang=lang)
elif handle == "del": # 全部关闭转发
GlobalUtils.delAllStreamProxy(lang=lang)
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = LANG_VIEWS_T(request, "msg_invalid_parameter")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg
}
g_logger.info("StreamView.openHandleAllStreamProxy() res:%s" % str(res))
return f_responseJson(res)
def api_openAddStreamProxy(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
lang = f_parseRequestLang(request)
if request.method == 'POST':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parsePostParams(request)
g_logger.info("StreamView.openAddStreamProxy() params:%s" % str(params))
stream_code = params.get("code", "").strip()
stream = StreamModel.objects.filter(code=stream_code).first()
if stream:
if stream.forward_state == 1:
ret = True
msg = LANG_VIEWS_T(request, "stream_forward_already_enabled")
else:
ret, msg = GlobalUtils.addStreamProxy(stream, lang=lang)
if ret:
stream.forward_state = 1
stream.save()
from app.views.NvrView import _sync_stream_recording
_sync_stream_recording(stream)
ret = True
msg = LANG_VIEWS_T(request, "stream_forward_enabled_success")
else:
msg = LANG_VIEWS_T(request, "stream_not_found")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg
}
g_logger.info("StreamView.openAddStreamProxy() res:%s" % str(res))
return f_responseJson(res)
def api_openDelStreamProxy(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
lang = f_parseRequestLang(request)
if request.method == 'POST':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parsePostParams(request)
g_logger.info("StreamView.openDelStreamProxy() params:%s" % str(params))
stream_code = params.get("code", "").strip()
stream = StreamModel.objects.filter(code=stream_code).first()
if stream:
__ret, __msg = GlobalUtils.delStreamProxy(stream, lang=lang)
stream.forward_state = 0
stream.save()
from app.views.NvrView import _sync_stream_recording
_sync_stream_recording(stream)
ret = __ret
msg = __msg
else:
msg = LANG_VIEWS_T(request, "stream_not_found")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg
}
g_logger.info("StreamView.openDelStreamProxy() res:%s" % str(res))
return f_responseJson(res)
def api_openPtz(request):
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
lang = f_parseRequestLang(request)
if request.method == 'POST':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parsePostParams(request)
g_logger.info("StreamView.openPtz() params:%s" % str(params))
code = params.get("code", "").strip()
try:
ptzType = int(params.get("ptzType", 0))
except (TypeError, ValueError):
ptzType = -1
# 优先读取前端实际参数名 ptzSpeed兼容旧字段 val
try:
val = int(params.get("ptzSpeed", params.get("val", 0)) or 0)
except (TypeError, ValueError):
val = 0
if ptzType < 0 or ptzType > 14:
msg = LANG_VIEWS_T(request, "ptz_type_range_error")
else:
# 除停止外均需非零速度才会动作越界值钳制到0-255
if ptzType != 0 and val < 1:
val = 32
val = min(max(val, 0), 255)
stream = StreamModel.objects.filter(code=code).first()
if stream:
if stream.pull_stream_type == 21:
ret, msg = g_gb28181SipServer.request_ptz(
client_id=stream.camera_device_id,
channel_id=stream.code,
ptz_type=ptzType,
val=val,
# 分进程模式web进程无实时SIP注册表从库中取设备地址外发
device_ip=stream.pull_stream_ip or None,
device_port=stream.pull_stream_port or 0)
else:
msg = LANG_VIEWS_T(request, "stream_pull_type_not_supported")
else:
msg = LANG_VIEWS_T(request, "stream_not_found")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg
}
g_logger.info("StreamView.openPtz() res:%s" % str(res))
return f_responseJson(res)
def api_openGetAllStreamData(request):
"""在线预览页面获取所有在线流数据(原 OpenView 中的同名接口迁移至此)"""
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
data = []
request_ip = f_parseRequestIp(request)
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
params = f_parseGetParams(request)
handle = params.get("handle", "").strip()
g_logger.info("StreamView.openGetAllStreamData() request_ip:%s,params:%s" % (request_ip, str(params)))
# 获取所有 ZLM 在线流
medias = g_zlm.getMediaList(request_ip=request_ip)
# 构建 app_name -> 摄像头信息 映射(从数据库)
db_streams = g_database.select("select id,app,name,code,nickname from av_stream")
db_dict = {}
for s in db_streams:
key = "{app}_{name}".format(app=s["app"], name=s["name"])
db_dict[key] = s
for m in medias:
app = m.get("app", "")
name = m.get("name", "")
app_name = m.get("app_name", "{0}_{1}".format(app, name))
nickname = name
code = app_name
db_info = db_dict.get(app_name)
if db_info:
code = db_info.get("code") or app_name
nickname = db_info.get("nickname") or name
m["id"] = db_info.get("id")
m["code"] = code
m["nickname"] = nickname
data.append(m)
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"data": data
}
g_logger.info("StreamView.openGetAllStreamData() res.data length:%d" % len(data))
return f_responseJson(res)
def api_openGetStatisticsStream(request):
"""首页流媒体统计(原 OpenView 接口迁移至此)"""
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
info = {"groupCount": 0, "count": 0, "forwardCount": 0}
request_ip = f_parseRequestIp(request)
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
g_logger.info("StreamView.openGetStatisticsStream() request_ip:%s" % request_ip)
try:
# 接入数量 = 数据库中摄像头总数
db_rows = g_database.select("select app,name from av_stream")
info["count"] = len(db_rows)
# 在线数量 = ZLM 中实际存在的流数量
medias = g_zlm.getMediaList(request_ip=request_ip)
info["forwardCount"] = len(medias)
# 分组数量 = 不同 app 的数量
apps = set()
for r in db_rows:
if r.get("app"):
apps.add(r["app"])
info["groupCount"] = len(apps)
except Exception as e:
g_logger.error("StreamView.openGetStatisticsStream() e:%s" % str(e))
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
res = {
"code": 1000 if ret else 0,
"msg": msg,
"info": info
}
g_logger.info("StreamView.openGetStatisticsStream() res:%s" % str(res))
return f_responseJson(res)
def api_openOnvifDiscover(request):
"""WS-Discovery 发现局域网 ONVIF 设备"""
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
data = []
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
try:
from app.services.onvif_discovery import discover_onvif, get_rtsp_url_from_onvif
params = f_parseGetParams(request)
timeout = float(params.get("timeout", 3))
username = (params.get("username") or "").strip()
password = (params.get("password") or "").strip()
resolve = str(params.get("resolve_rtsp", "0")).strip() in ("1", "true", "yes")
devices = discover_onvif(timeout=timeout)
for d in devices:
item = dict(d)
if resolve and d.get("xaddr"):
rtsp = get_rtsp_url_from_onvif(d["xaddr"], username, password)
if rtsp:
item["rtsp_url"] = rtsp
data.append(item)
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
except Exception as e:
msg = str(e)
g_logger.exception("openOnvifDiscover: %s", e)
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
return f_responseJson({"code": 1000 if ret else 0, "msg": msg, "data": data})