feat(core): 优化媒体文件扫描和处理功能

- 添加跳过ffprobe相关文件的逻辑,避免处理临时释放文件
- 修复图片处理中的变量引用错误
- 实现大文件指纹复用机制,避免重复全量读取计算MD5
- 新增文件头尾采样指纹算法,提升大文件处理性能
- 重构ffprobe内嵌资源管理,支持多平台自动选择对应版本
- 添加媒体信息数据库的指纹索引和去向跟踪功能
- 优化程序启动流程,失败时跳过媒体整理但保留图片处理
- 更新文档说明多平台ffprobe支持和构建要求
This commit is contained in:
2025-03-21 21:00:59 +08:00
parent b276e8c56f
commit 066e28a2d6
16 changed files with 156 additions and 45 deletions
+45 -15
View File
@@ -2,6 +2,7 @@ package store
import (
"database/sql"
"path/filepath"
"time"
sqlite "github.com/glebarez/go-sqlite"
@@ -19,21 +20,25 @@ func init() {
// MediaInfo 媒体文件信息记录,按字段拆分存放,便于查询
type MediaInfo struct {
Id int64 `xorm:"pk autoincr 'id'"`
FileMd5 string `xorm:"char(32) index 'file_md5'"` // 文件MD5,唯一标识
FilePath string `xorm:"text 'file_path'"` // 扫描时的源路径
FileName string `xorm:"varchar(255) 'file_name'"` // 文件名(含扩展名)
FileExt string `xorm:"varchar(32) 'file_ext'"` // 扩展名
FileSize int64 `xorm:"bigint 'file_size'"` // 文件大小(字节)
MimeType string `xorm:"varchar(128) 'mime_type'"` // MIME类型
MediaType string `xorm:"varchar(16) index 'media_type'"` // image/video/audio
Width int `xorm:"int index 'width'"` // 宽(像素)
Height int `xorm:"int index 'height'"` // 高(像素)
Duration float64 `xorm:"real 'duration'"` // 时长(秒)
Codec string `xorm:"varchar(64) 'codec'"` // 视频编码,如h264/hevc
Format string `xorm:"varchar(64) 'format'"` // 容器格式,如mp4/mkv
RawInfo string `xorm:"text 'raw_info'"` // ffprobe原始JSON(完整信息兜底)
CreatedAt time.Time `xorm:"created 'created_at'"` // 首次入库时间
UpdatedAt time.Time `xorm:"updated 'updated_at'"` // 最近更新时间
FileMd5 string `xorm:"char(32) index 'file_md5'"` // 文件MD5,唯一标识
FilePath string `xorm:"text 'file_path'"` // 扫描时的源路径
FileName string `xorm:"varchar(255) 'file_name'"` // 文件名(含扩展名)
FileExt string `xorm:"varchar(32) 'file_ext'"` // 扩展名
FileSize int64 `xorm:"bigint 'file_size'"` // 文件大小(字节)
MimeType string `xorm:"varchar(128) 'mime_type'"` // MIME类型
MediaType string `xorm:"varchar(16) index 'media_type'"` // image/video/audio
Width int `xorm:"int index 'width'"` // 宽(像素)
Height int `xorm:"int index 'height'"` // 高(像素)
Duration float64 `xorm:"real 'duration'"` // 时长(秒)
Codec string `xorm:"varchar(64) 'codec'"` // 视频编码,如h264/hevc
Format string `xorm:"varchar(64) 'format'"` // 容器格式,如mp4/mkv
RawInfo string `xorm:"text 'raw_info'"` // ffprobe原始JSON(完整信息兜底)
HeadMd5 string `xorm:"char(32) index('idx_fp') 'head_md5'"` // 文件头256KB的MD5(指纹加速)
TailMd5 string `xorm:"char(32) index('idx_fp') 'tail_md5'"` // 文件尾256KB的MD5(指纹加速)
Category string `xorm:"varchar(64) index 'category'"` // 归类的目录名(去向)
DestDir string `xorm:"text 'dest_dir'"` // 移动后的目录路径(去向)
CreatedAt time.Time `xorm:"created 'created_at'"` // 首次入库时间
UpdatedAt time.Time `xorm:"updated 'updated_at'"` // 最近更新时间
}
var engine *xorm.Engine
@@ -78,3 +83,28 @@ func SaveMedia(info *MediaInfo) error {
_, err = engine.Insert(info)
return err
}
// FindMediaByFingerprint 按 (文件大小 + 头256KB + 尾256KB) 查找已入库记录,避免对大文件重复计算全量MD5
func FindMediaByFingerprint(size int64, headMd5 string, tailMd5 string) (*MediaInfo, bool) {
if engine == nil || headMd5 == "" || tailMd5 == "" {
return nil, false
}
info := new(MediaInfo)
has, err := engine.Where("file_size = ? AND head_md5 = ? AND tail_md5 = ?", size, headMd5, tailMd5).Get(info)
if err != nil || !has {
return nil, false
}
return info, true
}
// UpdateMediaDest 按源路径回填文件的归类目录与去向
func UpdateMediaDest(filePath string, destDir string) error {
if engine == nil {
return nil
}
_, err := engine.Where("file_path = ?", filePath).Update(&MediaInfo{
Category: filepath.Base(destDir),
DestDir: destDir,
})
return err
}