package plugin_mp4 import ( "os" "path/filepath" "strings" "time" task "github.com/langhuihui/gotask" "github.com/shirou/gopsutil/v4/disk" "gorm.io/gorm" "m7s.live/v5" ) // mysql数据库里Exception 定义异常结构体 type Exception struct { CreateTime string `json:"createTime" gorm:"type:varchar(50)"` AlarmType string `json:"alarmType" gorm:"type:varchar(50)"` AlarmDesc string `json:"alarmDesc" gorm:"type:varchar(50)"` ServerIP string `json:"serverIP" gorm:"type:varchar(50)"` StreamPath string `json:"streamPath" gorm:"type:varchar(50)"` } // // 向第三方发送异常报警 // func (p *MP4Plugin) SendToThirdPartyAPI(exception *Exception) { // exception.CreateTime = time.Now().Format("2006-01-02 15:04:05") // exception.ServerIP = p.GetCommonConf().PublicIP // data, err := json.Marshal(exception) // if err != nil { // p.Error("SendToThirdPartyAPI", " marshalling exception error", err.Error()) // return // } // err = p.DB.Create(&exception).Error // if err != nil { // p.Error("SendToThirdPartyAPI", "insert into db error", err.Error()) // return // } // resp, err := http.Post(p.ExceptionPostUrl, "application/json", bytes.NewBuffer(data)) // if err != nil { // p.Error("SendToThirdPartyAPI", "Error sending exception to third party API error", err.Error()) // return // } // defer resp.Body.Close() // if resp.StatusCode != http.StatusOK { // p.Error("SendToThirdPartyAPI", "Failed to send exception, status code:", resp.StatusCode) // } else { // p.Info("SendToThirdPartyAPI", "Exception sent successfully!") // } // } // // 磁盘超上限报警 // func (p *DeleteRecordTask) getDisckException(streamPath string) bool { // if p.getDiskOutOfSpace(p.DiskMaxPercent) { // exceptionChannel <- &Exception{AlarmType: "disk alarm", AlarmDesc: "disk is full", StreamPath: streamPath} // return true // } // return false // } // 判断磁盘使用量是否中超限 func (p *DeleteRecordTask) getDiskOutOfSpace(filePath string) bool { exePath := filepath.Dir(filePath) d, err := disk.Usage(exePath) if err != nil || d == nil { p.Error("getDiskOutOfSpace", "error", err) return false } p.plugin.Debug("getDiskOutOfSpace", "current path", exePath, "disk UsedPercent", d.UsedPercent, "total disk space", d.Total, "disk free", d.Free, "disk usage", d.Used, "AutoOverWriteDiskPercent", p.AutoOverWriteDiskPercent, "DiskMaxPercent", p.DiskMaxPercent) return d.UsedPercent >= p.AutoOverWriteDiskPercent } func (p *DeleteRecordTask) deleteOldestFile() { //当当前磁盘使用量大于AutoOverWriteDiskPercent自动覆盖磁盘使用量配置时,自动删除最旧的文件 //连续录像删除最旧的文件 // 创建一个数组来存储所有的conf.FilePath var filePaths []string if len(p.plugin.GetCommonConf().OnPub.Record) > 0 { for _, conf := range p.plugin.GetCommonConf().OnPub.Record { // 处理路径,去掉最后的/$0部分,只保留目录部分 dirPath := filepath.Dir(conf.FilePath) p.Info("deleteOldestFile", "original filepath", conf.FilePath, "processed filepath", dirPath) filePaths = append(filePaths, dirPath) } } p.Debug("deleteOldestFile", "after get onpub.record,filePaths.length", len(filePaths)) if p.plugin.EventRecordFilePath != "" { // 同样处理EventRecordFilePath dirPath := filepath.Dir(p.plugin.EventRecordFilePath) filePaths = append(filePaths, dirPath) } p.Debug("deleteOldestFile", "after get eventrecordfilepath,filePaths.length", len(filePaths)) for _, filePath := range filePaths { for p.getDiskOutOfSpace(filePath) { var recordStreams []m7s.RecordStream // 使用不同的方法进行路径匹配,避免ESCAPE语法问题 // 解决方案:用MySQL能理解的简单方式匹配路径前缀 basePath := filePath // 直接替换所有反斜杠,不需要判断是否包含 basePath = strings.Replace(basePath, "\\", "\\\\", -1) searchPattern := basePath + "%" p.Info("deleteOldestFile", "searching with path pattern", searchPattern) err := p.DB.Where(" record_level!='high' AND end_time IS NOT NULL"). Where("file_path LIKE ?", searchPattern). Order("end_time ASC").Limit(1).Find(&recordStreams).Error if err == nil { if len(recordStreams) > 0 { p.Info("deleteOldestFile", "found %d records", len(recordStreams)) for _, record := range recordStreams { p.Info("deleteOldestFile", "ready to delete oldestfile,ID", record.ID, "create time", record.EndTime, "filepath", record.FilePath) err = os.Remove(record.FilePath) if err != nil { // 检查是否为文件不存在的错误 if os.IsNotExist(err) { // 文件不存在,记录日志但视为删除成功 p.Warn("deleteOldestFile", "file does not exist, continuing with database deletion", record.FilePath) // 继续删除数据库记录 err = p.DB.Delete(&record).Error if err != nil { p.Error("deleteOldestFile", "delete record from db error", err) } } else { // 其他错误,记录并跳过此记录 p.Error("deleteOldestFile", "delete file from disk error", err) continue } } else { // 文件删除成功,继续删除数据库记录 err = p.DB.Delete(&record).Error if err != nil { p.Error("deleteOldestFile", "delete record from db error", err) } } } } } else { p.Error("deleteOldestFile", "search record from db error", err) } time.Sleep(time.Second * 3) } } } type DeleteRecordTask struct { task.TickTask DiskMaxPercent float64 AutoOverWriteDiskPercent float64 RecordFileExpireDays int DB *gorm.DB plugin *MP4Plugin } func (t *DeleteRecordTask) GetTickInterval() time.Duration { return 1 * time.Minute } func (t *DeleteRecordTask) Tick(any) { t.deleteOldestFile() if t.RecordFileExpireDays <= 0 { return } //搜索event_records表中event_id值为0的(非事件)录像,并将其create_time与当前时间比对,大于RecordFileExpireDays则进行删除,数据库标记is_delete为1,磁盘上删除录像文件 var records []m7s.RecordStream expireTime := time.Now().AddDate(0, 0, -t.RecordFileExpireDays) t.Debug("RecordFileExpireDays is set to auto delete oldestfile", "expireTime", expireTime.Format("2006-01-02 15:04:05")) err := t.DB.Find(&records, "end_time < ? AND end_time IS NOT NULL", expireTime).Error if err == nil { for _, record := range records { t.Info("RecordFileExpireDays is set to auto delete oldestfile", "ID", record.ID, "create time", record.EndTime, "filepath", record.FilePath) err = os.Remove(record.FilePath) if err != nil { // 检查是否为文件不存在的错误 if os.IsNotExist(err) { // 文件不存在,记录日志但视为删除成功 t.Warn("RecordFileExpireDays set to auto delete oldestfile", "file does not exist, continuing with database deletion", record.FilePath) } else { // 其他错误,记录但继续处理 t.Error("RecordFileExpireDays set to auto delete oldestfile", "delete file from disk error", err) } } // 无论文件是否存在,都删除数据库记录 err = t.DB.Delete(&record).Error if err != nil { t.Error("RecordFileExpireDays set to auto delete oldestfile", "delete record from db error", err) } } } }