feat(core): 添加PSD/PSB图片格式支持并重构媒体文件处理

- 新增ReadPsdImage函数支持PSD/PSB格式图片信息读取
- 修改文件扫描逻辑,将视频处理改为媒体候选处理,使用ffprobe统一判定
- 添加对PSB格式的支持,与PSD共享相同8BPS文件头处理
- 重构handle_image.go中的图片处理逻辑,优化错误处理和记录保存
- 更新文件扫描过滤规则,支持更多图片格式并跳过数据库文件
- 添加文件MD5计算功能用于文件唯一性标识
- 修改媒体处理流程,支持音频文件识别和分类处理
- 移除废弃的视频时长转换函数和相关依赖
- 更新依赖版本并添加SQLite支持用于媒体信息存储
This commit is contained in:
2025-03-18 21:04:15 +08:00
parent f5e3f6b4ef
commit b276e8c56f
16 changed files with 770 additions and 311 deletions
+42 -47
View File
@@ -6,57 +6,60 @@ import (
"kenaito-media-picker/vars"
"os"
"strings"
"time"
)
func DoHandleVideo(pickerDir string) {
// 释放ffprobe
readerFileName := "./ffprobe.exe"
if util.CheckFileIsExist(readerFileName) {
_ = os.Remove(readerFileName)
}
err := util.WriteByteArraysToFile(vars.VideoDecodeToolWin64, readerFileName)
if err != nil {
fmt.Println("=== 释放解码器失败, 5秒后将自动退出", err)
time.Sleep(time.Second * 5)
return
}
total := len(vars.GlobalVideoPathList) // 总数
successCount := 0 // 成功数
errorCount := 0 // 失败数
ignoreCount := 0 // 忽略数
for _, videoFilePath := range vars.GlobalVideoPathList {
suffix := vars.GlobalFilePath2FileExtMap[videoFilePath]
if util.IsSupportVideo(suffix) {
width, height, err := util.ReadVideoWidthHeight(videoFilePath)
if err == nil {
successCount = successCount + 1
vars.VideoPath2WidthHeightMap[videoFilePath] = fmt.Sprintf("%d-%d", width, height)
vars.VideoPath2WidthHeightTagMap[videoFilePath] = fmt.Sprintf("[%dx%d]", width, height)
fmt.Printf("=== Video总数: %d, 已读取Info: %d, 成功数: %d, 失败数: %d \n", total, successCount+errorCount+ignoreCount, successCount, errorCount)
duration := util.ReadVideoDuration(videoFilePath)
if duration == 0 {
vars.VideoPath2DurationMap[videoFilePath] = "0H0M0S"
} else {
vars.VideoPath2DurationMap[videoFilePath] = util.SecondsToHms(duration)
}
} else {
// DoHandleMedia 处理所有媒体候选文件:ffprobe判定视频/音频/不支持
func DoHandleMedia(pickerDir string) {
total := len(vars.GlobalMediaPathList)
videoCount := 0 // 视频数
audioCount := 0 // 音频数
errorCount := 0 // 异常数
ignoreCount := 0
for _, mediaFilePath := range vars.GlobalMediaPathList {
fileMime := vars.GlobalFilePath2MimeInfoMap[mediaFilePath]
probe, err := util.ProbeMediaFile(util.FFprobeExePath(), mediaFilePath)
if err != nil {
if strings.Contains(fileMime, "video/") {
// mime是视频但ffprobe读不出来,视为损坏/异常视频
errorCount = errorCount + 1
vars.VideoReadErrorPathList = append(vars.VideoReadErrorPathList, videoFilePath)
fmt.Printf("=== 异常视频: %s \n", videoFilePath)
vars.VideoReadErrorPathList = append(vars.VideoReadErrorPathList, mediaFilePath)
fmt.Printf("=== 异常视频: %s \n", mediaFilePath)
} else {
ignoreCount = ignoreCount + 1
vars.VideoIgnorePathList = append(vars.VideoIgnorePathList, mediaFilePath)
}
continue
}
// 其他的直接先忽略吧, 爱改改, 不改拉倒
width, height := probe.VideoResolution()
if width > 0 && height > 0 { // 有视频流
videoCount = videoCount + 1
vars.VideoPath2WidthHeightMap[mediaFilePath] = fmt.Sprintf("%d-%d", width, height)
saveMediaRecord(mediaFilePath, "video", width, height, probe.DurationSeconds(), probe.VideoCodec(), probe.FormatName(), probe.RawJSON())
fmt.Printf("=== 媒体总数: %d, 已处理: %d, 视频: %d, 音频: %d, 异常: %d, 忽略: %d \n", total, videoCount+audioCount+errorCount+ignoreCount, videoCount, audioCount, errorCount, ignoreCount)
continue
}
if probe.HasAudio() { // 仅有音频流
audioCount = audioCount + 1
vars.GlobalAudioPathList = append(vars.GlobalAudioPathList, mediaFilePath)
saveMediaRecord(mediaFilePath, "audio", 0, 0, probe.DurationSeconds(), probe.AudioCodec(), probe.FormatName(), probe.RawJSON())
continue
}
// 既无视频也无音频(字幕流等)
ignoreCount = ignoreCount + 1
vars.VideoIgnorePathList = append(vars.VideoIgnorePathList, videoFilePath)
vars.VideoIgnorePathList = append(vars.VideoIgnorePathList, mediaFilePath)
}
if len(vars.VideoReadErrorPathList) > 0 {
readInfoErrorPath := pickerDir + string(os.PathSeparator) + "读取异常"
readInfoErrorPath := pickerDir + string(os.PathSeparator) + "视频_读取异常"
if util.CreateDir(readInfoErrorPath) {
util.DoMoveFileToDir(vars.VideoReadErrorPathList, readInfoErrorPath)
}
}
if len(vars.GlobalAudioPathList) > 0 {
audioPath := pickerDir + string(os.PathSeparator) + "音频"
if util.CreateDir(audioPath) {
util.DoMoveFileToDir(vars.GlobalAudioPathList, audioPath)
}
}
if len(vars.VideoIgnorePathList) > 0 {
ignorePath := pickerDir + string(os.PathSeparator) + "已忽略"
if util.CreateDir(ignorePath) {
@@ -64,21 +67,13 @@ func DoHandleVideo(pickerDir string) {
}
}
doPickVideoFile(pickerDir, vars.VideoPath2WidthHeightMap)
// 删除ffprobe
if util.CheckFileIsExist(readerFileName) {
_ = os.Remove(readerFileName)
}
fmt.Printf("=== 视频处理完毕 \n\n")
fmt.Printf("=== 视频/音频处理完毕 \n\n")
}
// 条件视频并分组存放
func doPickVideoFile(pickerDir string, videoPath2WidthHeightMap map[string]string) {
if len(videoPath2WidthHeightMap) == 0 {
fmt.Printf("=== 当前目录下没有扫描到视频文件, %s \n", pickerDir)
readerFileName := "./ffprobe.exe"
if util.CheckFileIsExist(readerFileName) {
_ = os.Remove(readerFileName)
}
return
}
for currentVideoPath, infoStr := range videoPath2WidthHeightMap {