Files
monibuca/plugin/mp4/exception.go
2025-09-26 15:57:26 +08:00

191 lines
7.2 KiB
Go
Raw 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.

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)
}
}
}
}