mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 07:42:07 +08:00
191 lines
7.2 KiB
Go
191 lines
7.2 KiB
Go
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)
|
||
}
|
||
}
|
||
}
|
||
}
|