feat: add tags to streampath

This commit is contained in:
pggiroro
2025-10-03 20:33:01 +08:00
parent 0e2d7ee3c0
commit d7a3f2c55d
7 changed files with 1450 additions and 286 deletions

View File

@@ -608,3 +608,227 @@ func (p *MP4Plugin) Delete(ctx context.Context, req *mp4pb.ReqRecordDelete) (res
}
return p.Server.DeleteRecord(ctx, globalReq)
}
// CreateTag 创建标签
func (p *MP4Plugin) CreateTag(ctx context.Context, req *mp4pb.ReqCreateTag) (res *mp4pb.ResponseTag, err error) {
res = &mp4pb.ResponseTag{}
// 检查数据库连接
if p.DB == nil {
res.Code = 500
res.Message = pkg.ErrNoDB.Error()
return res, pkg.ErrNoDB
}
// 解析标签时间
tagTime, err := util.TimeQueryParse(req.TagTime)
if err != nil {
res.Code = 400
res.Message = "标签时间格式错误: " + err.Error()
return res, err
}
// 创建标签记录
tag := &mp4.TagModel{
TagName: req.TagName,
StreamPath: req.StreamPath,
TagTime: tagTime,
}
// 保存到数据库
if err = p.DB.Create(tag).Error; err != nil {
res.Code = 500
res.Message = "创建标签失败: " + err.Error()
return res, err
}
// 返回成功结果
res.Code = 0
res.Message = "创建成功"
res.Data = &mp4pb.TagInfo{
Id: uint32(tag.ID),
TagName: tag.TagName,
StreamPath: tag.StreamPath,
TagTime: tag.TagTime.Format(time.RFC3339),
CreatedAt: tag.CreatedAt.Format(time.RFC3339),
UpdatedAt: tag.UpdatedAt.Format(time.RFC3339),
}
return res, nil
}
// UpdateTag 更新标签
func (p *MP4Plugin) UpdateTag(ctx context.Context, req *mp4pb.ReqUpdateTag) (res *mp4pb.ResponseTag, err error) {
res = &mp4pb.ResponseTag{}
// 检查数据库连接
if p.DB == nil {
res.Code = 500
res.Message = pkg.ErrNoDB.Error()
return res, pkg.ErrNoDB
}
// 查询标签是否存在
var tag mp4.TagModel
if err = p.DB.First(&tag, req.Id).Error; err != nil {
res.Code = 404
res.Message = "标签不存在: " + err.Error()
return res, err
}
// 更新字段
if req.TagName != "" {
tag.TagName = req.TagName
}
if req.StreamPath != "" {
tag.StreamPath = req.StreamPath
}
if req.TagTime != "" {
tagTime, err := util.TimeQueryParse(req.TagTime)
if err != nil {
res.Code = 400
res.Message = "标签时间格式错误: " + err.Error()
return res, err
}
tag.TagTime = tagTime
}
// 保存更新
if err = p.DB.Save(&tag).Error; err != nil {
res.Code = 500
res.Message = "更新标签失败: " + err.Error()
return res, err
}
// 返回成功结果
res.Code = 0
res.Message = "更新成功"
res.Data = &mp4pb.TagInfo{
Id: uint32(tag.ID),
TagName: tag.TagName,
StreamPath: tag.StreamPath,
TagTime: tag.TagTime.Format(time.RFC3339),
CreatedAt: tag.CreatedAt.Format(time.RFC3339),
UpdatedAt: tag.UpdatedAt.Format(time.RFC3339),
}
return res, nil
}
// DeleteTag 删除标签(软删除)
func (p *MP4Plugin) DeleteTag(ctx context.Context, req *mp4pb.ReqDeleteTag) (res *mp4pb.ResponseTag, err error) {
res = &mp4pb.ResponseTag{}
// 检查数据库连接
if p.DB == nil {
res.Code = 500
res.Message = pkg.ErrNoDB.Error()
return res, pkg.ErrNoDB
}
// 软删除标签
if err = p.DB.Delete(&mp4.TagModel{}, req.Id).Error; err != nil {
res.Code = 500
res.Message = "删除标签失败: " + err.Error()
return res, err
}
// 返回成功结果
res.Code = 0
res.Message = "删除成功"
return res, nil
}
// ListTag 查询标签列表
func (p *MP4Plugin) ListTag(ctx context.Context, req *mp4pb.ReqListTag) (res *mp4pb.ResponseTagList, err error) {
res = &mp4pb.ResponseTagList{}
// 检查数据库连接
if p.DB == nil {
res.Code = 500
res.Message = pkg.ErrNoDB.Error()
return res, pkg.ErrNoDB
}
// 构建查询
query := p.DB.Model(&mp4.TagModel{})
// 流路径过滤(默认模糊匹配)
if req.StreamPath != "" {
if strings.Contains(req.StreamPath, "*") {
query = query.Where("stream_path LIKE ?", strings.ReplaceAll(req.StreamPath, "*", "%"))
} else {
query = query.Where("stream_path LIKE ?", "%"+req.StreamPath+"%")
}
}
// 标签名称过滤(默认模糊匹配)
if req.TagName != "" {
if strings.Contains(req.TagName, "*") {
query = query.Where("tag_name LIKE ?", strings.ReplaceAll(req.TagName, "*", "%"))
} else {
query = query.Where("tag_name LIKE ?", "%"+req.TagName+"%")
}
}
// 时间范围过滤(只有当传入了时间参数时才进行过滤)
if req.Start != "" {
startTime, err := util.TimeQueryParse(req.Start)
if err == nil && !startTime.IsZero() {
query = query.Where("tag_time >= ?", startTime)
}
}
if req.End != "" {
endTime, err := util.TimeQueryParse(req.End)
if err == nil && !endTime.IsZero() {
query = query.Where("tag_time <= ?", endTime)
}
}
// 分页
page := req.Page
count := req.Count
if page < 1 {
page = 1
}
if count < 1 {
count = 10
}
offset := (page - 1) * count
// 获取总数
var total int64
if err = query.Count(&total).Error; err != nil {
res.Code = 500
res.Message = "查询总数失败: " + err.Error()
return res, err
}
// 查询数据
var tags []mp4.TagModel
if err = query.Order("tag_time DESC").Offset(int(offset)).Limit(int(count)).Find(&tags).Error; err != nil {
res.Code = 500
res.Message = "查询标签失败: " + err.Error()
return res, err
}
// 转换为响应格式
res.Code = 0
res.Message = "查询成功"
res.Total = uint32(total)
res.List = make([]*mp4pb.TagInfo, 0, len(tags))
for _, tag := range tags {
res.List = append(res.List, &mp4pb.TagInfo{
Id: uint32(tag.ID),
TagName: tag.TagName,
StreamPath: tag.StreamPath,
TagTime: tag.TagTime.Format(time.RFC3339),
CreatedAt: tag.CreatedAt.Format(time.RFC3339),
UpdatedAt: tag.UpdatedAt.Format(time.RFC3339),
})
}
return res, nil
}

View File

@@ -56,6 +56,10 @@ func (p *MP4Plugin) Start() (err error) {
if err != nil {
return
}
err = p.DB.AutoMigrate(&mp4.TagModel{})
if err != nil {
return
}
if p.AutoOverWriteDiskPercent > 0 {
var deleteRecordTask DeleteRecordTask
deleteRecordTask.DB = p.DB

View File

@@ -585,6 +585,475 @@ func (x *ResponseStopRecord) GetData() uint64 {
return 0
}
// 标签相关消息定义
type TagInfo struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
TagName string `protobuf:"bytes,2,opt,name=tagName,proto3" json:"tagName,omitempty"`
StreamPath string `protobuf:"bytes,3,opt,name=streamPath,proto3" json:"streamPath,omitempty"`
TagTime string `protobuf:"bytes,4,opt,name=tagTime,proto3" json:"tagTime,omitempty"`
CreatedAt string `protobuf:"bytes,5,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
UpdatedAt string `protobuf:"bytes,6,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TagInfo) Reset() {
*x = TagInfo{}
mi := &file_mp4_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *TagInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TagInfo) ProtoMessage() {}
func (x *TagInfo) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TagInfo.ProtoReflect.Descriptor instead.
func (*TagInfo) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{8}
}
func (x *TagInfo) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
func (x *TagInfo) GetTagName() string {
if x != nil {
return x.TagName
}
return ""
}
func (x *TagInfo) GetStreamPath() string {
if x != nil {
return x.StreamPath
}
return ""
}
func (x *TagInfo) GetTagTime() string {
if x != nil {
return x.TagTime
}
return ""
}
func (x *TagInfo) GetCreatedAt() string {
if x != nil {
return x.CreatedAt
}
return ""
}
func (x *TagInfo) GetUpdatedAt() string {
if x != nil {
return x.UpdatedAt
}
return ""
}
type ReqCreateTag struct {
state protoimpl.MessageState `protogen:"open.v1"`
TagName string `protobuf:"bytes,1,opt,name=tagName,proto3" json:"tagName,omitempty"`
StreamPath string `protobuf:"bytes,2,opt,name=streamPath,proto3" json:"streamPath,omitempty"`
TagTime string `protobuf:"bytes,3,opt,name=tagTime,proto3" json:"tagTime,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReqCreateTag) Reset() {
*x = ReqCreateTag{}
mi := &file_mp4_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReqCreateTag) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqCreateTag) ProtoMessage() {}
func (x *ReqCreateTag) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReqCreateTag.ProtoReflect.Descriptor instead.
func (*ReqCreateTag) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{9}
}
func (x *ReqCreateTag) GetTagName() string {
if x != nil {
return x.TagName
}
return ""
}
func (x *ReqCreateTag) GetStreamPath() string {
if x != nil {
return x.StreamPath
}
return ""
}
func (x *ReqCreateTag) GetTagTime() string {
if x != nil {
return x.TagTime
}
return ""
}
type ReqUpdateTag struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
TagName string `protobuf:"bytes,2,opt,name=tagName,proto3" json:"tagName,omitempty"`
StreamPath string `protobuf:"bytes,3,opt,name=streamPath,proto3" json:"streamPath,omitempty"`
TagTime string `protobuf:"bytes,4,opt,name=tagTime,proto3" json:"tagTime,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReqUpdateTag) Reset() {
*x = ReqUpdateTag{}
mi := &file_mp4_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReqUpdateTag) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqUpdateTag) ProtoMessage() {}
func (x *ReqUpdateTag) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReqUpdateTag.ProtoReflect.Descriptor instead.
func (*ReqUpdateTag) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{10}
}
func (x *ReqUpdateTag) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
func (x *ReqUpdateTag) GetTagName() string {
if x != nil {
return x.TagName
}
return ""
}
func (x *ReqUpdateTag) GetStreamPath() string {
if x != nil {
return x.StreamPath
}
return ""
}
func (x *ReqUpdateTag) GetTagTime() string {
if x != nil {
return x.TagTime
}
return ""
}
type ReqDeleteTag struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReqDeleteTag) Reset() {
*x = ReqDeleteTag{}
mi := &file_mp4_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReqDeleteTag) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqDeleteTag) ProtoMessage() {}
func (x *ReqDeleteTag) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReqDeleteTag.ProtoReflect.Descriptor instead.
func (*ReqDeleteTag) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{11}
}
func (x *ReqDeleteTag) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
type ReqListTag struct {
state protoimpl.MessageState `protogen:"open.v1"`
StreamPath string `protobuf:"bytes,1,opt,name=streamPath,proto3" json:"streamPath,omitempty"`
TagName string `protobuf:"bytes,2,opt,name=tagName,proto3" json:"tagName,omitempty"`
Start string `protobuf:"bytes,3,opt,name=start,proto3" json:"start,omitempty"`
End string `protobuf:"bytes,4,opt,name=end,proto3" json:"end,omitempty"`
Page uint32 `protobuf:"varint,5,opt,name=page,proto3" json:"page,omitempty"`
Count uint32 `protobuf:"varint,6,opt,name=count,proto3" json:"count,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReqListTag) Reset() {
*x = ReqListTag{}
mi := &file_mp4_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReqListTag) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqListTag) ProtoMessage() {}
func (x *ReqListTag) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReqListTag.ProtoReflect.Descriptor instead.
func (*ReqListTag) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{12}
}
func (x *ReqListTag) GetStreamPath() string {
if x != nil {
return x.StreamPath
}
return ""
}
func (x *ReqListTag) GetTagName() string {
if x != nil {
return x.TagName
}
return ""
}
func (x *ReqListTag) GetStart() string {
if x != nil {
return x.Start
}
return ""
}
func (x *ReqListTag) GetEnd() string {
if x != nil {
return x.End
}
return ""
}
func (x *ReqListTag) GetPage() uint32 {
if x != nil {
return x.Page
}
return 0
}
func (x *ReqListTag) GetCount() uint32 {
if x != nil {
return x.Count
}
return 0
}
type ResponseTag struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
Data *TagInfo `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResponseTag) Reset() {
*x = ResponseTag{}
mi := &file_mp4_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResponseTag) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResponseTag) ProtoMessage() {}
func (x *ResponseTag) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResponseTag.ProtoReflect.Descriptor instead.
func (*ResponseTag) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{13}
}
func (x *ResponseTag) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *ResponseTag) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *ResponseTag) GetData() *TagInfo {
if x != nil {
return x.Data
}
return nil
}
type ResponseTagList struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
List []*TagInfo `protobuf:"bytes,3,rep,name=list,proto3" json:"list,omitempty"`
Total uint32 `protobuf:"varint,4,opt,name=total,proto3" json:"total,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResponseTagList) Reset() {
*x = ResponseTagList{}
mi := &file_mp4_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResponseTagList) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResponseTagList) ProtoMessage() {}
func (x *ResponseTagList) ProtoReflect() protoreflect.Message {
mi := &file_mp4_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResponseTagList.ProtoReflect.Descriptor instead.
func (*ResponseTagList) Descriptor() ([]byte, []int) {
return file_mp4_proto_rawDescGZIP(), []int{14}
}
func (x *ResponseTagList) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *ResponseTagList) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *ResponseTagList) GetList() []*TagInfo {
if x != nil {
return x.List
}
return nil
}
func (x *ResponseTagList) GetTotal() uint32 {
if x != nil {
return x.Total
}
return 0
}
var File_mp4_proto protoreflect.FileDescriptor
const file_mp4_proto_rawDesc = "" +
@@ -645,7 +1114,50 @@ const file_mp4_proto_rawDesc = "" +
"\x12ResponseStopRecord\x12\x12\n" +
"\x04code\x18\x01 \x01(\x05R\x04code\x12\x18\n" +
"\amessage\x18\x02 \x01(\tR\amessage\x12\x12\n" +
"\x04data\x18\x03 \x01(\x04R\x04data2\xca\x04\n" +
"\x04data\x18\x03 \x01(\x04R\x04data\"\xa9\x01\n" +
"\aTagInfo\x12\x0e\n" +
"\x02id\x18\x01 \x01(\rR\x02id\x12\x18\n" +
"\atagName\x18\x02 \x01(\tR\atagName\x12\x1e\n" +
"\n" +
"streamPath\x18\x03 \x01(\tR\n" +
"streamPath\x12\x18\n" +
"\atagTime\x18\x04 \x01(\tR\atagTime\x12\x1c\n" +
"\tcreatedAt\x18\x05 \x01(\tR\tcreatedAt\x12\x1c\n" +
"\tupdatedAt\x18\x06 \x01(\tR\tupdatedAt\"b\n" +
"\fReqCreateTag\x12\x18\n" +
"\atagName\x18\x01 \x01(\tR\atagName\x12\x1e\n" +
"\n" +
"streamPath\x18\x02 \x01(\tR\n" +
"streamPath\x12\x18\n" +
"\atagTime\x18\x03 \x01(\tR\atagTime\"r\n" +
"\fReqUpdateTag\x12\x0e\n" +
"\x02id\x18\x01 \x01(\rR\x02id\x12\x18\n" +
"\atagName\x18\x02 \x01(\tR\atagName\x12\x1e\n" +
"\n" +
"streamPath\x18\x03 \x01(\tR\n" +
"streamPath\x12\x18\n" +
"\atagTime\x18\x04 \x01(\tR\atagTime\"\x1e\n" +
"\fReqDeleteTag\x12\x0e\n" +
"\x02id\x18\x01 \x01(\rR\x02id\"\x98\x01\n" +
"\n" +
"ReqListTag\x12\x1e\n" +
"\n" +
"streamPath\x18\x01 \x01(\tR\n" +
"streamPath\x12\x18\n" +
"\atagName\x18\x02 \x01(\tR\atagName\x12\x14\n" +
"\x05start\x18\x03 \x01(\tR\x05start\x12\x10\n" +
"\x03end\x18\x04 \x01(\tR\x03end\x12\x12\n" +
"\x04page\x18\x05 \x01(\rR\x04page\x12\x14\n" +
"\x05count\x18\x06 \x01(\rR\x05count\"]\n" +
"\vResponseTag\x12\x12\n" +
"\x04code\x18\x01 \x01(\x05R\x04code\x12\x18\n" +
"\amessage\x18\x02 \x01(\tR\amessage\x12 \n" +
"\x04data\x18\x03 \x01(\v2\f.mp4.TagInfoR\x04data\"w\n" +
"\x0fResponseTagList\x12\x12\n" +
"\x04code\x18\x01 \x01(\x05R\x04code\x12\x18\n" +
"\amessage\x18\x02 \x01(\tR\amessage\x12 \n" +
"\x04list\x18\x03 \x03(\v2\f.mp4.TagInfoR\x04list\x12\x14\n" +
"\x05total\x18\x04 \x01(\rR\x05total2\x94\a\n" +
"\x03api\x12]\n" +
"\x04List\x12\x12.mp4.ReqRecordList\x1a\x1a.global.RecordResponseList\"%\x82\xd3\xe4\x93\x02\x1f\x12\x1d/mp4/api/list/{streamPath=**}\x12T\n" +
"\aCatalog\x12\x16.google.protobuf.Empty\x1a\x17.global.ResponseCatalog\"\x18\x82\xd3\xe4\x93\x02\x12\x12\x10/mp4/api/catalog\x12b\n" +
@@ -654,7 +1166,11 @@ const file_mp4_proto_rawDesc = "" +
"EventStart\x12\x13.mp4.ReqEventRecord\x1a\x18.mp4.ResponseEventRecord\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/mp4/api/event/start\x12g\n" +
"\vStartRecord\x12\x13.mp4.ReqStartRecord\x1a\x18.mp4.ResponseStartRecord\")\x82\xd3\xe4\x93\x02#:\x01*\"\x1e/mp4/api/start/{streamPath=**}\x12c\n" +
"\n" +
"StopRecord\x12\x12.mp4.ReqStopRecord\x1a\x17.mp4.ResponseStopRecord\"(\x82\xd3\xe4\x93\x02\":\x01*\"\x1d/mp4/api/stop/{streamPath=**}B\x1bZ\x19m7s.live/v5/plugin/mp4/pbb\x06proto3"
"StopRecord\x12\x12.mp4.ReqStopRecord\x1a\x17.mp4.ResponseStopRecord\"(\x82\xd3\xe4\x93\x02\":\x01*\"\x1d/mp4/api/stop/{streamPath=**}\x12M\n" +
"\tCreateTag\x12\x11.mp4.ReqCreateTag\x1a\x10.mp4.ResponseTag\"\x1b\x82\xd3\xe4\x93\x02\x15:\x01*\"\x10/mp4/api/tag/add\x12U\n" +
"\tUpdateTag\x12\x11.mp4.ReqUpdateTag\x1a\x10.mp4.ResponseTag\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/mp4/api/tag/update/{id}\x12U\n" +
"\tDeleteTag\x12\x11.mp4.ReqDeleteTag\x1a\x10.mp4.ResponseTag\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/mp4/api/tag/delete/{id}\x12K\n" +
"\aListTag\x12\x0f.mp4.ReqListTag\x1a\x14.mp4.ResponseTagList\"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/mp4/api/tag/listB\x1bZ\x19m7s.live/v5/plugin/mp4/pbb\x06proto3"
var (
file_mp4_proto_rawDescOnce sync.Once
@@ -668,7 +1184,7 @@ func file_mp4_proto_rawDescGZIP() []byte {
return file_mp4_proto_rawDescData
}
var file_mp4_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_mp4_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
var file_mp4_proto_goTypes = []any{
(*ReqRecordList)(nil), // 0: mp4.ReqRecordList
(*ReqRecordDelete)(nil), // 1: mp4.ReqRecordDelete
@@ -678,31 +1194,48 @@ var file_mp4_proto_goTypes = []any{
(*ResponseStartRecord)(nil), // 5: mp4.ResponseStartRecord
(*ReqStopRecord)(nil), // 6: mp4.ReqStopRecord
(*ResponseStopRecord)(nil), // 7: mp4.ResponseStopRecord
(*durationpb.Duration)(nil), // 8: google.protobuf.Duration
(*emptypb.Empty)(nil), // 9: google.protobuf.Empty
(*pb.RecordResponseList)(nil), // 10: global.RecordResponseList
(*pb.ResponseCatalog)(nil), // 11: global.ResponseCatalog
(*pb.ResponseDelete)(nil), // 12: global.ResponseDelete
(*TagInfo)(nil), // 8: mp4.TagInfo
(*ReqCreateTag)(nil), // 9: mp4.ReqCreateTag
(*ReqUpdateTag)(nil), // 10: mp4.ReqUpdateTag
(*ReqDeleteTag)(nil), // 11: mp4.ReqDeleteTag
(*ReqListTag)(nil), // 12: mp4.ReqListTag
(*ResponseTag)(nil), // 13: mp4.ResponseTag
(*ResponseTagList)(nil), // 14: mp4.ResponseTagList
(*durationpb.Duration)(nil), // 15: google.protobuf.Duration
(*emptypb.Empty)(nil), // 16: google.protobuf.Empty
(*pb.RecordResponseList)(nil), // 17: global.RecordResponseList
(*pb.ResponseCatalog)(nil), // 18: global.ResponseCatalog
(*pb.ResponseDelete)(nil), // 19: global.ResponseDelete
}
var file_mp4_proto_depIdxs = []int32{
8, // 0: mp4.ReqStartRecord.fragment:type_name -> google.protobuf.Duration
0, // 1: mp4.api.List:input_type -> mp4.ReqRecordList
9, // 2: mp4.api.Catalog:input_type -> google.protobuf.Empty
1, // 3: mp4.api.Delete:input_type -> mp4.ReqRecordDelete
2, // 4: mp4.api.EventStart:input_type -> mp4.ReqEventRecord
4, // 5: mp4.api.StartRecord:input_type -> mp4.ReqStartRecord
6, // 6: mp4.api.StopRecord:input_type -> mp4.ReqStopRecord
10, // 7: mp4.api.List:output_type -> global.RecordResponseList
11, // 8: mp4.api.Catalog:output_type -> global.ResponseCatalog
12, // 9: mp4.api.Delete:output_type -> global.ResponseDelete
3, // 10: mp4.api.EventStart:output_type -> mp4.ResponseEventRecord
5, // 11: mp4.api.StartRecord:output_type -> mp4.ResponseStartRecord
7, // 12: mp4.api.StopRecord:output_type -> mp4.ResponseStopRecord
7, // [7:13] is the sub-list for method output_type
1, // [1:7] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
15, // 0: mp4.ReqStartRecord.fragment:type_name -> google.protobuf.Duration
8, // 1: mp4.ResponseTag.data:type_name -> mp4.TagInfo
8, // 2: mp4.ResponseTagList.list:type_name -> mp4.TagInfo
0, // 3: mp4.api.List:input_type -> mp4.ReqRecordList
16, // 4: mp4.api.Catalog:input_type -> google.protobuf.Empty
1, // 5: mp4.api.Delete:input_type -> mp4.ReqRecordDelete
2, // 6: mp4.api.EventStart:input_type -> mp4.ReqEventRecord
4, // 7: mp4.api.StartRecord:input_type -> mp4.ReqStartRecord
6, // 8: mp4.api.StopRecord:input_type -> mp4.ReqStopRecord
9, // 9: mp4.api.CreateTag:input_type -> mp4.ReqCreateTag
10, // 10: mp4.api.UpdateTag:input_type -> mp4.ReqUpdateTag
11, // 11: mp4.api.DeleteTag:input_type -> mp4.ReqDeleteTag
12, // 12: mp4.api.ListTag:input_type -> mp4.ReqListTag
17, // 13: mp4.api.List:output_type -> global.RecordResponseList
18, // 14: mp4.api.Catalog:output_type -> global.ResponseCatalog
19, // 15: mp4.api.Delete:output_type -> global.ResponseDelete
3, // 16: mp4.api.EventStart:output_type -> mp4.ResponseEventRecord
5, // 17: mp4.api.StartRecord:output_type -> mp4.ResponseStartRecord
7, // 18: mp4.api.StopRecord:output_type -> mp4.ResponseStopRecord
13, // 19: mp4.api.CreateTag:output_type -> mp4.ResponseTag
13, // 20: mp4.api.UpdateTag:output_type -> mp4.ResponseTag
13, // 21: mp4.api.DeleteTag:output_type -> mp4.ResponseTag
14, // 22: mp4.api.ListTag:output_type -> mp4.ResponseTagList
13, // [13:23] is the sub-list for method output_type
3, // [3:13] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_mp4_proto_init() }
@@ -716,7 +1249,7 @@ func file_mp4_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_mp4_proto_rawDesc), len(file_mp4_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumMessages: 15,
NumExtensions: 0,
NumServices: 1,
},

File diff suppressed because it is too large Load Diff

View File

@@ -41,6 +41,29 @@ service api {
body: "*"
};
}
rpc CreateTag (ReqCreateTag) returns (ResponseTag) {
option (google.api.http) = {
post: "/mp4/api/tag/add"
body: "*"
};
}
rpc UpdateTag (ReqUpdateTag) returns (ResponseTag) {
option (google.api.http) = {
post: "/mp4/api/tag/update/{id}"
body: "*"
};
}
rpc DeleteTag (ReqDeleteTag) returns (ResponseTag) {
option (google.api.http) = {
post: "/mp4/api/tag/delete/{id}"
body: "*"
};
}
rpc ListTag (ReqListTag) returns (ResponseTagList) {
option (google.api.http) = {
get: "/mp4/api/tag/list"
};
}
}
message ReqRecordList {
@@ -99,4 +122,53 @@ message ResponseStopRecord {
int32 code = 1;
string message = 2;
uint64 data = 3;
}
// 标签相关消息定义
message TagInfo {
uint32 id = 1;
string tagName = 2;
string streamPath = 3;
string tagTime = 4;
string createdAt = 5;
string updatedAt = 6;
}
message ReqCreateTag {
string tagName = 1;
string streamPath = 2;
string tagTime = 3;
}
message ReqUpdateTag {
uint32 id = 1;
string tagName = 2;
string streamPath = 3;
string tagTime = 4;
}
message ReqDeleteTag {
uint32 id = 1;
}
message ReqListTag {
string streamPath = 1;
string tagName = 2;
string start = 3;
string end = 4;
uint32 page = 5;
uint32 count = 6;
}
message ResponseTag {
int32 code = 1;
string message = 2;
TagInfo data = 3;
}
message ResponseTagList {
int32 code = 1;
string message = 2;
repeated TagInfo list = 3;
uint32 total = 4;
}

View File

@@ -27,6 +27,10 @@ const (
Api_EventStart_FullMethodName = "/mp4.api/EventStart"
Api_StartRecord_FullMethodName = "/mp4.api/StartRecord"
Api_StopRecord_FullMethodName = "/mp4.api/StopRecord"
Api_CreateTag_FullMethodName = "/mp4.api/CreateTag"
Api_UpdateTag_FullMethodName = "/mp4.api/UpdateTag"
Api_DeleteTag_FullMethodName = "/mp4.api/DeleteTag"
Api_ListTag_FullMethodName = "/mp4.api/ListTag"
)
// ApiClient is the client API for Api service.
@@ -39,6 +43,10 @@ type ApiClient interface {
EventStart(ctx context.Context, in *ReqEventRecord, opts ...grpc.CallOption) (*ResponseEventRecord, error)
StartRecord(ctx context.Context, in *ReqStartRecord, opts ...grpc.CallOption) (*ResponseStartRecord, error)
StopRecord(ctx context.Context, in *ReqStopRecord, opts ...grpc.CallOption) (*ResponseStopRecord, error)
CreateTag(ctx context.Context, in *ReqCreateTag, opts ...grpc.CallOption) (*ResponseTag, error)
UpdateTag(ctx context.Context, in *ReqUpdateTag, opts ...grpc.CallOption) (*ResponseTag, error)
DeleteTag(ctx context.Context, in *ReqDeleteTag, opts ...grpc.CallOption) (*ResponseTag, error)
ListTag(ctx context.Context, in *ReqListTag, opts ...grpc.CallOption) (*ResponseTagList, error)
}
type apiClient struct {
@@ -109,6 +117,46 @@ func (c *apiClient) StopRecord(ctx context.Context, in *ReqStopRecord, opts ...g
return out, nil
}
func (c *apiClient) CreateTag(ctx context.Context, in *ReqCreateTag, opts ...grpc.CallOption) (*ResponseTag, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResponseTag)
err := c.cc.Invoke(ctx, Api_CreateTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) UpdateTag(ctx context.Context, in *ReqUpdateTag, opts ...grpc.CallOption) (*ResponseTag, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResponseTag)
err := c.cc.Invoke(ctx, Api_UpdateTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) DeleteTag(ctx context.Context, in *ReqDeleteTag, opts ...grpc.CallOption) (*ResponseTag, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResponseTag)
err := c.cc.Invoke(ctx, Api_DeleteTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) ListTag(ctx context.Context, in *ReqListTag, opts ...grpc.CallOption) (*ResponseTagList, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResponseTagList)
err := c.cc.Invoke(ctx, Api_ListTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ApiServer is the server API for Api service.
// All implementations must embed UnimplementedApiServer
// for forward compatibility.
@@ -119,6 +167,10 @@ type ApiServer interface {
EventStart(context.Context, *ReqEventRecord) (*ResponseEventRecord, error)
StartRecord(context.Context, *ReqStartRecord) (*ResponseStartRecord, error)
StopRecord(context.Context, *ReqStopRecord) (*ResponseStopRecord, error)
CreateTag(context.Context, *ReqCreateTag) (*ResponseTag, error)
UpdateTag(context.Context, *ReqUpdateTag) (*ResponseTag, error)
DeleteTag(context.Context, *ReqDeleteTag) (*ResponseTag, error)
ListTag(context.Context, *ReqListTag) (*ResponseTagList, error)
mustEmbedUnimplementedApiServer()
}
@@ -147,6 +199,18 @@ func (UnimplementedApiServer) StartRecord(context.Context, *ReqStartRecord) (*Re
func (UnimplementedApiServer) StopRecord(context.Context, *ReqStopRecord) (*ResponseStopRecord, error) {
return nil, status.Errorf(codes.Unimplemented, "method StopRecord not implemented")
}
func (UnimplementedApiServer) CreateTag(context.Context, *ReqCreateTag) (*ResponseTag, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateTag not implemented")
}
func (UnimplementedApiServer) UpdateTag(context.Context, *ReqUpdateTag) (*ResponseTag, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateTag not implemented")
}
func (UnimplementedApiServer) DeleteTag(context.Context, *ReqDeleteTag) (*ResponseTag, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteTag not implemented")
}
func (UnimplementedApiServer) ListTag(context.Context, *ReqListTag) (*ResponseTagList, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTag not implemented")
}
func (UnimplementedApiServer) mustEmbedUnimplementedApiServer() {}
func (UnimplementedApiServer) testEmbeddedByValue() {}
@@ -276,6 +340,78 @@ func _Api_StopRecord_Handler(srv interface{}, ctx context.Context, dec func(inte
return interceptor(ctx, in, info, handler)
}
func _Api_CreateTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReqCreateTag)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).CreateTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Api_CreateTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).CreateTag(ctx, req.(*ReqCreateTag))
}
return interceptor(ctx, in, info, handler)
}
func _Api_UpdateTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReqUpdateTag)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).UpdateTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Api_UpdateTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).UpdateTag(ctx, req.(*ReqUpdateTag))
}
return interceptor(ctx, in, info, handler)
}
func _Api_DeleteTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReqDeleteTag)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).DeleteTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Api_DeleteTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).DeleteTag(ctx, req.(*ReqDeleteTag))
}
return interceptor(ctx, in, info, handler)
}
func _Api_ListTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReqListTag)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).ListTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Api_ListTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).ListTag(ctx, req.(*ReqListTag))
}
return interceptor(ctx, in, info, handler)
}
// Api_ServiceDesc is the grpc.ServiceDesc for Api service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -307,6 +443,22 @@ var Api_ServiceDesc = grpc.ServiceDesc{
MethodName: "StopRecord",
Handler: _Api_StopRecord_Handler,
},
{
MethodName: "CreateTag",
Handler: _Api_CreateTag_Handler,
},
{
MethodName: "UpdateTag",
Handler: _Api_UpdateTag_Handler,
},
{
MethodName: "DeleteTag",
Handler: _Api_DeleteTag_Handler,
},
{
MethodName: "ListTag",
Handler: _Api_ListTag_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "mp4.proto",

View File

@@ -0,0 +1,23 @@
package mp4
import (
"time"
"gorm.io/gorm"
)
// TagModel 标签模型
type TagModel struct {
ID uint `gorm:"primarykey" json:"id"`
TagName string `json:"tagName" gorm:"type:varchar(255);comment:标签名称"`
StreamPath string `json:"streamPath" gorm:"type:varchar(255);comment:流路径"`
TagTime time.Time `json:"tagTime" gorm:"comment:标签时间"`
CreatedAt time.Time `json:"createdAt" gorm:"comment:创建时间"`
UpdatedAt time.Time `json:"updatedAt" gorm:"comment:修改时间"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt,omitempty"`
}
// TableName 指定数据库表名
func (d *TagModel) TableName() string {
return "record_tag"
}