From 4da4b738e6450b87f4115403c682a59b5b07ab65 Mon Sep 17 00:00:00 2001 From: Odboy Date: Wed, 26 Mar 2025 21:08:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8C=89=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 vars 全局状态,按职责拆分 internal/ 包:scanner/probe/classify/organize/mover/store/digest - 数据驱动归类规则替代图片/视频重复分支,store 改为结构体实例 - build.sh 按系统类型编译:Windows 用 upx 压缩,macOS/Linux 不压缩 - 忽略目录拆分:图片_已忽略 / 视频_已忽略 - 输出构建产物加入 .gitignore --- .gitignore | 9 +- README.md | 21 +- build.sh | 30 +- core/bmp.go | 114 --- core/file_scanner.go | 100 --- core/handle_image.go | 750 -------------------- core/handle_video.go | 718 ------------------- core/save_media.go | 57 -- core/webp.go | 25 - internal/classify/classify.go | 72 ++ internal/classify/standard.go | 52 ++ internal/digest/digest.go | 53 ++ internal/mover/mover.go | 88 +++ internal/organize/organize.go | 181 +++++ internal/probe/bmp.go | 52 ++ internal/probe/ffprobe.go | 32 + internal/probe/ffprobe_embed_darwin.go | 8 + internal/probe/ffprobe_embed_unsupported.go | 6 + internal/probe/ffprobe_embed_windows.go | 8 + internal/probe/image.go | 76 ++ internal/probe/probe.go | 80 +++ internal/probe/psd.go | 32 + internal/probe/webp.go | 22 + internal/scanner/scanner.go | 138 ++++ {store => internal/store}/store.go | 53 +- main.go | 74 +- util/cale.go | 19 - util/file.go | 162 ----- util/image.go | 43 -- util/video.go | 221 ------ vars/ffprobe_darwin.go | 8 - vars/ffprobe_unsupported.go | 6 - vars/ffprobe_windows.go | 8 - vars/global.go | 11 - vars/image.go | 81 --- vars/video.go | 82 --- 36 files changed, 1016 insertions(+), 2476 deletions(-) delete mode 100644 core/bmp.go delete mode 100644 core/file_scanner.go delete mode 100644 core/handle_image.go delete mode 100644 core/handle_video.go delete mode 100644 core/save_media.go delete mode 100644 core/webp.go create mode 100644 internal/classify/classify.go create mode 100644 internal/classify/standard.go create mode 100644 internal/digest/digest.go create mode 100644 internal/mover/mover.go create mode 100644 internal/organize/organize.go create mode 100644 internal/probe/bmp.go create mode 100644 internal/probe/ffprobe.go create mode 100644 internal/probe/ffprobe_embed_darwin.go create mode 100644 internal/probe/ffprobe_embed_unsupported.go create mode 100644 internal/probe/ffprobe_embed_windows.go create mode 100644 internal/probe/image.go create mode 100644 internal/probe/probe.go create mode 100644 internal/probe/psd.go create mode 100644 internal/probe/webp.go create mode 100644 internal/scanner/scanner.go rename {store => internal/store}/store.go (70%) delete mode 100644 util/cale.go delete mode 100644 util/file.go delete mode 100644 util/image.go delete mode 100644 util/video.go delete mode 100644 vars/ffprobe_darwin.go delete mode 100644 vars/ffprobe_unsupported.go delete mode 100644 vars/ffprobe_windows.go delete mode 100644 vars/global.go delete mode 100644 vars/image.go delete mode 100644 vars/video.go diff --git a/.gitignore b/.gitignore index 8b87646..2cda855 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,11 @@ # Go workspace file go.work -# 内嵌的 ffprobe 二进制(约 80~150MB),不入库,需自行放置于 vars/bin/ -vars/bin/ +# 内嵌的 ffprobe 二进制(约 80~150MB),不入库,需自行放置于 internal/probe/bin/ +internal/probe/bin/ + +# 构建产物(build.sh 输出) +kenaito-media-picker +kenaito-media-picker.exe +kenaito-media-picker-release.exe diff --git a/README.md b/README.md index 928d5cb..7f5e31d 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ sh build.sh ``` - 依赖 Go 1.19+(`go.mod` 指定 1.19) -- 每个平台构建时只内嵌**本平台**的 ffprobe:`vars/bin/ffprobe_mac`(macOS)、`vars/bin/ffprobe.exe`(Windows),需提前放置(未入库,见 `.gitignore`) -- `build.sh` 使用 `upx` 压缩产物,输出 `kenaito-media-picker.exe`(Windows 单文件) -- 编译所需工具(ffmpeg / upx)压缩包已放在 `doc/` 目录,按需解压使用 +- 每个平台构建时只内嵌**本平台**的 ffprobe:`internal/probe/bin/ffprobe_mac`(macOS)、`internal/probe/bin/ffprobe.exe`(Windows),需提前放置(未入库,见 `.gitignore`) +- `build.sh` 按系统类型编译:**Windows** 下 `go build` 后使用 `upx` 压缩,输出 `kenaito-media-picker.exe`;**macOS / Linux** 直接构建,输出 `kenaito-media-picker`(不压缩) +- 编译所需工具(ffmpeg / upx)压缩包已放在 `doc/` 目录,按需解压使用(upx 仅 Windows 构建时需要,放置于项目根目录 `upx`) ## 使用方法 @@ -87,7 +87,7 @@ kenaito-media-picker.exe {uid}-视频_横屏_GIF / 视频_竖屏_GIF / 视频_等比_GIF 音频 ← 所有音频文件(ffmpeg 可识别的格式) 视频_读取异常 ← mime 为视频但 ffprobe 读取失败 -已忽略 ← 非媒体文件或无法识别 +视频_已忽略 ← 非媒体文件或无法识别 ``` > 视频时长与分辨率通过内嵌 ffprobe 读取(处理时会临时释放到当前目录、完成后自动删除);凡 ffmpeg 支持的视频/音频格式均可识别。 @@ -95,10 +95,15 @@ kenaito-media-picker.exe ## 目录结构 ```text -├── main.go # 程序入口(扫描 → 过滤 → 图片/媒体处理) -├── core/ # 核心逻辑:文件扫描、MIME 过滤、图片/视频/音频分组与移动 -├── util/ # 工具:MIME 识别、图片尺寸读取、ffprobe 封装、文件移动 -├── vars/ # 全局状态 + 按系统内嵌 ffprobe(go:embed,二进制在 vars/bin/) +├── main.go # 程序入口(装配与编排) +├── internal/ +│ ├── scanner/ # 文件扫描、MIME 读取、图片/媒体候选过滤 +│ ├── probe/ # 媒体探测:图片尺寸 + ffprobe 解析 + 内嵌解码器释放 +│ ├── classify/ # 纯函数归类规则(方向 + 标准分辨率 + K档) +│ ├── organize/ # 整理编排:探测→归类→收集→移动→记录去向 +│ ├── mover/ # 文件移动(rename / 跨盘 copy+delete) +│ ├── store/ # SQLite 媒体信息持久化 +│ └── digest/ # 文件 MD5 / 指纹采样 ├── doc/ # 构建依赖(ffmpeg / upx 压缩包) └── build.sh # 编译脚本(upx 压缩) ``` diff --git a/build.sh b/build.sh index 23e836f..1821e6b 100644 --- a/build.sh +++ b/build.sh @@ -1,7 +1,23 @@ -rm -f kenaito-media-picker.exe -rm -f kenaito-media-picker-release.exe -# build -go build -o kenaito-media-picker-release.exe main.go -# upx compress -./upx -o kenaito-media-picker.exe kenaito-media-picker-release.exe -rm -f kenaito-media-picker-release.exe +#!/bin/sh +# 构建脚本:按系统类型选择编译方式 +# Windows (MINGW/MSYS/CYGWIN): go build + upx 压缩 → kenaito-media-picker.exe +# macOS / Linux: 直接构建,不压缩 → kenaito-media-picker + +set -e + +case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*) + echo "=== Windows: go build + upx 压缩" + rm -f kenaito-media-picker.exe kenaito-media-picker-release.exe + go build -o kenaito-media-picker-release.exe . + ./upx -o kenaito-media-picker.exe kenaito-media-picker-release.exe + rm -f kenaito-media-picker-release.exe + ;; + *) + echo "=== 非 Windows (macOS/Linux): 直接构建,不压缩" + rm -f kenaito-media-picker + go build -o kenaito-media-picker . + ;; +esac + +echo "=== 构建完成" diff --git a/core/bmp.go b/core/bmp.go deleted file mode 100644 index 0613bcb..0000000 --- a/core/bmp.go +++ /dev/null @@ -1,114 +0,0 @@ -package core - -import ( - "encoding/binary" - "fmt" - "io" - "os" -) - -// BMPFileHeader represents the bitmap file header structure. -type BMPFileHeader struct { - FileType [2]byte - FileSize uint32 - Reserved1 uint16 - Reserved2 uint16 - DataOffset uint32 -} - -// BMPInfoHeader represents the bitmap info header structure. -type BMPInfoHeader struct { - Size uint32 - Width int32 - Height int32 - Planes uint16 - BitsPerPixel uint16 - Compression uint32 - SizeImage uint32 - XPelsPerMeter int32 - YPelsPerMeter int32 - ColorsUsed uint32 - ColorsImportant uint32 -} - -func ReadBmpImage(filePath string) (error, int32, int32) { - file, err := os.Open(filePath) - if err != nil { - fmt.Printf("=== Failed to open file: %v\n", err) - return err, 0, 0 - } - defer file.Close() - - // Read the file header. - var fileHeader BMPFileHeader - if err := binary.Read(file, binary.LittleEndian, &fileHeader); err != nil { - fmt.Printf("=== Failed to Read file: %v\n", err) - return err, 0, 0 - } - - // Check if it's a valid BMP file by verifying the file type. - if string(fileHeader.FileType[:]) != "BM" { - return fmt.Errorf("not a valid BMP file: file type is %q, expected \"BM\"", string(fileHeader.FileType[:])), 0, 0 - } - - // Read the info header. - var infoHeader BMPInfoHeader - if err := binary.Read(file, binary.LittleEndian, &infoHeader); err != nil { - fmt.Printf("=== Failed to Read file: %v\n", err) - return err, 0, 0 - } - // 自顶向下 BMP 的高度为负值,取绝对值避免影响后续解析 - return err, absInt32(infoHeader.Width), absInt32(infoHeader.Height) - //fmt.Printf("=== File size: %d bytes\n", fileHeader.FileSize) - //fmt.Printf("=== Image dimensions: %dx%d\n", ) - //fmt.Printf("=== Bits per pixel: %d\n", infoHeader.BitsPerPixel) - - // At this point, you would typically read the pixel data into a slice, - // taking into account any padding required for alignment and the specific - // pixel format (e.g., RGB, RGBA, indexed color). This part is omitted here. - - // You may also need to skip over a possible color palette if present. - // The size of the palette can be inferred from the number of colors used and - // the bits per pixel value. - - // For simplicity, we'll just read the rest of the file as raw bytes. - //data, err := ioutil.ReadAll(file) - //if err != nil { - // panic(err) - //} - //fmt.Printf("=== Read %d bytes of pixel data.\n", len(data)) -} - -// absInt32 返回 int32 的绝对值 -func absInt32(v int32) int32 { - if v < 0 { - return -v - } - return v -} - -// ReadPsdImage 读取PSD/PSB格式的图片信息(两者共享相同的8BPS文件头) -func ReadPsdImage(filePath string) (err error, width int, height int) { - file, err := os.Open(filePath) - if err != nil { - fmt.Printf("=== Failed to open file: %v\n", err) - return err, 0, 0 - } - defer file.Close() - - var header [26]byte - if _, err := io.ReadFull(file, header[:]); err != nil { - fmt.Printf("=== Failed to read PSD/PSB header: %v\n", err) - return err, 0, 0 - } - if string(header[0:4]) != "8BPS" { - return fmt.Errorf("not a valid PSD/PSB file: magic %q, expected \"8BPS\"", string(header[0:4])), 0, 0 - } - version := binary.BigEndian.Uint16(header[4:6]) - if version != 1 && version != 2 { - return fmt.Errorf("unsupported PSD/PSB version: %d", version), 0, 0 - } - height = int(binary.BigEndian.Uint32(header[14:18])) - width = int(binary.BigEndian.Uint32(header[18:22])) - return nil, width, height -} diff --git a/core/file_scanner.go b/core/file_scanner.go deleted file mode 100644 index 4b13b54..0000000 --- a/core/file_scanner.go +++ /dev/null @@ -1,100 +0,0 @@ -package core - -import ( - "fmt" - "github.com/redmask-hb/GoSimplePrint/goPrint" - "kenaito-media-picker/store" - "kenaito-media-picker/util" - "kenaito-media-picker/vars" - "log" - "os" - "path" - "path/filepath" - "strings" -) - -type FileScanner struct { -} - -func (FileScanner) DoScan(rootDir string) { - fmt.Printf("=== 开始扫描文件 \n") - if err := filepath.Walk(rootDir, visit); err != nil { - log.Fatal(err) - } - doReadFileMimeInfo() -} - -func doReadFileMimeInfo() { - total := len(vars.GlobalFilePathList) - fmt.Printf("=== 文件总数: %d \n", total) - // 扫描文件mime信息 - var count = 0 - bar := goPrint.NewBar(100) - bar.SetNotice("=== 扫描文件Mime:") - bar.SetGraph(">") - bar.SetNoticeColor(goPrint.FontColor.Red) - for _, currentPath := range vars.GlobalFilePathList { - // mime - mt := util.ReadFileMimeInfo(currentPath) - if mt != nil { - vars.GlobalFilePath2MimeInfoMap[currentPath] = mt.String() - } else { - vars.GlobalFilePath2MimeInfoMap[currentPath] = "" - } - count = count + 1 - bar.PrintBar(util.CalcPercentage(count, total)) - } - bar.PrintEnd("=== Finish") -} - -func (FileScanner) DoFilter() { - total := len(vars.GlobalFilePathList) - var count = 0 - bar := goPrint.NewBar(100) - bar.SetNotice("=== 过滤已支持的媒体:") - bar.SetGraph(">") - for _, globalFilePath := range vars.GlobalFilePathList { - fileMime := vars.GlobalFilePath2MimeInfoMap[globalFilePath] - count = count + 1 - // 图片 - ext := path.Ext(globalFilePath) - if strings.Contains(fileMime, "image/") || util.IsSupportImage(ext) || strings.EqualFold(ext, ".psd") { - vars.GlobalImagePathList = append(vars.GlobalImagePathList, globalFilePath) - bar.PrintBar(util.CalcPercentage(count, total)) - continue - } - // 其余作为媒体候选,由ffprobe最终判定(视频/音频/不支持), - // 只要ffmpeg支持的格式都会被识别,不再依赖扩展名白名单 - if util.IsPotentialMedia(fileMime) { - vars.GlobalMediaPathList = append(vars.GlobalMediaPathList, globalFilePath) - bar.PrintBar(util.CalcPercentage(count, total)) - continue - } - // 明显的非媒体文件(代码/文档/压缩包等)直接不处理 - } - bar.PrintEnd("=== Finish") -} - -// 定义walkFn回调函数visit -func visit(currentPath string, info os.FileInfo, err error) error { - if err != nil { - return err // 如果有错误,直接返回 - } - if info.IsDir() && vars.GlobalPickerDir != "" && currentPath == vars.GlobalPickerDir { - return filepath.SkipDir // 跳过白名单目录(已整理文件的输出目录) - } - if !info.IsDir() { - if filepath.Base(currentPath) == store.DBFileName { - return nil // 跳过媒体信息数据库文件 - } - if filepath.Base(currentPath) == util.FFprobeExeName || filepath.Base(currentPath) == "ffprobe_mac" { - return nil // 跳过ffprobe相关文件(中断残留的释放文件 / vars/bin内嵌源文件) - } - vars.GlobalFilePathList = append(vars.GlobalFilePathList, currentPath) - // filename, include ext - vars.GlobalFilePath2FileNameMap[currentPath] = filepath.Base(currentPath) - // file ext - vars.GlobalFilePath2FileExtMap[currentPath] = path.Ext(currentPath) - } - return nil -} diff --git a/core/handle_image.go b/core/handle_image.go deleted file mode 100644 index a2a95fe..0000000 --- a/core/handle_image.go +++ /dev/null @@ -1,750 +0,0 @@ -package core - -import ( - "fmt" - _ "image/gif" // 导入gif支持 - _ "image/jpeg" // 导入jpeg支持 - _ "image/png" // 导入png支持 - "kenaito-media-picker/util" - "kenaito-media-picker/vars" - "os" - "strings" -) - -func DoHandleImage(pickerDir string) { - total := len(vars.GlobalImagePathList) // 总数 - successCount := 0 // 成功数 - errorCount := 0 // 失败数 - ignoreCount := 0 // 忽略数 - for _, imageFilePath := range vars.GlobalImagePathList { - suffix := vars.GlobalFilePath2FileExtMap[imageFilePath] - if strings.EqualFold(suffix, ".webp") { // 特殊文件处理, webp为网络常用图片格式 - webpErr, webpWidth, webpHeight := ReadWebpImage(imageFilePath) - if webpErr == nil { - vars.ImagePath2WidthHeightMap[imageFilePath] = fmt.Sprintf("%d-%d", webpWidth, webpHeight) - saveMediaRecord(imageFilePath, "image", webpWidth, webpHeight, 0, "", "", "") - successCount = successCount + 1 - } else { - errorCount = errorCount + 1 - vars.ImageReadErrorPathList = append(vars.ImageReadErrorPathList, imageFilePath) - fmt.Printf("=== 异常图片: %s \n", imageFilePath) - } - continue - } - if strings.EqualFold(suffix, ".bmp") { // 特殊文件处理 - bpmErr, bmpWidth, bmpHeight := ReadBmpImage(imageFilePath) - if bpmErr == nil { - vars.ImagePath2WidthHeightMap[imageFilePath] = fmt.Sprintf("%d-%d", bmpWidth, bmpHeight) - saveMediaRecord(imageFilePath, "image", int(bmpWidth), int(bmpHeight), 0, "", "", "") - successCount = successCount + 1 - } else { - errorCount = errorCount + 1 - vars.ImageReadErrorPathList = append(vars.ImageReadErrorPathList, imageFilePath) - fmt.Printf("=== 异常图片: %s \n", imageFilePath) - } - continue - } - if strings.EqualFold(suffix, ".psd") { // 特殊文件处理 - vars.ImagePsdList = append(vars.ImagePsdList, imageFilePath) - successCount = successCount + 1 - continue - } - if strings.EqualFold(suffix, ".psb") { // 特殊文件处理, psb与psd共享文件头 - psbErr, psbWidth, psbHeight := ReadPsdImage(imageFilePath) - if psbErr == nil { - vars.ImagePath2WidthHeightMap[imageFilePath] = fmt.Sprintf("%d-%d", psbWidth, psbHeight) - saveMediaRecord(imageFilePath, "image", psbWidth, psbHeight, 0, "", "", "") - successCount = successCount + 1 - } else { - errorCount = errorCount + 1 - vars.ImageReadErrorPathList = append(vars.ImageReadErrorPathList, imageFilePath) - fmt.Printf("=== 异常图片: %s \n", imageFilePath) - } - continue - } - if util.IsSupportImage(suffix) { - err, width, height := util.ReadImageInfo(imageFilePath) - if err == nil { - successCount = successCount + 1 - vars.ImagePath2WidthHeightMap[imageFilePath] = fmt.Sprintf("%d-%d", width, height) - saveMediaRecord(imageFilePath, "image", width, height, 0, "", "", "") - fmt.Printf("=== Image总数: %d, 已读取Info: %d, 成功数: %d, 失败数: %d \n", total, successCount+errorCount+ignoreCount, successCount, errorCount) - } else if util.CheckFileIsExist(util.FFprobeExePath()) { - // Go标准库解码失败,尝试用ffprobe兜底(ffmpeg支持的图片格式更广,如tiff/svg等) - if probe, perr := util.ProbeMediaFile(util.FFprobeExePath(), imageFilePath); perr == nil { - if w, h := probe.VideoResolution(); w > 0 && h > 0 { - successCount = successCount + 1 - vars.ImagePath2WidthHeightMap[imageFilePath] = fmt.Sprintf("%d-%d", w, h) - saveMediaRecord(imageFilePath, "image", w, h, 0, probe.VideoCodec(), probe.FormatName(), probe.RawJSON()) - continue - } - } - errorCount = errorCount + 1 - vars.ImageReadErrorPathList = append(vars.ImageReadErrorPathList, imageFilePath) - fmt.Printf("=== 异常图片: %s \n", imageFilePath) - } else { - errorCount = errorCount + 1 - vars.ImageReadErrorPathList = append(vars.ImageReadErrorPathList, imageFilePath) - fmt.Printf("=== 异常图片: %s \n", imageFilePath) - } - continue - } - // 其他的直接先忽略吧, 爱改改, 不改拉倒 - ignoreCount = ignoreCount + 1 - vars.ImageIgnorePathList = append(vars.ImageIgnorePathList, imageFilePath) - } - if len(vars.ImagePsdList) > 0 { - psdImagePath := pickerDir + string(os.PathSeparator) + "图片_PSD" - if util.CreateDir(psdImagePath) { - util.DoMoveFileToDir(vars.ImagePsdList, psdImagePath) - } - } - if len(vars.ImageReadErrorPathList) > 0 { - readInfoErrorPath := pickerDir + string(os.PathSeparator) + "图片_读取异常" - if util.CreateDir(readInfoErrorPath) { - util.DoMoveFileToDir(vars.ImageReadErrorPathList, readInfoErrorPath) - } - } - if len(vars.ImageIgnorePathList) > 0 { - ignorePath := pickerDir + string(os.PathSeparator) + "图片_已忽略" - if util.CreateDir(ignorePath) { - util.DoMoveFileToDir(vars.ImageIgnorePathList, ignorePath) - } - } - doPickImageFile(pickerDir, vars.ImagePath2WidthHeightMap) - fmt.Printf("=== 图片处理完毕 \n\n") -} - -// 条件图片并分组存放 -func doPickImageFile(pickerDir string, imagePath2WidthHeightMap map[string]string) { - if len(imagePath2WidthHeightMap) == 0 { - fmt.Printf("=== 当前目录下没有扫描到图片文件, %s \n", pickerDir) - return - } - for currentImagePath, infoStr := range imagePath2WidthHeightMap { - width2Height := strings.Split(infoStr, "-") - width := util.String2int(width2Height[0]) - height := util.String2int(width2Height[1]) - suffix := vars.GlobalFilePath2FileExtMap[currentImagePath] - if strings.EqualFold(suffix, ".gif") { - handleGifImage(currentImagePath) - continue - } - if width > height { - handleHorizontalImage(currentImagePath, width, height) - continue - } - if width < height { - handleVerticalImage(currentImagePath, height) - continue - } - handleSquareImage(currentImagePath, width) - } - moveHorizontalImage(pickerDir) - moveVerticalImage(pickerDir) - moveSquareImage(pickerDir) - moveGifImage(pickerDir) -} - -// 统一处理gif -func handleGifImage(path string) { - vars.ImageGifList = append(vars.ImageGifList, path) -} - -// 处理垂直图片 -func handleVerticalImage(currentImagePath string, height int) { - // 非标规格 - if height < 1024 { - vars.ImageVerticalNormalList = append(vars.ImageVerticalNormalList, currentImagePath) - } else if height < 2048 { - vars.ImageVertical1KList = append(vars.ImageVertical1KList, currentImagePath) - } else if height < 3072 { - vars.ImageVertical2KList = append(vars.ImageVertical2KList, currentImagePath) - } else if height < 4096 { - vars.ImageVertical3KList = append(vars.ImageVertical3KList, currentImagePath) - } else if height < 5120 { - vars.ImageVertical4KList = append(vars.ImageVertical4KList, currentImagePath) - } else if height < 6144 { - vars.ImageVertical5KList = append(vars.ImageVertical5KList, currentImagePath) - } else if height < 7168 { - vars.ImageVertical6KList = append(vars.ImageVertical6KList, currentImagePath) - } else if height < 8192 { - vars.ImageVertical7KList = append(vars.ImageVertical7KList, currentImagePath) - } else if height < 9216 { - vars.ImageVertical8KList = append(vars.ImageVertical8KList, currentImagePath) - } else if height < 10240 { - vars.ImageVertical9KList = append(vars.ImageVertical9KList, currentImagePath) - } else if height < 11264 { - vars.ImageVertical10KList = append(vars.ImageVertical10KList, currentImagePath) - } else if height < 12288 { - vars.ImageVertical11KList = append(vars.ImageVertical11KList, currentImagePath) - } else if height < 13312 { - vars.ImageVertical12KList = append(vars.ImageVertical12KList, currentImagePath) - } else if height < 14336 { - vars.ImageVertical13KList = append(vars.ImageVertical13KList, currentImagePath) - } else if height < 15360 { - vars.ImageVertical14KList = append(vars.ImageVertical14KList, currentImagePath) - } else if height < 16384 { - vars.ImageVertical15KList = append(vars.ImageVertical15KList, currentImagePath) - } else if height < 17408 { - vars.ImageVertical16KList = append(vars.ImageVertical16KList, currentImagePath) - } else if height < 18432 { - vars.ImageVertical17KList = append(vars.ImageVertical17KList, currentImagePath) - } else if height < 19456 { - vars.ImageVertical18KList = append(vars.ImageVertical18KList, currentImagePath) - } else if height < 20480 { - vars.ImageVertical19KList = append(vars.ImageVertical19KList, currentImagePath) - } else if height >= 20480 { // 提示请忽略 - vars.ImageVerticalHKList = append(vars.ImageVerticalHKList, currentImagePath) - } -} - -// 处理横向图片 -func handleHorizontalImage(currentImagePath string, width int, height int) { - // 1280×720(720p)—— 标准高清(HD),常用于早期高清视频或小型屏幕 - if width == 1280 && height == 720 { - vars.ImageHorizontalStandard720PList = append(vars.ImageHorizontalStandard720PList, currentImagePath) - return - } - // 1600×900—— 介于HD和FHD之间 - if width == 1600 && height == 900 { - vars.ImageHorizontalStandard1D5KList = append(vars.ImageHorizontalStandard1D5KList, currentImagePath) - return - } - // 1920×1080(1080p)—— 全高清,主流显示器、电视和视频的分辨率 - if width == 1920 && height == 1080 { - vars.ImageHorizontalStandard1080PList = append(vars.ImageHorizontalStandard1080PList, currentImagePath) - return - } - // 2048×1080(2dk)—— 影院2k - if width == 2048 && height == 1080 { - vars.ImageHorizontalStandard2DKList = append(vars.ImageHorizontalStandard2DKList, currentImagePath) - return - } - // 2560×1440(1440p)—— 俗称“2.5K”,电竞显示器或高端手机屏幕 - if width == 2560 && height == 1440 { - vars.ImageHorizontalStandard2D5KList = append(vars.ImageHorizontalStandard2D5KList, currentImagePath) - return - } - // 2560×1080—— 带鱼屏显示器(超宽屏) - if width == 2560 && height == 1080 { - vars.ImageHorizontalStandardUltraWide2List = append(vars.ImageHorizontalStandardUltraWide2List, currentImagePath) - return - } - // 3440×1440—— 带鱼屏显示器(超宽屏) - if width == 3440 && height == 1440 { - vars.ImageHorizontalStandardUltraWide3List = append(vars.ImageHorizontalStandardUltraWide3List, currentImagePath) - return - } - // 5120×2160—— 带鱼屏显示器(超宽屏) - if width == 5120 && height == 2160 { - vars.ImageHorizontalStandardUltraWide4List = append(vars.ImageHorizontalStandardUltraWide4List, currentImagePath) - return - } - // 3840×2160(主流4K)—— 超高清,现代高端显示器、电视和影视制作 - if width == 3840 && height == 2160 { - vars.ImageHorizontalStandard4KList = append(vars.ImageHorizontalStandard4KList, currentImagePath) - return - } - // 4096×2160(DCI 4K)—— 电影行业标准 - if width == 4096 && height == 2160 { - vars.ImageHorizontalStandard4KHList = append(vars.ImageHorizontalStandard4KHList, currentImagePath) - return - } - // 7680×4320—— 超高清,用于专业影视或高端设备 - if width == 7680 && height == 4320 { - vars.ImageHorizontalStandard8KList = append(vars.ImageHorizontalStandard8KList, currentImagePath) - return - } - // 非标规格 - if width < 1024 { - vars.ImageHorizontalNormalList = append(vars.ImageHorizontalNormalList, currentImagePath) - } else if width < 2048 { - vars.ImageHorizontal1KList = append(vars.ImageHorizontal1KList, currentImagePath) - } else if width < 3072 { - vars.ImageHorizontal2KList = append(vars.ImageHorizontal2KList, currentImagePath) - } else if width < 4096 { - vars.ImageHorizontal3KList = append(vars.ImageHorizontal3KList, currentImagePath) - } else if width < 5120 { - vars.ImageHorizontal4KList = append(vars.ImageHorizontal4KList, currentImagePath) - } else if width < 6144 { - vars.ImageHorizontal5KList = append(vars.ImageHorizontal5KList, currentImagePath) - } else if width < 7168 { - vars.ImageHorizontal6KList = append(vars.ImageHorizontal6KList, currentImagePath) - } else if width < 8192 { - vars.ImageHorizontal7KList = append(vars.ImageHorizontal7KList, currentImagePath) - } else if width < 9216 { - vars.ImageHorizontal8KList = append(vars.ImageHorizontal8KList, currentImagePath) - } else if width < 10240 { - vars.ImageHorizontal9KList = append(vars.ImageHorizontal9KList, currentImagePath) - } else if width < 11264 { - vars.ImageHorizontal10KList = append(vars.ImageHorizontal10KList, currentImagePath) - } else if width < 12288 { - vars.ImageHorizontal11KList = append(vars.ImageHorizontal11KList, currentImagePath) - } else if width < 13312 { - vars.ImageHorizontal12KList = append(vars.ImageHorizontal12KList, currentImagePath) - } else if width < 14336 { - vars.ImageHorizontal13KList = append(vars.ImageHorizontal13KList, currentImagePath) - } else if width < 15360 { - vars.ImageHorizontal14KList = append(vars.ImageHorizontal14KList, currentImagePath) - } else if width < 16384 { - vars.ImageHorizontal15KList = append(vars.ImageHorizontal15KList, currentImagePath) - } else if width < 17408 { - vars.ImageHorizontal16KList = append(vars.ImageHorizontal16KList, currentImagePath) - } else if width < 18432 { - vars.ImageHorizontal17KList = append(vars.ImageHorizontal17KList, currentImagePath) - } else if width < 19456 { - vars.ImageHorizontal18KList = append(vars.ImageHorizontal18KList, currentImagePath) - } else if width < 20480 { - vars.ImageHorizontal19KList = append(vars.ImageHorizontal19KList, currentImagePath) - } else if width >= 20480 { // 提示请忽略 - vars.ImageHorizontalHKList = append(vars.ImageHorizontalHKList, currentImagePath) - } -} - -// 处理等比图片 -func handleSquareImage(currentImagePath string, width int) { - // 非标规格 - if width < 1024 { - vars.ImageSquareNormalList = append(vars.ImageSquareNormalList, currentImagePath) - } else if width < 2048 { - vars.ImageSquare1KList = append(vars.ImageSquare1KList, currentImagePath) - } else if width < 3072 { - vars.ImageSquare2KList = append(vars.ImageSquare2KList, currentImagePath) - } else if width < 4096 { - vars.ImageSquare3KList = append(vars.ImageSquare3KList, currentImagePath) - } else if width < 5120 { - vars.ImageSquare4KList = append(vars.ImageSquare4KList, currentImagePath) - } else if width < 6144 { - vars.ImageSquare5KList = append(vars.ImageSquare5KList, currentImagePath) - } else if width < 7168 { - vars.ImageSquare6KList = append(vars.ImageSquare6KList, currentImagePath) - } else if width < 8192 { - vars.ImageSquare7KList = append(vars.ImageSquare7KList, currentImagePath) - } else if width < 9216 { - vars.ImageSquare8KList = append(vars.ImageSquare8KList, currentImagePath) - } else if width < 10240 { - vars.ImageSquare9KList = append(vars.ImageSquare9KList, currentImagePath) - } else if width < 11264 { - vars.ImageSquare10KList = append(vars.ImageSquare10KList, currentImagePath) - } else if width < 12288 { - vars.ImageSquare11KList = append(vars.ImageSquare11KList, currentImagePath) - } else if width < 13312 { - vars.ImageSquare12KList = append(vars.ImageSquare12KList, currentImagePath) - } else if width < 14336 { - vars.ImageSquare13KList = append(vars.ImageSquare13KList, currentImagePath) - } else if width < 15360 { - vars.ImageSquare14KList = append(vars.ImageSquare14KList, currentImagePath) - } else if width < 16384 { - vars.ImageSquare15KList = append(vars.ImageSquare15KList, currentImagePath) - } else if width < 17408 { - vars.ImageSquare16KList = append(vars.ImageSquare16KList, currentImagePath) - } else if width < 18432 { - vars.ImageSquare17KList = append(vars.ImageSquare17KList, currentImagePath) - } else if width < 19456 { - vars.ImageSquare18KList = append(vars.ImageSquare18KList, currentImagePath) - } else if width < 20480 { - vars.ImageSquare19KList = append(vars.ImageSquare19KList, currentImagePath) - } else if width >= 20480 { - vars.ImageSquareHKList = append(vars.ImageSquareHKList, currentImagePath) - } -} - -// 移动水平图片 -func moveHorizontalImage(pickerDir string) { - pathSeparator := string(os.PathSeparator) - // 标准 - if len(vars.ImageHorizontalStandard720PList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_720P" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard720PList, imageDirPath) - } - if len(vars.ImageHorizontalStandard1D5KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_1D5KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard1D5KList, imageDirPath) - } - if len(vars.ImageHorizontalStandard1080PList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_1080P" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard1080PList, imageDirPath) - } - if len(vars.ImageHorizontalStandard2DKList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_2DKP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard2DKList, imageDirPath) - } - if len(vars.ImageHorizontalStandard2D5KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_2D5KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard2D5KList, imageDirPath) - } - if len(vars.ImageHorizontalStandardUltraWide2List) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_ultraWide2KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandardUltraWide2List, imageDirPath) - } - if len(vars.ImageHorizontalStandardUltraWide3List) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_ultraWide3KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandardUltraWide3List, imageDirPath) - } - if len(vars.ImageHorizontalStandardUltraWide4List) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_ultraWide4KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandardUltraWide4List, imageDirPath) - } - if len(vars.ImageHorizontalStandard4KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_4KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard4KList, imageDirPath) - } - if len(vars.ImageHorizontalStandard4KHList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_4KHP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard4KHList, imageDirPath) - } - if len(vars.ImageHorizontalStandard8KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_8KP" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalStandard8KList, imageDirPath) - } - // 非标准 - if len(vars.ImageHorizontalNormalList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_普通" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalNormalList, imageDirPath) - } - if len(vars.ImageHorizontal1KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_1K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal1KList, imageDirPath) - } - if len(vars.ImageHorizontal2KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_2K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal2KList, imageDirPath) - } - if len(vars.ImageHorizontal3KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_3K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal3KList, imageDirPath) - } - if len(vars.ImageHorizontal4KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_4K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal4KList, imageDirPath) - } - if len(vars.ImageHorizontal5KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_5K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal5KList, imageDirPath) - } - if len(vars.ImageHorizontal6KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_6K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal6KList, imageDirPath) - } - if len(vars.ImageHorizontal7KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_7K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal7KList, imageDirPath) - } - if len(vars.ImageHorizontal8KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_8K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal8KList, imageDirPath) - } - if len(vars.ImageHorizontal9KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_9K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal9KList, imageDirPath) - } - if len(vars.ImageHorizontal10KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_10K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal10KList, imageDirPath) - } - if len(vars.ImageHorizontal11KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_11K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal11KList, imageDirPath) - } - if len(vars.ImageHorizontal12KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_12K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal12KList, imageDirPath) - } - if len(vars.ImageHorizontal13KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_13K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal13KList, imageDirPath) - } - if len(vars.ImageHorizontal14KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_14K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal14KList, imageDirPath) - } - if len(vars.ImageHorizontal15KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_15K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal15KList, imageDirPath) - } - if len(vars.ImageHorizontal16KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_16K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal16KList, imageDirPath) - } - if len(vars.ImageHorizontal17KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_17K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal17KList, imageDirPath) - } - if len(vars.ImageHorizontal18KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_18K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal18KList, imageDirPath) - } - if len(vars.ImageHorizontal19KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_19K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontal19KList, imageDirPath) - } - if len(vars.ImageHorizontalHKList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_横屏_原图" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageHorizontalHKList, imageDirPath) - } -} - -// 移动垂直图片 -func moveVerticalImage(pickerDir string) { - pathSeparator := string(os.PathSeparator) - // 非标准 - if len(vars.ImageVerticalNormalList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_普通" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVerticalNormalList, imageDirPath) - } - if len(vars.ImageVertical1KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_1K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical1KList, imageDirPath) - } - if len(vars.ImageVertical2KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_2K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical2KList, imageDirPath) - } - if len(vars.ImageVertical3KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_3K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical3KList, imageDirPath) - } - if len(vars.ImageVertical4KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_4K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical4KList, imageDirPath) - } - if len(vars.ImageVertical5KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_5K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical5KList, imageDirPath) - } - if len(vars.ImageVertical6KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_6K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical6KList, imageDirPath) - } - if len(vars.ImageVertical7KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_7K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical7KList, imageDirPath) - } - if len(vars.ImageVertical8KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_8K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical8KList, imageDirPath) - } - if len(vars.ImageVertical9KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_9K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical9KList, imageDirPath) - } - if len(vars.ImageVertical10KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_10K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical10KList, imageDirPath) - } - if len(vars.ImageVertical11KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_11K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical11KList, imageDirPath) - } - if len(vars.ImageVertical12KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_12K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical12KList, imageDirPath) - } - if len(vars.ImageVertical13KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_13K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical13KList, imageDirPath) - } - if len(vars.ImageVertical14KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_14K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical14KList, imageDirPath) - } - if len(vars.ImageVertical15KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_15K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical15KList, imageDirPath) - } - if len(vars.ImageVertical16KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_16K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical16KList, imageDirPath) - } - if len(vars.ImageVertical17KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_17K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical17KList, imageDirPath) - } - if len(vars.ImageVertical18KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_18K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical18KList, imageDirPath) - } - if len(vars.ImageVertical19KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_19K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVertical19KList, imageDirPath) - } - if len(vars.ImageVerticalHKList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_竖屏_原图" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageVerticalHKList, imageDirPath) - } -} - -// 移动等比图片 -func moveSquareImage(pickerDir string) { - pathSeparator := string(os.PathSeparator) - // 非标准 - if len(vars.ImageSquareNormalList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_普通" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquareNormalList, imageDirPath) - } - if len(vars.ImageSquare1KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_1K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare1KList, imageDirPath) - } - if len(vars.ImageSquare2KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_2K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare2KList, imageDirPath) - } - if len(vars.ImageSquare3KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_3K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare3KList, imageDirPath) - } - if len(vars.ImageSquare4KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_4K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare4KList, imageDirPath) - } - if len(vars.ImageSquare5KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_5K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare5KList, imageDirPath) - } - if len(vars.ImageSquare6KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_6K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare6KList, imageDirPath) - } - if len(vars.ImageSquare7KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_7K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare7KList, imageDirPath) - } - if len(vars.ImageSquare8KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_8K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare8KList, imageDirPath) - } - if len(vars.ImageSquare9KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_9K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare9KList, imageDirPath) - } - if len(vars.ImageSquare10KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_10K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare10KList, imageDirPath) - } - if len(vars.ImageSquare11KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_11K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare11KList, imageDirPath) - } - if len(vars.ImageSquare12KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_12K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare12KList, imageDirPath) - } - if len(vars.ImageSquare13KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_13K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare13KList, imageDirPath) - } - if len(vars.ImageSquare14KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_14K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare14KList, imageDirPath) - } - if len(vars.ImageSquare15KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_15K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare15KList, imageDirPath) - } - if len(vars.ImageSquare16KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_16K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare16KList, imageDirPath) - } - if len(vars.ImageSquare17KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_17K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare17KList, imageDirPath) - } - if len(vars.ImageSquare18KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_18K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare18KList, imageDirPath) - } - if len(vars.ImageSquare19KList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_19K" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquare19KList, imageDirPath) - } - if len(vars.ImageSquareHKList) > 0 { - imageDirPath := pickerDir + pathSeparator + "图片_等比_原图" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageSquareHKList, imageDirPath) - } -} - -// 移动gif图片 -func moveGifImage(pickerDir string) { - if len(vars.ImageGifList) > 0 { - pathSeparator := string(os.PathSeparator) - imageDirPath := pickerDir + pathSeparator + "图片_GIF" - util.CreateDir(imageDirPath) - util.DoMoveFileToDir(vars.ImageGifList, imageDirPath) - } -} diff --git a/core/handle_video.go b/core/handle_video.go deleted file mode 100644 index 59594f5..0000000 --- a/core/handle_video.go +++ /dev/null @@ -1,718 +0,0 @@ -package core - -import ( - "fmt" - "kenaito-media-picker/util" - "kenaito-media-picker/vars" - "os" - "strings" -) - -// 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, 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, mediaFilePath) - } - if len(vars.VideoReadErrorPathList) > 0 { - 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) { - util.DoMoveFileToDir(vars.VideoIgnorePathList, ignorePath) - } - } - doPickVideoFile(pickerDir, vars.VideoPath2WidthHeightMap) - fmt.Printf("=== 视频/音频处理完毕 \n\n") -} - -// 条件视频并分组存放 -func doPickVideoFile(pickerDir string, videoPath2WidthHeightMap map[string]string) { - if len(videoPath2WidthHeightMap) == 0 { - fmt.Printf("=== 当前目录下没有扫描到视频文件, %s \n", pickerDir) - return - } - for currentVideoPath, infoStr := range videoPath2WidthHeightMap { - width2Height := strings.Split(infoStr, "-") - width := util.String2int(width2Height[0]) - height := util.String2int(width2Height[1]) - suffix := vars.GlobalFilePath2FileExtMap[currentVideoPath] - if width > height { - handleHorizontalVideo(currentVideoPath, width, height, suffix) - continue - } - if width < height { - handleVerticalVideo(currentVideoPath, height, suffix) - continue - } - handleSquareVideo(currentVideoPath, width, height, suffix) - } - moveNormalVideo(pickerDir) - moveHorizontalVideo(pickerDir) - moveVerticalVideo(pickerDir) - moveSquareVideo(pickerDir) - moveGifVideo(pickerDir) -} - -// 移动垂直视频 -func moveVerticalVideo(pickerDir string) { - pathSeparator := string(os.PathSeparator) - if len(vars.VideoVertical1KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_1k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical1KList, videoDirPath) - } - if len(vars.VideoVertical2KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_2k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical2KList, videoDirPath) - } - if len(vars.VideoVertical3KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_3k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical3KList, videoDirPath) - } - if len(vars.VideoVertical4KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_4k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical4KList, videoDirPath) - } - if len(vars.VideoVertical5KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_5k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical5KList, videoDirPath) - } - if len(vars.VideoVertical6KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_6k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical6KList, videoDirPath) - } - if len(vars.VideoVertical7KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_7k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical7KList, videoDirPath) - } - if len(vars.VideoVertical8KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_8k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical8KList, videoDirPath) - } - if len(vars.VideoVertical9KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_9k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical9KList, videoDirPath) - } - if len(vars.VideoVertical10KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_10k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical10KList, videoDirPath) - } - if len(vars.VideoVertical11KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_11k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical11KList, videoDirPath) - } - if len(vars.VideoVertical12KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_12k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical12KList, videoDirPath) - } - if len(vars.VideoVertical13KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_13k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical13KList, videoDirPath) - } - if len(vars.VideoVertical14KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_14k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical14KList, videoDirPath) - } - if len(vars.VideoVertical15KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_15k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical15KList, videoDirPath) - } - if len(vars.VideoVertical16KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_16k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical16KList, videoDirPath) - } - if len(vars.VideoVertical17KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_17k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical17KList, videoDirPath) - } - if len(vars.VideoVertical18KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_18k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical18KList, videoDirPath) - } - if len(vars.VideoVertical19KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_19k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVertical19KList, videoDirPath) - } - if len(vars.VideoVerticalHKList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_原画质" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVerticalHKList, videoDirPath) - } -} - -// 移动水平视频 -func moveHorizontalVideo(pickerDir string) { - pathSeparator := string(os.PathSeparator) - if len(vars.VideoHorizontal1KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_1k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal1KList, videoDirPath) - } - if len(vars.VideoHorizontal2KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_2k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal2KList, videoDirPath) - } - if len(vars.VideoHorizontal3KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_3k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal3KList, videoDirPath) - } - if len(vars.VideoHorizontal4KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_4k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal4KList, videoDirPath) - } - if len(vars.VideoHorizontal5KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_5k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal5KList, videoDirPath) - } - if len(vars.VideoHorizontal6KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_6k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal6KList, videoDirPath) - } - if len(vars.VideoHorizontal7KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_7k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal7KList, videoDirPath) - } - if len(vars.VideoHorizontal8KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_8k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal8KList, videoDirPath) - } - if len(vars.VideoHorizontal9KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_9k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal9KList, videoDirPath) - } - if len(vars.VideoHorizontal10KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_10k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal10KList, videoDirPath) - } - if len(vars.VideoHorizontal11KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_11k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal11KList, videoDirPath) - } - if len(vars.VideoHorizontal12KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_12k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal12KList, videoDirPath) - } - if len(vars.VideoHorizontal13KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_13k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal13KList, videoDirPath) - } - if len(vars.VideoHorizontal14KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_14k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal14KList, videoDirPath) - } - if len(vars.VideoHorizontal15KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_15k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal15KList, videoDirPath) - } - if len(vars.VideoHorizontal16KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_16k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal16KList, videoDirPath) - } - if len(vars.VideoHorizontal17KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_17k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal17KList, videoDirPath) - } - if len(vars.VideoHorizontal18KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_18k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal18KList, videoDirPath) - } - if len(vars.VideoHorizontal19KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_19k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontal19KList, videoDirPath) - } - if len(vars.VideoHorizontalHKList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_原画质" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalHKList, videoDirPath) - } - if len(vars.VideoHorizontalStandard720PList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_720P" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard720PList, videoDirPath) - } - if len(vars.VideoHorizontalStandard1D5PList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_1D5KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard1D5PList, videoDirPath) - } - if len(vars.VideoHorizontalStandard1080PList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_1080P" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard1080PList, videoDirPath) - } - if len(vars.VideoHorizontalStandard2DKList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_2DKP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard2DKList, videoDirPath) - } - if len(vars.VideoHorizontalStandard2D5KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_2D5KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard2D5KList, videoDirPath) - } - if len(vars.VideoHorizontalStandardUltraWide2List) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_UltraWide2KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandardUltraWide2List, videoDirPath) - } - if len(vars.VideoHorizontalStandardUltraWide3List) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_UltraWide3KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandardUltraWide3List, videoDirPath) - } - if len(vars.VideoHorizontalStandardUltraWide4List) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_UltraWide4KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandardUltraWide4List, videoDirPath) - } - if len(vars.VideoHorizontalStandard4KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_4KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard4KList, videoDirPath) - } - if len(vars.VideoHorizontalStandard4KHList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_4KHP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard4KHList, videoDirPath) - } - if len(vars.VideoHorizontalStandard8KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_8KP" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalStandard8KList, videoDirPath) - } -} - -// 移动等比视频 -func moveSquareVideo(pickerDir string) { - pathSeparator := string(os.PathSeparator) - if len(vars.VideoSquare1KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_1k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare1KList, videoDirPath) - } - if len(vars.VideoSquare2KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_2k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare2KList, videoDirPath) - } - if len(vars.VideoSquare3KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_3k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare3KList, videoDirPath) - } - if len(vars.VideoSquare4KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_4k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare4KList, videoDirPath) - } - if len(vars.VideoSquare5KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_5k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare5KList, videoDirPath) - } - if len(vars.VideoSquare6KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_6k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare6KList, videoDirPath) - } - if len(vars.VideoSquare7KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_7k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare7KList, videoDirPath) - } - if len(vars.VideoSquare8KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_8k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare8KList, videoDirPath) - } - if len(vars.VideoSquare9KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_9k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare9KList, videoDirPath) - } - if len(vars.VideoSquare10KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_10k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare10KList, videoDirPath) - } - if len(vars.VideoSquare11KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_11k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare11KList, videoDirPath) - } - if len(vars.VideoSquare12KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_12k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare12KList, videoDirPath) - } - if len(vars.VideoSquare13KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_13k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare13KList, videoDirPath) - } - if len(vars.VideoSquare14KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_14k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare14KList, videoDirPath) - } - if len(vars.VideoSquare15KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_15k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare15KList, videoDirPath) - } - if len(vars.VideoSquare16KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_16k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare16KList, videoDirPath) - } - if len(vars.VideoSquare17KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_17k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare17KList, videoDirPath) - } - if len(vars.VideoSquare18KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_18k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare18KList, videoDirPath) - } - if len(vars.VideoSquare19KList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_19k" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquare19KList, videoDirPath) - } - if len(vars.VideoSquareHKList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_原画质" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquareHKList, videoDirPath) - } -} - -// 移动gif视频 -func moveGifVideo(pickerDir string) { - pathSeparator := string(os.PathSeparator) - if len(vars.VideoHorizontalGifList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_GIF" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalGifList, videoDirPath) - } - if len(vars.VideoVerticalGifList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_GIF" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVerticalGifList, videoDirPath) - } - if len(vars.VideoSquareGifList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_GIF" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquareGifList, videoDirPath) - } -} - -// 移动普通视频 -func moveNormalVideo(pickerDir string) { - pathSeparator := string(os.PathSeparator) - if len(vars.VideoHorizontalNormalList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_横屏_普通" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoHorizontalNormalList, videoDirPath) - } - if len(vars.VideoVerticalNormalList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_竖屏_普通" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoVerticalNormalList, videoDirPath) - } - if len(vars.VideoSquareNormalList) > 0 { - videoDirPath := pickerDir + pathSeparator + "视频_等比_普通" - util.CreateDir(videoDirPath) - util.DoMoveFileToDir(vars.VideoSquareNormalList, videoDirPath) - } -} - -// 处理垂直视频 -func handleVerticalVideo(currentVideoPath string, height int, suffix string) { - if strings.EqualFold(suffix, ".gif") { - vars.VideoVerticalGifList = append(vars.VideoVerticalGifList, currentVideoPath) - return - } - if height < 1024 { - vars.VideoVerticalNormalList = append(vars.VideoVerticalNormalList, currentVideoPath) - } else if height < 2048 { - vars.VideoVertical1KList = append(vars.VideoVertical1KList, currentVideoPath) - } else if height < 3072 { - vars.VideoVertical2KList = append(vars.VideoVertical2KList, currentVideoPath) - } else if height < 4096 { - vars.VideoVertical3KList = append(vars.VideoVertical3KList, currentVideoPath) - } else if height < 5120 { - vars.VideoVertical4KList = append(vars.VideoVertical4KList, currentVideoPath) - } else if height < 6144 { - vars.VideoVertical5KList = append(vars.VideoVertical5KList, currentVideoPath) - } else if height < 7168 { - vars.VideoVertical6KList = append(vars.VideoVertical6KList, currentVideoPath) - } else if height < 8192 { - vars.VideoVertical7KList = append(vars.VideoVertical7KList, currentVideoPath) - } else if height < 9216 { - vars.VideoVertical8KList = append(vars.VideoVertical8KList, currentVideoPath) - } else if height < 10240 { - vars.VideoVertical9KList = append(vars.VideoVertical9KList, currentVideoPath) - } else if height < 11264 { - vars.VideoVertical10KList = append(vars.VideoVertical10KList, currentVideoPath) - } else if height < 12288 { - vars.VideoVertical11KList = append(vars.VideoVertical11KList, currentVideoPath) - } else if height < 13312 { - vars.VideoVertical12KList = append(vars.VideoVertical12KList, currentVideoPath) - } else if height < 14336 { - vars.VideoVertical13KList = append(vars.VideoVertical13KList, currentVideoPath) - } else if height < 15360 { - vars.VideoVertical14KList = append(vars.VideoVertical14KList, currentVideoPath) - } else if height < 16384 { - vars.VideoVertical15KList = append(vars.VideoVertical15KList, currentVideoPath) - } else if height < 17408 { - vars.VideoVertical16KList = append(vars.VideoVertical16KList, currentVideoPath) - } else if height < 18432 { - vars.VideoVertical17KList = append(vars.VideoVertical17KList, currentVideoPath) - } else if height < 19456 { - vars.VideoVertical18KList = append(vars.VideoVertical18KList, currentVideoPath) - } else if height < 20480 { - vars.VideoVertical19KList = append(vars.VideoVertical19KList, currentVideoPath) - } else if height >= 20480 { - vars.VideoVerticalHKList = append(vars.VideoVerticalHKList, currentVideoPath) - } -} - -// 处理横向视频 -func handleHorizontalVideo(currentVideoPath string, width int, height int, suffix string) { - if strings.EqualFold(suffix, ".gif") { - vars.VideoHorizontalGifList = append(vars.VideoHorizontalGifList, currentVideoPath) - return - } - // 1280×720(720p)—— 标准高清(HD),常用于早期高清视频或小型屏幕 - if width == 1280 && height == 720 { - vars.VideoHorizontalStandard720PList = append(vars.VideoHorizontalStandard720PList, currentVideoPath) - return - } - // 1600×900—— 介于HD和FHD之间 - if width == 1600 && height == 900 { - vars.VideoHorizontalStandard1D5PList = append(vars.VideoHorizontalStandard1D5PList, currentVideoPath) - return - } - // 1920×1080(1080p)—— 全高清,主流显示器、电视和视频的分辨率 - if width == 1920 && height == 1080 { - vars.VideoHorizontalStandard1080PList = append(vars.VideoHorizontalStandard1080PList, currentVideoPath) - return - } - // 2048×1080(2dk)—— 影院2k - if width == 2048 && height == 1080 { - vars.VideoHorizontalStandard2DKList = append(vars.VideoHorizontalStandard2DKList, currentVideoPath) - return - } - // 2560×1440(1440p)—— 俗称“2.5K”,电竞显示器或高端手机屏幕 - if width == 2560 && height == 1440 { - vars.VideoHorizontalStandard2D5KList = append(vars.VideoHorizontalStandard2D5KList, currentVideoPath) - return - } - // 2560×1080—— 带鱼屏显示器(超宽屏) - if width == 2560 && height == 1080 { - vars.VideoHorizontalStandardUltraWide2List = append(vars.VideoHorizontalStandardUltraWide2List, currentVideoPath) - return - } - // 3440×1440—— 带鱼屏显示器(超宽屏) - if width == 3440 && height == 1440 { - vars.VideoHorizontalStandardUltraWide3List = append(vars.VideoHorizontalStandardUltraWide3List, currentVideoPath) - return - } - // 5120×2160—— 带鱼屏显示器(超宽屏) - if width == 5120 && height == 2160 { - vars.VideoHorizontalStandardUltraWide4List = append(vars.VideoHorizontalStandardUltraWide4List, currentVideoPath) - return - } - // 3840×2160(主流4K)—— 超高清,现代高端显示器、电视和影视制作 - if width == 3840 && height == 2160 { - vars.VideoHorizontalStandard4KList = append(vars.VideoHorizontalStandard4KList, currentVideoPath) - return - } - // 4096×2160(DCI 4K)—— 电影行业标准 - if width == 4096 && height == 2160 { - vars.VideoHorizontalStandard4KHList = append(vars.VideoHorizontalStandard4KHList, currentVideoPath) - return - } - // 7680×4320—— 超高清,用于专业影视或高端设备 - if width == 7680 && height == 4320 { - vars.VideoHorizontalStandard8KList = append(vars.VideoHorizontalStandard8KList, currentVideoPath) - return - } - // 非标规格 - if width < 1024 { - vars.VideoHorizontalNormalList = append(vars.VideoHorizontalNormalList, currentVideoPath) - } else if width < 2048 { - vars.VideoHorizontal1KList = append(vars.VideoHorizontal1KList, currentVideoPath) - } else if width < 3072 { - vars.VideoHorizontal2KList = append(vars.VideoHorizontal2KList, currentVideoPath) - } else if width < 4096 { - vars.VideoHorizontal3KList = append(vars.VideoHorizontal3KList, currentVideoPath) - } else if width < 5120 { - vars.VideoHorizontal4KList = append(vars.VideoHorizontal4KList, currentVideoPath) - } else if width < 6144 { - vars.VideoHorizontal5KList = append(vars.VideoHorizontal5KList, currentVideoPath) - } else if width < 7168 { - vars.VideoHorizontal6KList = append(vars.VideoHorizontal6KList, currentVideoPath) - } else if width < 8192 { - vars.VideoHorizontal7KList = append(vars.VideoHorizontal7KList, currentVideoPath) - } else if width < 9216 { - vars.VideoHorizontal8KList = append(vars.VideoHorizontal8KList, currentVideoPath) - } else if width < 10240 { - vars.VideoHorizontal9KList = append(vars.VideoHorizontal9KList, currentVideoPath) - } else if width < 11264 { - vars.VideoHorizontal10KList = append(vars.VideoHorizontal10KList, currentVideoPath) - } else if width < 12288 { - vars.VideoHorizontal11KList = append(vars.VideoHorizontal11KList, currentVideoPath) - } else if width < 13312 { - vars.VideoHorizontal12KList = append(vars.VideoHorizontal12KList, currentVideoPath) - } else if width < 14336 { - vars.VideoHorizontal13KList = append(vars.VideoHorizontal13KList, currentVideoPath) - } else if width < 15360 { - vars.VideoHorizontal14KList = append(vars.VideoHorizontal14KList, currentVideoPath) - } else if width < 16384 { - vars.VideoHorizontal15KList = append(vars.VideoHorizontal15KList, currentVideoPath) - } else if width < 17408 { - vars.VideoHorizontal16KList = append(vars.VideoHorizontal16KList, currentVideoPath) - } else if width < 18432 { - vars.VideoHorizontal17KList = append(vars.VideoHorizontal17KList, currentVideoPath) - } else if width < 19456 { - vars.VideoHorizontal18KList = append(vars.VideoHorizontal18KList, currentVideoPath) - } else if width < 20480 { - vars.VideoHorizontal19KList = append(vars.VideoHorizontal19KList, currentVideoPath) - } else if width >= 20480 { // 提示请忽略 - vars.VideoHorizontalHKList = append(vars.VideoHorizontalHKList, currentVideoPath) - } -} - -// 处理等比视频 -func handleSquareVideo(currentVideoPath string, width int, height int, suffix string) { - if strings.EqualFold(suffix, ".gif") { - vars.VideoSquareGifList = append(vars.VideoSquareGifList, currentVideoPath) - return - } - if width < 1024 { - vars.VideoSquareNormalList = append(vars.VideoSquareNormalList, currentVideoPath) - } else if width < 2048 { - vars.VideoSquare1KList = append(vars.VideoSquare1KList, currentVideoPath) - } else if width < 3072 { - vars.VideoSquare2KList = append(vars.VideoSquare2KList, currentVideoPath) - } else if width < 4096 { - vars.VideoSquare3KList = append(vars.VideoSquare3KList, currentVideoPath) - } else if width < 5120 { - vars.VideoSquare4KList = append(vars.VideoSquare4KList, currentVideoPath) - } else if width < 6144 { - vars.VideoSquare5KList = append(vars.VideoSquare5KList, currentVideoPath) - } else if width < 7168 { - vars.VideoSquare6KList = append(vars.VideoSquare6KList, currentVideoPath) - } else if width < 8192 { - vars.VideoSquare7KList = append(vars.VideoSquare7KList, currentVideoPath) - } else if width < 9216 { - vars.VideoSquare8KList = append(vars.VideoSquare8KList, currentVideoPath) - } else if width < 10240 { - vars.VideoSquare9KList = append(vars.VideoSquare9KList, currentVideoPath) - } else if width < 11264 { - vars.VideoSquare10KList = append(vars.VideoSquare10KList, currentVideoPath) - } else if width < 12288 { - vars.VideoSquare11KList = append(vars.VideoSquare11KList, currentVideoPath) - } else if width < 13312 { - vars.VideoSquare12KList = append(vars.VideoSquare12KList, currentVideoPath) - } else if width < 14336 { - vars.VideoSquare13KList = append(vars.VideoSquare13KList, currentVideoPath) - } else if width < 15360 { - vars.VideoSquare14KList = append(vars.VideoSquare14KList, currentVideoPath) - } else if width < 16384 { - vars.VideoSquare15KList = append(vars.VideoSquare15KList, currentVideoPath) - } else if width < 17408 { - vars.VideoSquare16KList = append(vars.VideoSquare16KList, currentVideoPath) - } else if width < 18432 { - vars.VideoSquare17KList = append(vars.VideoSquare17KList, currentVideoPath) - } else if width < 19456 { - vars.VideoSquare18KList = append(vars.VideoSquare18KList, currentVideoPath) - } else if width < 20480 { - vars.VideoSquare19KList = append(vars.VideoSquare19KList, currentVideoPath) - } else if width >= 20480 { - vars.VideoSquareHKList = append(vars.VideoSquareHKList, currentVideoPath) - } -} diff --git a/core/save_media.go b/core/save_media.go deleted file mode 100644 index 12ce620..0000000 --- a/core/save_media.go +++ /dev/null @@ -1,57 +0,0 @@ -package core - -import ( - "fmt" - "kenaito-media-picker/store" - "kenaito-media-picker/util" - "kenaito-media-picker/vars" - "os" -) - -// saveMediaRecord 将媒体信息写入数据库;大文件优先用指纹(头尾采样)复用已入库的MD5,避免全量读取 -func saveMediaRecord(filePath string, mediaType string, width int, height int, duration float64, codec string, format string, rawInfo string) { - st, err := os.Stat(filePath) - if err != nil { - fmt.Printf("=== 读取文件信息失败: %s, 错误: %v\n", filePath, err) - return - } - size := st.Size() - - md5Str := "" - headMd5, tailMd5 := "", "" - if size > util.FingerprintThreshold { - if head, tail, ferr := util.CalcFileFingerprint(filePath, size); ferr == nil { - headMd5, tailMd5 = head, tail - if existing, ok := store.FindMediaByFingerprint(size, head, tail); ok && existing.FileMd5 != "" { - md5Str = existing.FileMd5 // 指纹命中,复用MD5,跳过全量读取 - } - } - } - if md5Str == "" { - if md5Str, err = util.CalcFileMd5(filePath); err != nil { - fmt.Printf("=== 计算MD5失败: %s, 错误: %v\n", filePath, err) - return - } - } - - info := &store.MediaInfo{ - FileMd5: md5Str, - FilePath: filePath, - FileName: vars.GlobalFilePath2FileNameMap[filePath], - FileExt: vars.GlobalFilePath2FileExtMap[filePath], - FileSize: size, - MimeType: vars.GlobalFilePath2MimeInfoMap[filePath], - MediaType: mediaType, - Width: width, - Height: height, - Duration: duration, - Codec: codec, - Format: format, - RawInfo: rawInfo, - HeadMd5: headMd5, - TailMd5: tailMd5, - } - if err := store.SaveMedia(info); err != nil { - fmt.Printf("=== 保存媒体信息失败: %s, 错误: %v\n", filePath, err) - } -} diff --git a/core/webp.go b/core/webp.go deleted file mode 100644 index 4dcf4ab..0000000 --- a/core/webp.go +++ /dev/null @@ -1,25 +0,0 @@ -package core - -import ( - "fmt" - "golang.org/x/image/webp" - "os" -) - -// ReadWebpImage 读取webp格式的图片信息 -func ReadWebpImage(webpFilePath string) (err error, width int, height int) { - // 打开WebP文件 - file, err := os.Open(webpFilePath) - if err != nil { - fmt.Printf("=== Failed to open file: %v\n", err) - return err, 0, 0 - } - defer file.Close() - // 使用webp.DecodeConfig解码WebP图片配置信息(不加载完整像素数据) - imgConfig, err := webp.DecodeConfig(file) - if err != nil { - fmt.Printf("=== Failed to decode WebP image config: %v\n", err) - return err, 0, 0 - } - return nil, imgConfig.Width, imgConfig.Height -} diff --git a/internal/classify/classify.go b/internal/classify/classify.go new file mode 100644 index 0000000..2a1c8f9 --- /dev/null +++ b/internal/classify/classify.go @@ -0,0 +1,72 @@ +// Package classify 提供纯函数的分辨率归类规则,不依赖任何状态。 +package classify + +import "strings" + +// Orientation 画面方向 +type Orientation int + +const ( + OrientationSquare Orientation = iota + OrientationHorizontal + OrientationVertical +) + +// OrientationOf 由宽高判定画面方向 +func OrientationOf(width, height int) Orientation { + switch { + case width > height: + return OrientationHorizontal + case width < height: + return OrientationVertical + default: + return OrientationSquare + } +} + +func (o Orientation) String() string { + switch o { + case OrientationHorizontal: + return "横屏" + case OrientationVertical: + return "竖屏" + default: + return "等比" + } +} + +// ImageDir 返回图片归类子目录名,如 "图片_横屏_1080P" / "图片_GIF" +func ImageDir(ext string, width, height int) string { + if strings.EqualFold(ext, ".gif") { + return "图片_GIF" + } + return resolutionDir("图片", ext, width, height) +} + +// VideoDir 返回视频归类子目录名,如 "视频_竖屏_4k" / "视频_横屏_GIF" +func VideoDir(ext string, width, height int) string { + return resolutionDir("视频", ext, width, height) +} + +func resolutionDir(kind, ext string, width, height int) string { + ori := OrientationOf(width, height).String() + if strings.EqualFold(ext, ".gif") { + return kind + "_" + ori + "_GIF" + } + if suffix := matchStandard(width, height); suffix != "" { + return kind + "_" + ori + "_" + suffix + } + overflow := "原图" + if kind == "视频" { + overflow = "原画质" + } + return kind + "_" + ori + "_" + levelName(longEdge(width, height), overflow) +} + +// longEdge 返回长边像素 +func longEdge(w, h int) int { + if w > h { + return w + } + return h +} diff --git a/internal/classify/standard.go b/internal/classify/standard.go new file mode 100644 index 0000000..4fa3437 --- /dev/null +++ b/internal/classify/standard.go @@ -0,0 +1,52 @@ +package classify + +import "fmt" + +// standardResolution 标准分辨率规则:宽高精确匹配 +type standardResolution struct { + width int + height int + suffix string +} + +var standardResolutions = []standardResolution{ + {1280, 720, "720P"}, // 720p 标准高清 + {1600, 900, "1D5KP"}, // 介于 HD 与 FHD 之间 + {1920, 1080, "1080P"}, // 1080p 全高清 + {2048, 1080, "2DKP"}, // 影院 2K + {2560, 1440, "2D5KP"}, // 2.5K + {2560, 1080, "UltraWide2KP"}, // 带鱼屏 + {3440, 1440, "UltraWide3KP"}, // 带鱼屏 + {5120, 2160, "UltraWide4KP"}, // 带鱼屏 + {3840, 2160, "4KP"}, // 主流 4K + {4096, 2160, "4KHP"}, // DCI 4K + {7680, 4320, "8KP"}, // 8K +} + +// 非标分辨率分档参数 +const ( + bucketStep = 1024 // 每 1024px 一档 + maxLevel = 19 // 1K ~ 19K 共 19 档(普通另算 1 档,合计 20 档) +) + +// matchStandard 精确匹配标准分辨率,命中返回后缀,否则返回空串 +func matchStandard(width, height int) string { + for _, r := range standardResolutions { + if r.width == width && r.height == height { + return r.suffix + } + } + return "" +} + +// levelName 按长边分档:普通(<1024) / 1K~19K(每1024一档) / overflow(≥20480) +func levelName(longEdge int, overflow string) string { + if longEdge < bucketStep { + return "普通" + } + level := longEdge / bucketStep + if level > maxLevel { + return overflow + } + return fmt.Sprintf("%dK", level) +} diff --git a/internal/digest/digest.go b/internal/digest/digest.go new file mode 100644 index 0000000..55e310c --- /dev/null +++ b/internal/digest/digest.go @@ -0,0 +1,53 @@ +// Package digest 提供文件 MD5 与指纹采样计算,供媒体入库时复用。 +package digest + +import ( + "crypto/md5" + "encoding/hex" + "io" + "os" +) + +const ( + // FingerprintThreshold 文件大于该字节数才使用指纹复用,避免全量MD5 + FingerprintThreshold = 1 << 20 + fingerprintHeadSize = 256 << 10 // 文件头采样256KB + fingerprintTailSize = 256 << 10 // 文件尾采样256KB +) + +// CalcFileMd5 计算文件MD5(分块读取,适合大文件) +func CalcFileMd5(filePath string) (string, error) { + file, err := os.Open(filePath) + if err != nil { + return "", err + } + defer file.Close() + h := md5.New() + if _, err := io.Copy(h, file); err != nil { + return "", err + } + return hex.EncodeToString(h.Sum(nil)), nil +} + +// CalcFileFingerprint 计算文件头部与尾部采样MD5(只读取很少字节,用于快速识别相同文件) +func CalcFileFingerprint(filePath string, size int64) (headMd5, tailMd5 string, err error) { + file, err := os.Open(filePath) + if err != nil { + return "", "", err + } + defer file.Close() + h := md5.New() + if _, err := io.CopyN(h, file, fingerprintHeadSize); err != nil { + return "", "", err + } + headMd5 = hex.EncodeToString(h.Sum(nil)) + h = md5.New() + if _, err := file.Seek(size-fingerprintTailSize, io.SeekStart); err != nil { + return "", "", err + } + if _, err := io.CopyN(h, file, fingerprintTailSize); err != nil { + return "", "", err + } + tailMd5 = hex.EncodeToString(h.Sum(nil)) + return headMd5, tailMd5, nil +} diff --git a/internal/mover/mover.go b/internal/mover/mover.go new file mode 100644 index 0000000..5b750a4 --- /dev/null +++ b/internal/mover/mover.go @@ -0,0 +1,88 @@ +// Package mover 负责文件的移动,跨文件系统时自动回退为复制后删除。 +package mover + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/redmask-hb/GoSimplePrint/goPrint" + uuid "github.com/satori/go.uuid" +) + +// Mover 文件移动器 +type Mover struct{} + +// MoveToDir 将一批文件移动到目标目录(目标文件名使用 uuid 前缀防止同名覆盖)。 +// 返回移动成功的 源路径 → 目标目录 映射,供记录去向使用。 +func (m *Mover) MoveToDir(filePaths []string, targetDir string) map[string]string { + moved := make(map[string]string) + if len(filePaths) == 0 { + return moved + } + if err := os.MkdirAll(targetDir, 0755); err != nil { + fmt.Printf("=== 创建目录失败: %s, 错误: %v\n", targetDir, err) + return moved + } + + bar := goPrint.NewBar(100) + bar.SetNotice("=== 移动文件到目录:") + bar.SetGraph(">") + total := len(filePaths) + for i, src := range filePaths { + if moveOne(src, targetDir) { + moved[src] = targetDir + } + bar.PrintBar(percent(i+1, total)) + } + bar.PrintEnd("=== Finish") + return moved +} + +// moveOne 移动单个文件;跨文件系统时 fallback 为 copy+delete +func moveOne(src, targetDir string) bool { + fileName := strings.ReplaceAll(uuid.NewV4().String(), "-", "") + "_" + filepath.Base(src) + target := filepath.Join(targetDir, fileName) + if err := os.Rename(src, target); err == nil { + return true + } + if err := copyFile(src, target); err != nil { + fmt.Printf("=== 复制文件失败, 源: %s, 目标: %s, 错误: %v\n", src, target, err) + return false + } + if err := os.Remove(src); err != nil { + fmt.Printf("=== 删除源文件失败: %s, 错误: %v\n", src, err) + // 复制成功但删除失败,文件已存在于两处,视为移动成功 + } + return true +} + +// copyFile 逐块复制文件 +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + if _, err := io.Copy(out, in); err != nil { + return err + } + return out.Close() +} + +// percent 计算百分比(0~100) +func percent(part, total int) int { + if total <= 0 { + return 100 + } + return int(float64(part) / float64(total) * 100) +} diff --git a/internal/organize/organize.go b/internal/organize/organize.go new file mode 100644 index 0000000..f8caef6 --- /dev/null +++ b/internal/organize/organize.go @@ -0,0 +1,181 @@ +// Package organize 编排整理流程:探测 → 归类 → 收集 bucket → 一次性移动并记录去向。 +package organize + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "kenaito-media-picker/internal/classify" + "kenaito-media-picker/internal/digest" + "kenaito-media-picker/internal/mover" + "kenaito-media-picker/internal/probe" + "kenaito-media-picker/internal/scanner" + "kenaito-media-picker/internal/store" +) + +// 特殊归类目录名 +const ( + dirImagePSD = "图片_PSD" + dirImageErr = "图片_读取异常" + dirImageIgnored = "图片_已忽略" + dirVideoErr = "视频_读取异常" + dirVideoIgnored = "视频_已忽略" + dirAudio = "音频" +) + +// Organizer 整理器:按目标目录收集文件,处理完成后统一建目录并移动 +type Organizer struct { + pickerDir string + ffprobePath string + store *store.Store + mover *mover.Mover + buckets map[string][]string // 目标目录 → 文件路径 + order []string // 目标目录的创建顺序(保证输出稳定) +} + +// New 创建整理器;store 可为 nil(数据库初始化失败时仅不记录信息) +func New(pickerDir, ffprobePath string, st *store.Store) *Organizer { + return &Organizer{ + pickerDir: pickerDir, + ffprobePath: ffprobePath, + store: st, + mover: &mover.Mover{}, + buckets: make(map[string][]string), + } +} + +// add 将文件归入某个子目录 +func (o *Organizer) add(f scanner.File, subDir string) { + dir := filepath.Join(o.pickerDir, subDir) + if _, ok := o.buckets[dir]; !ok { + o.order = append(o.order, dir) + } + o.buckets[dir] = append(o.buckets[dir], f.Path) +} + +// record 将媒体信息写入数据库(大文件优先用指纹复用已入库的 MD5) +func (o *Organizer) record(f scanner.File, mediaType string, res *probe.ProbeResult) { + st, err := os.Stat(f.Path) + if err != nil { + fmt.Printf("=== 读取文件信息失败: %s, 错误: %v\n", f.Path, err) + return + } + size := st.Size() + + md5Str := "" + headMd5, tailMd5 := "", "" + if size > digest.FingerprintThreshold { + if head, tail, ferr := digest.CalcFileFingerprint(f.Path, size); ferr == nil { + headMd5, tailMd5 = head, tail + if existing, ok := o.store.FindMediaByFingerprint(size, head, tail); ok && existing.FileMd5 != "" { + md5Str = existing.FileMd5 // 指纹命中,复用MD5,跳过全量读取 + } + } + } + if md5Str == "" { + if md5Str, err = digest.CalcFileMd5(f.Path); err != nil { + fmt.Printf("=== 计算MD5失败: %s, 错误: %v\n", f.Path, err) + return + } + } + + codec := res.VideoCodec + if mediaType == "audio" { + codec = res.AudioCodec + } + info := &store.MediaInfo{ + FileMd5: md5Str, + FilePath: f.Path, + FileName: f.Name, + FileExt: f.Ext, + FileSize: size, + MimeType: f.Mime, + MediaType: mediaType, + Width: res.Width, + Height: res.Height, + Duration: res.Duration, + Codec: codec, + Format: res.Format, + RawInfo: res.RawJSON, + HeadMd5: headMd5, + TailMd5: tailMd5, + } + if err := o.store.SaveMedia(info); err != nil { + fmt.Printf("=== 保存媒体信息失败: %s, 错误: %v\n", f.Path, err) + } +} + +// ProcessImages 处理全部图片:探测尺寸并归类 +func (o *Organizer) ProcessImages(images []scanner.File) { + success, fail, ignore := 0, 0, 0 + for _, f := range images { + switch { + case strings.EqualFold(f.Ext, ".psd"): + o.add(f, dirImagePSD) + success++ + continue + case !probe.IsSupportImage(f.Ext): + o.add(f, dirImageIgnored) + ignore++ + continue + } + res, err := probe.ProbeImage(f.Path, o.ffprobePath) + if err != nil { + o.add(f, dirImageErr) + fail++ + fmt.Printf("=== 异常图片: %s \n", f.Path) + continue + } + o.record(f, "image", res) + o.add(f, classify.ImageDir(f.Ext, res.Width, res.Height)) + success++ + } + fmt.Printf("=== 图片处理完毕,成功: %d, 读取异常: %d, 忽略: %d \n\n", success, fail, ignore) +} + +// ProcessMedia 处理媒体候选:ffprobe 判定视频/音频/不支持 +func (o *Organizer) ProcessMedia(media []scanner.File) { + video, audio, fail, ignore := 0, 0, 0, 0 + for _, f := range media { + res, err := probe.ProbeMediaFile(o.ffprobePath, f.Path) + if err != nil { + if strings.HasPrefix(f.Mime, "video/") { + fail++ + o.add(f, dirVideoErr) + fmt.Printf("=== 异常视频: %s \n", f.Path) + } else { + ignore++ + o.add(f, dirVideoIgnored) + } + continue + } + switch { + case res.HasVideo: + video++ + o.record(f, "video", res) + o.add(f, classify.VideoDir(f.Ext, res.Width, res.Height)) + case res.HasAudio: + audio++ + o.record(f, "audio", res) + o.add(f, dirAudio) + default: + ignore++ + o.add(f, dirVideoIgnored) + } + } + fmt.Printf("=== 媒体处理完毕,视频: %d, 音频: %d, 读取异常: %d, 忽略: %d \n\n", video, audio, fail, ignore) +} + +// Flush 将所有收集到的文件移动到对应目录,并回填归类去向 +func (o *Organizer) Flush() { + for _, dir := range o.order { + moved := o.mover.MoveToDir(o.buckets[dir], dir) + for src, dest := range moved { + if err := o.store.UpdateMediaDest(src, dest); err != nil { + fmt.Printf("=== 更新媒体去向失败: %s, 错误: %v\n", src, err) + } + } + } +} diff --git a/internal/probe/bmp.go b/internal/probe/bmp.go new file mode 100644 index 0000000..f02c0e3 --- /dev/null +++ b/internal/probe/bmp.go @@ -0,0 +1,52 @@ +package probe + +import ( + "encoding/binary" + "fmt" + "os" +) + +// probeBmp 读取 BMP 文件头中的宽高;自顶向下 BMP 的高度为负值,取绝对值 +func probeBmp(filePath string) (*ProbeResult, error) { + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + + var fileHeader struct { + FileType [2]byte + FileSize uint32 + Reserved1 uint16 + Reserved2 uint16 + DataOffset uint32 + } + if err := binary.Read(f, binary.LittleEndian, &fileHeader); err != nil { + return nil, err + } + if string(fileHeader.FileType[:]) != "BM" { + return nil, fmt.Errorf("not a valid BMP file") + } + var infoHeader struct { + Size uint32 + Width int32 + Height int32 + Planes uint16 + BitsPerPixel uint16 + Compression uint32 + SizeImage uint32 + XPelsPerMeter int32 + YPelsPerMeter int32 + } + if err := binary.Read(f, binary.LittleEndian, &infoHeader); err != nil { + return nil, err + } + return &ProbeResult{Width: abs(infoHeader.Width), Height: abs(infoHeader.Height)}, nil +} + +func abs(v int32) int { + if v < 0 { + return int(-v) + } + return int(v) +} diff --git a/internal/probe/ffprobe.go b/internal/probe/ffprobe.go new file mode 100644 index 0000000..2308c1e --- /dev/null +++ b/internal/probe/ffprobe.go @@ -0,0 +1,32 @@ +package probe + +import ( + "fmt" + "os" +) + +// FFprobeExeName 释放到当前目录的解码器文件名 +const FFprobeExeName = "ffprobe.exe" + +// FFprobePath 返回当前目录下的 ffprobe 可执行路径(Windows/Unix 均可直接执行) +func FFprobePath() string { + return "." + string(os.PathSeparator) + FFprobeExeName +} + +// HasEmbeddedFFprobe 当前平台是否内嵌了 ffprobe +func HasEmbeddedFFprobe() bool { + return len(embeddedFFprobe) > 0 +} + +// ReleaseFFprobe 释放内嵌的 ffprobe 到当前目录(按当前系统自动选择对应版本) +func ReleaseFFprobe() error { + if len(embeddedFFprobe) == 0 { + return fmt.Errorf("当前系统没有内嵌的ffprobe") + } + return os.WriteFile(FFprobeExeName, embeddedFFprobe, 0777) +} + +// RemoveFFprobe 删除当前目录下释放的 ffprobe +func RemoveFFprobe() { + _ = os.Remove(FFprobeExeName) +} diff --git a/internal/probe/ffprobe_embed_darwin.go b/internal/probe/ffprobe_embed_darwin.go new file mode 100644 index 0000000..060dddb --- /dev/null +++ b/internal/probe/ffprobe_embed_darwin.go @@ -0,0 +1,8 @@ +//go:build darwin + +package probe + +import _ "embed" + +//go:embed bin/ffprobe_mac +var embeddedFFprobe []byte // macOS 版 ffprobe(未入库,见 .gitignore) diff --git a/internal/probe/ffprobe_embed_unsupported.go b/internal/probe/ffprobe_embed_unsupported.go new file mode 100644 index 0000000..7a93b78 --- /dev/null +++ b/internal/probe/ffprobe_embed_unsupported.go @@ -0,0 +1,6 @@ +//go:build !darwin && !windows + +package probe + +// embeddedFFprobe 当前平台没有内嵌的 ffprobe +var embeddedFFprobe []byte diff --git a/internal/probe/ffprobe_embed_windows.go b/internal/probe/ffprobe_embed_windows.go new file mode 100644 index 0000000..a4e749c --- /dev/null +++ b/internal/probe/ffprobe_embed_windows.go @@ -0,0 +1,8 @@ +//go:build windows + +package probe + +import _ "embed" + +//go:embed bin/ffprobe.exe +var embeddedFFprobe []byte // Windows 版 ffprobe(未入库,见 .gitignore) diff --git a/internal/probe/image.go b/internal/probe/image.go new file mode 100644 index 0000000..3575412 --- /dev/null +++ b/internal/probe/image.go @@ -0,0 +1,76 @@ +package probe + +import ( + "fmt" + "image" + "os" + "path/filepath" + "strings" +) + +// supportImageTypes 支持的标准图片扩展名(大小写不敏感) +var supportImageTypes = []string{ + ".bmp", + ".gif", + ".jpg", + ".jpeg", + ".jpe", + ".png", + ".webp", + ".psb", + ".jfif", +} + +// IsSupportImage 判断扩展名是否属于支持的图片格式 +func IsSupportImage(ext string) bool { + for _, t := range supportImageTypes { + if strings.EqualFold(t, ext) { + return true + } + } + return false +} + +// ProbeImage 统一探测图片尺寸: +// 1. 特殊格式(webp/bmp/psd/psb)走专用解码器 +// 2. 其余支持格式走 Go 标准库 image.DecodeConfig +// 3. 标准库失败时用 ffprobe 兜底(ffmpeg 支持的图片格式更广,如 tiff/svg 等) +// +// ffprobePath 为空表示不启用 ffprobe 兜底;扩展名不属于支持格式时返回错误。 +func ProbeImage(filePath, ffprobePath string) (*ProbeResult, error) { + ext := strings.ToLower(filepath.Ext(filePath)) + switch ext { + case ".webp": + return probeWebp(filePath) + case ".bmp": + return probeBmp(filePath) + case ".psd", ".psb": + return probePsd(filePath) + } + if !IsSupportImage(ext) { + return nil, fmt.Errorf("不支持的图片格式: %s", ext) + } + if w, h, err := decodeConfig(filePath); err == nil { + return &ProbeResult{Width: w, Height: h}, nil + } + if ffprobePath != "" { + if res, err := ProbeMediaFile(ffprobePath, filePath); err == nil && res.HasVideo { + return res, nil + } + } + return nil, fmt.Errorf("无法读取图片尺寸: %s", filePath) +} + +// decodeConfig 用 Go 标准库读取图片宽高 +func decodeConfig(filePath string) (int, int, error) { + f, err := os.Open(filePath) + if err != nil { + return 0, 0, err + } + defer f.Close() + cfg, _, err := image.DecodeConfig(f) + if err != nil { + return 0, 0, err + } + return cfg.Width, cfg.Height, nil +} diff --git a/internal/probe/probe.go b/internal/probe/probe.go new file mode 100644 index 0000000..9007d62 --- /dev/null +++ b/internal/probe/probe.go @@ -0,0 +1,80 @@ +// Package probe 负责媒体信息探测:图片尺寸读取、ffprobe 视频/音频解析与内嵌解码器释放。 +package probe + +import ( + "encoding/json" + "fmt" + "os/exec" + "strconv" +) + +// ProbeResult 媒体探测结果(图片/视频/音频通用) +type ProbeResult struct { + HasVideo bool + HasAudio bool + Width int + Height int + Duration float64 + VideoCodec string + AudioCodec string + Format string + RawJSON string +} + +type streamInfo struct { + Index int `json:"index"` + CodecType string `json:"codec_type"` + CodecName string `json:"codec_name"` + Width int `json:"width"` + Height int `json:"height"` +} + +type formatInfo struct { + Duration string `json:"duration"` + Format string `json:"format_name"` +} + +type probeOutput struct { + Streams []streamInfo `json:"streams"` + Format formatInfo `json:"format"` +} + +// ProbeMediaFile 用 ffprobe 探测媒体文件,一次调用同时获取视频/音频流与时长 +func ProbeMediaFile(ffprobePath, filePath string) (*ProbeResult, error) { + cmd := exec.Command(ffprobePath, "-v", "error", + "-show_entries", "stream=index,codec_type,codec_name,width,height:format=duration,format_name", + "-of", "json", filePath) + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("ffprobe failed: %w", err) + } + var out probeOutput + if err := json.Unmarshal(output, &out); err != nil { + return nil, fmt.Errorf("parse ffprobe output: %w", err) + } + res := &ProbeResult{ + Format: out.Format.Format, + RawJSON: string(output), + } + if d, err := strconv.ParseFloat(out.Format.Duration, 64); err == nil { + res.Duration = d + } + for _, s := range out.Streams { + switch s.CodecType { + case "video": + res.HasVideo = true + if res.VideoCodec == "" { + res.VideoCodec = s.CodecName + } + if res.Width == 0 && s.Width > 0 && s.Height > 0 { + res.Width, res.Height = s.Width, s.Height + } + case "audio": + res.HasAudio = true + if res.AudioCodec == "" { + res.AudioCodec = s.CodecName + } + } + } + return res, nil +} diff --git a/internal/probe/psd.go b/internal/probe/psd.go new file mode 100644 index 0000000..4e1d74d --- /dev/null +++ b/internal/probe/psd.go @@ -0,0 +1,32 @@ +package probe + +import ( + "encoding/binary" + "fmt" + "io" + "os" +) + +// probePsd 读取 PSD/PSB 格式的宽高(两者共享相同的 8BPS 文件头) +func probePsd(filePath string) (*ProbeResult, error) { + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + + var header [26]byte + if _, err := io.ReadFull(f, header[:]); err != nil { + return nil, err + } + if string(header[0:4]) != "8BPS" { + return nil, fmt.Errorf("not a valid PSD/PSB file") + } + version := binary.BigEndian.Uint16(header[4:6]) + if version != 1 && version != 2 { + return nil, fmt.Errorf("unsupported PSD/PSB version: %d", version) + } + height := int(binary.BigEndian.Uint32(header[14:18])) + width := int(binary.BigEndian.Uint32(header[18:22])) + return &ProbeResult{Width: width, Height: height}, nil +} diff --git a/internal/probe/webp.go b/internal/probe/webp.go new file mode 100644 index 0000000..f50b619 --- /dev/null +++ b/internal/probe/webp.go @@ -0,0 +1,22 @@ +package probe + +import ( + "fmt" + "os" + + "golang.org/x/image/webp" +) + +// probeWebp 读取 WebP 图片宽高(只解码配置,不加载完整像素数据) +func probeWebp(filePath string) (*ProbeResult, error) { + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + cfg, err := webp.DecodeConfig(f) + if err != nil { + return nil, fmt.Errorf("decode webp: %w", err) + } + return &ProbeResult{Width: cfg.Width, Height: cfg.Height}, nil +} diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go new file mode 100644 index 0000000..f1e7b25 --- /dev/null +++ b/internal/scanner/scanner.go @@ -0,0 +1,138 @@ +// Package scanner 负责递归扫描文件、读取 MIME 并按类型过滤。 +package scanner + +import ( + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "kenaito-media-picker/internal/probe" + + "github.com/gabriel-vasile/mimetype" + "github.com/redmask-hb/GoSimplePrint/goPrint" +) + +// File 扫描到的文件信息 +type File struct { + Path string // 完整路径 + Name string // 文件名(含扩展名) + Ext string // 扩展名(含点) + Mime string // MIME 类型 +} + +// Result 过滤结果 +type Result struct { + Files []File // 全部文件 + Images []File // 图片文件 + Media []File // 待 ffprobe 确认的媒体候选 +} + +// Options 扫描选项 +type Options struct { + RootDir string // 扫描根目录 + SkipDir string // 白名单输出目录,整目录跳过 + SkipNames []string // 按文件名跳过(数据库、ffprobe 等) +} + +// Scan 递归扫描目录并读取每个文件的 MIME 信息 +func Scan(opts Options) (*Result, error) { + res := &Result{} + err := filepath.Walk(opts.RootDir, func(currentPath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + if opts.SkipDir != "" && currentPath == opts.SkipDir { + return filepath.SkipDir + } + return nil + } + for _, n := range opts.SkipNames { + if info.Name() == n { + return nil + } + } + res.Files = append(res.Files, File{ + Path: currentPath, + Name: info.Name(), + Ext: path.Ext(currentPath), + }) + return nil + }) + if err != nil { + return nil, err + } + + total := len(res.Files) + fmt.Printf("=== 文件总数: %d \n", total) + bar := goPrint.NewBar(100) + bar.SetNotice("=== 扫描文件Mime:") + bar.SetGraph(">") + bar.SetNoticeColor(goPrint.FontColor.Red) + for i := range res.Files { + res.Files[i].Mime = detectMime(res.Files[i].Path) + bar.PrintBar(percent(i+1, total)) + } + bar.PrintEnd("=== Finish") + return res, nil +} + +// Filter 将文件分为图片与媒体候选 +func (r *Result) Filter() { + for _, f := range r.Files { + if strings.Contains(f.Mime, "image/") || probe.IsSupportImage(f.Ext) || strings.EqualFold(f.Ext, ".psd") { + r.Images = append(r.Images, f) + continue + } + if isPotentialMedia(f.Mime) { + r.Media = append(r.Media, f) + continue + } + // 明显的非媒体文件(代码/文档/压缩包等)直接不处理 + } +} + +// detectMime 读取文件的 MIME 类型 +func detectMime(filePath string) string { + mt, err := mimetype.DetectFile(filePath) + if err != nil { + return "" + } + return mt.String() +} + +// isPotentialMedia 判断是否可能是媒体文件(交由 ffprobe 进一步确认) +func isPotentialMedia(fileMime string) bool { + if fileMime == "" || fileMime == "application/octet-stream" { + return true + } + if strings.HasPrefix(fileMime, "text/") { + return false + } + if strings.HasPrefix(fileMime, "video/") || strings.HasPrefix(fileMime, "audio/") || strings.HasPrefix(fileMime, "image/") { + return true + } + switch fileMime { + case "application/zip", "application/gzip", "application/x-gzip", "application/pdf", + "application/json", "application/xml", "application/x-httpd-php", "application/x-sh", + "application/javascript", "application/x-7z-compressed", "application/x-rar-compressed", + "application/x-tar", "application/x-bzip2", "application/x-archive", "application/vnd.sqlite3", + "application/vnd.microsoft.portable-executable", "application/x-dosexec", "application/x-executable", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint": + return false + } + return true +} + +// percent 计算百分比(0~100),total 为 0 时返回 100 +func percent(part, total int) int { + if total <= 0 { + return 100 + } + return int(float64(part) / float64(total) * 100) +} diff --git a/store/store.go b/internal/store/store.go similarity index 70% rename from store/store.go rename to internal/store/store.go index 2af452f..d13c796 100644 --- a/store/store.go +++ b/internal/store/store.go @@ -1,3 +1,4 @@ +// Package store 提供媒体信息的 SQLite 持久化(xorm + 纯 Go 驱动,免 CGO)。 package store import ( @@ -41,56 +42,62 @@ type MediaInfo struct { UpdatedAt time.Time `xorm:"updated 'updated_at'"` // 最近更新时间 } -var engine *xorm.Engine +// Store 封装 SQLite 存取,按实例持有连接,不再依赖全局状态 +type Store struct { + engine *xorm.Engine +} -// Init 初始化数据库引擎并同步表结构 -func Init(dbPath string) error { +// Open 打开(或创建)数据库并同步表结构 +func Open(dbPath string) (*Store, error) { e, err := xorm.NewEngine("sqlite3", dbPath) if err != nil { - return err + return nil, err } e.ShowSQL(false) e.SetMaxOpenConns(1) // sqlite同一时刻只允许一个写连接 - engine = e - return engine.Sync(new(MediaInfo)) + if err := e.Sync(new(MediaInfo)); err != nil { + _ = e.Close() + return nil, err + } + return &Store{engine: e}, nil } // Close 关闭数据库连接 -func Close() error { - if engine != nil { - err := engine.Close() - engine = nil - return err +func (s *Store) Close() error { + if s == nil || s.engine == nil { + return nil } - return nil + err := s.engine.Close() + s.engine = nil + return err } // SaveMedia 按MD5去重保存/更新媒体信息 -func SaveMedia(info *MediaInfo) error { - if engine == nil { +func (s *Store) SaveMedia(info *MediaInfo) error { + if s == nil || s.engine == nil { return nil } existing := new(MediaInfo) - has, err := engine.Where("file_md5 = ?", info.FileMd5).Get(existing) + has, err := s.engine.Where("file_md5 = ?", info.FileMd5).Get(existing) if err != nil { return err } if has { info.Id = existing.Id - _, err = engine.ID(existing.Id).Update(info) + _, err = s.engine.ID(existing.Id).Update(info) return err } - _, err = engine.Insert(info) + _, err = s.engine.Insert(info) return err } // FindMediaByFingerprint 按 (文件大小 + 头256KB + 尾256KB) 查找已入库记录,避免对大文件重复计算全量MD5 -func FindMediaByFingerprint(size int64, headMd5 string, tailMd5 string) (*MediaInfo, bool) { - if engine == nil || headMd5 == "" || tailMd5 == "" { +func (s *Store) FindMediaByFingerprint(size int64, headMd5, tailMd5 string) (*MediaInfo, bool) { + if s == nil || s.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) + has, err := s.engine.Where("file_size = ? AND head_md5 = ? AND tail_md5 = ?", size, headMd5, tailMd5).Get(info) if err != nil || !has { return nil, false } @@ -98,11 +105,11 @@ func FindMediaByFingerprint(size int64, headMd5 string, tailMd5 string) (*MediaI } // UpdateMediaDest 按源路径回填文件的归类目录与去向 -func UpdateMediaDest(filePath string, destDir string) error { - if engine == nil { +func (s *Store) UpdateMediaDest(filePath, destDir string) error { + if s == nil || s.engine == nil { return nil } - _, err := engine.Where("file_path = ?", filePath).Update(&MediaInfo{ + _, err := s.engine.Where("file_path = ?", filePath).Update(&MediaInfo{ Category: filepath.Base(destDir), DestDir: destDir, }) diff --git a/main.go b/main.go index 67e56de..baae015 100644 --- a/main.go +++ b/main.go @@ -1,58 +1,70 @@ package main import ( - _ "embed" "fmt" - _ "image/gif" // 导入gif支持 - _ "image/jpeg" // 导入jpeg支持 - _ "image/png" // 导入png支持 - "kenaito-media-picker/core" - "kenaito-media-picker/store" - "kenaito-media-picker/util" - "kenaito-media-picker/vars" "os" "path/filepath" "time" + + "kenaito-media-picker/internal/organize" + "kenaito-media-picker/internal/probe" + "kenaito-media-picker/internal/scanner" + "kenaito-media-picker/internal/store" ) func main() { fmt.Println("=== 警告:该程序会改变目录结构,将在5秒后开始执行") time.Sleep(time.Second * 5) + rootDir, err := os.Getwd() if err != nil { fmt.Println("=== 获取当前路径异常", err) return } pickerDir := filepath.Join(rootDir, filepath.Base(rootDir)+"-picker") - vars.GlobalPickerDir = pickerDir fmt.Printf("=== 整理输出目录(白名单): %s \n", pickerDir) - scanner := core.FileScanner{} - scanner.DoScan(rootDir) - scanner.DoFilter() - // 初始化媒体信息数据库(失败不影响整理,仅无法记录) - if err := store.Init(filepath.Join(rootDir, store.DBFileName)); err != nil { + + // 1. 扫描并过滤(跳过输出目录、数据库与 ffprobe 相关文件) + res, err := scanner.Scan(scanner.Options{ + RootDir: rootDir, + SkipDir: pickerDir, + SkipNames: []string{store.DBFileName, probe.FFprobeExeName, "ffprobe_mac"}, + }) + if err != nil { + fmt.Println("=== 扫描失败:", err) + return + } + res.Filter() + + // 2. 初始化媒体信息数据库(失败不影响整理,仅无法记录) + st, err := store.Open(filepath.Join(rootDir, store.DBFileName)) + if err != nil { fmt.Println("=== 初始化数据库失败(继续整理, 不记录媒体信息):", err) + st = nil } - defer store.Close() - // 释放内嵌ffprobe(按当前系统);失败只跳过媒体整理,图片照常处理 - ffprobeReady := true - if len(vars.GlobalImagePathList) > 0 || len(vars.GlobalMediaPathList) > 0 { - if err := util.ReleaseFFprobe(); err != nil { + if st != nil { + defer st.Close() + } + + // 3. 释放内嵌 ffprobe(按当前系统);失败只跳过媒体整理,图片照常处理 + ffprobePath := "" + if (len(res.Images) > 0 || len(res.Media) > 0) && probe.HasEmbeddedFFprobe() { + if err := probe.ReleaseFFprobe(); err != nil { fmt.Println("=== 释放解码器失败, 视频/音频整理将跳过(图片照常处理):", err) - ffprobeReady = false + } else { + ffprobePath = probe.FFprobePath() + defer probe.RemoveFFprobe() } } - defer util.RemoveFFprobe() - if len(vars.GlobalImagePathList) > 0 { - core.DoHandleImage(pickerDir) + + // 4. 整理 + org := organize.New(pickerDir, ffprobePath, st) + if len(res.Images) > 0 { + org.ProcessImages(res.Images) } - if len(vars.GlobalMediaPathList) > 0 && ffprobeReady { - core.DoHandleMedia(pickerDir) - } - // 所有移动完成后,回填文件的归类目录与去向 - for src, dest := range vars.GlobalPath2DestDirMap { - if err := store.UpdateMediaDest(src, dest); err != nil { - fmt.Printf("=== 更新媒体去向失败: %s, 错误: %v\n", src, err) - } + if len(res.Media) > 0 && ffprobePath != "" { + org.ProcessMedia(res.Media) } + org.Flush() + fmt.Println("=== 整理完成") } diff --git a/util/cale.go b/util/cale.go deleted file mode 100644 index d20bd5d..0000000 --- a/util/cale.go +++ /dev/null @@ -1,19 +0,0 @@ -package util - -import ( - "strconv" -) - -// String2int 字符串转int -func String2int(str string) int { - intValue, err := strconv.Atoi(str) - if err != nil { - return 0 - } - return intValue -} - -// CalcPercentage 计算percentage相对于total的百分比 -func CalcPercentage(percentage int, total int) int { - return int(float64(percentage) / float64(total) * 100) -} diff --git a/util/file.go b/util/file.go deleted file mode 100644 index 9de4ea2..0000000 --- a/util/file.go +++ /dev/null @@ -1,162 +0,0 @@ -package util - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "github.com/gabriel-vasile/mimetype" - "io" - "io/ioutil" - "os" - "path/filepath" -) - -// CreateDir 创建目录(含递归创建父目录) -func CreateDir(dirPath string) bool { - err := os.MkdirAll(dirPath, 0755) - if err != nil { - fmt.Printf("=== 创建目录失败: %s, 错误: %v\n", dirPath, err) - } - return err == nil -} - -// GetFileDirectory 获取文件所在文件夹 -func GetFileDirectory(filePath string) string { - directoryPath, _ := filepath.Split(filePath) - return directoryPath -} - -// ReadFileMimeInfo 获取文件mime信息,失败时返回nil -func ReadFileMimeInfo(filepath string) *mimetype.MIME { - mt, err := mimetype.DetectFile(filepath) - if err != nil { - fmt.Printf("=== 读取Mime信息失败: %s, 错误: %v\n", filepath, err) - return nil - } - return mt -} - -// CheckFileIsExist 判断文件是否存在,存在返回 true,不存在返回false -func CheckFileIsExist(filename string) bool { - var exist = true - if _, err := os.Stat(filename); os.IsNotExist(err) { - exist = false - } - return exist -} - -// WriteByteArraysToFile 写字节数组到文件 -func WriteByteArraysToFile(content []byte, filename string) error { - return ioutil.WriteFile(filename, content, 0777) -} - -// CalcFileMd5 计算文件MD5(分块读取,适合大文件) -func CalcFileMd5(filePath string) (string, error) { - file, err := os.Open(filePath) - if err != nil { - return "", err - } - defer file.Close() - h := md5.New() - if _, err := io.Copy(h, file); err != nil { - return "", err - } - return hex.EncodeToString(h.Sum(nil)), nil -} - -// MD5指纹加速相关常量 -const ( - FingerprintThreshold = 1 << 20 // 文件大于1MB才使用指纹复用,避免全量MD5 - fingerprintHeadSize = 256 << 10 // 文件头采样256KB - fingerprintTailSize = 256 << 10 // 文件尾采样256KB -) - -// CalcFileFingerprint 计算文件的头部与尾部采样MD5(只读取很少字节,用于快速识别相同文件) -func CalcFileFingerprint(filePath string, size int64) (headMd5 string, tailMd5 string, err error) { - file, err := os.Open(filePath) - if err != nil { - return "", "", err - } - defer file.Close() - h := md5.New() - if _, err := io.CopyN(h, file, fingerprintHeadSize); err != nil { - return "", "", err - } - headMd5 = hex.EncodeToString(h.Sum(nil)) - h = md5.New() - if _, err := file.Seek(size-fingerprintTailSize, io.SeekStart); err != nil { - return "", "", err - } - if _, err := io.CopyN(h, file, fingerprintTailSize); err != nil { - return "", "", err - } - tailMd5 = hex.EncodeToString(h.Sum(nil)) - return headMd5, tailMd5, nil -} - -//// 移动文件到根目录 -//func renameFile(rootDir string, modelType string, videoList []string, pathSeparator string) { -// total := len(videoList) -// var count = 0 -// bar := goPrint.NewBar(100) -// bar.SetNotice("=== 重命名文件:") -// bar.SetGraph(">") -// for _, videoFilePath := range videoList { -// wh := videoPath2WidthHeightTagMap[videoFilePath] -// fileName := vars.GlobalFilePath2FileNameMap[videoFilePath] -// if strings.Contains(fileName, videoTag) { // 处理过了 -// fileNames := strings.Split(fileName, videoTag) -// if len(fileNames) == 2 { -// fileName = fileNames[1] -// targetFilePath := rootDir + pathSeparator + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + fileName -// err := os.Rename(videoFilePath, targetFilePath) -// if err != nil { -// fmt.Printf("=== 重命名异常: %s \n", videoFilePath) -// } -// } -// } else { -// targetFilePath := rootDir + pathSeparator + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + " - " + fileName -// err := os.Rename(videoFilePath, targetFilePath) -// if err != nil { -// fmt.Printf("=== 重命名异常: %s \n", videoFilePath) -// } -// } -// count = count + 1 -// bar.PrintBar(util.CalcPercentage(count, total)) -// } -// bar.PrintEnd("=== Finish") -//} -// -//// 移动文件到原目录 -//func renameFileV2(modelType string, videoList []string) { -// total := len(videoList) -// var count = 0 -// bar := goPrint.NewBar(100) -// bar.SetNotice("=== 重命名文件:") -// bar.SetGraph(">") -// for _, videoFilePath := range videoList { -// wh := videoPath2WidthHeightTagMap[videoFilePath] -// fileName := vars.GlobalFilePath2FileNameMap[videoFilePath] -// filePath := util.GetFileDirectory(videoFilePath) -// if strings.Contains(fileName, videoTag) { // 处理过了 -// fileNames := strings.Split(fileName, videoTag) -// if len(fileNames) == 2 { -// fileName = fileNames[1] -// targetFilePath := filePath + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + fileName -// err := os.Rename(videoFilePath, targetFilePath) -// if err != nil { -// fmt.Printf("=== 重命名异常: %s \n", videoFilePath) -// } -// } -// } else { -// targetFilePath := filePath + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + " - " + fileName -// err := os.Rename(videoFilePath, targetFilePath) -// if err != nil { -// fmt.Printf("=== 重命名异常: %s \n", videoFilePath) -// } -// } -// count = count + 1 -// bar.PrintBar(util.CalcPercentage(count, total)) -// } -// bar.PrintEnd("=== Finish") -//} diff --git a/util/image.go b/util/image.go deleted file mode 100644 index dae7c7e..0000000 --- a/util/image.go +++ /dev/null @@ -1,43 +0,0 @@ -package util - -import ( - "image" - "os" - "strings" -) - -var supportImageTypes = []string{ - ".bmp", - ".gif", - ".jpg", - ".jpeg", - ".jpe", - ".png", - ".webp", - ".psb", - ".jfif", -} - -// IsSupportImage 判断是否属于支持的图片文件 -func IsSupportImage(imageType string) bool { - for _, supportImageType := range supportImageTypes { - if strings.EqualFold(supportImageType, strings.ToLower(imageType)) { - return true - } - } - return false -} - -// ReadImageInfo 读取一般图片文件信息 -func ReadImageInfo(filePath string) (err error, width int, height int) { - file, err := os.Open(filePath) // 图片文件路径 - if err != nil { - return err, 0, 0 - } - defer file.Close() - img, _, err := image.DecodeConfig(file) - if err != nil { - return err, 0, 0 - } - return nil, img.Width, img.Height -} diff --git a/util/video.go b/util/video.go deleted file mode 100644 index 452996d..0000000 --- a/util/video.go +++ /dev/null @@ -1,221 +0,0 @@ -package util - -import ( - "encoding/json" - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - - "kenaito-media-picker/vars" - - "github.com/redmask-hb/GoSimplePrint/goPrint" - uuid "github.com/satori/go.uuid" -) - -// FFprobeExeName 内嵌解码器文件名 -const FFprobeExeName = "ffprobe.exe" - -// FFprobeExePath 返回带当前目录前缀的可执行路径(Windows/Unix均可直接执行) -func FFprobeExePath() string { - return "." + string(os.PathSeparator) + FFprobeExeName -} - -// ReleaseFFprobe 释放内嵌的ffprobe到当前目录(按当前系统自动选择对应版本) -func ReleaseFFprobe() error { - if len(vars.VideoDecodeTool) == 0 { - return fmt.Errorf("当前系统没有内嵌的ffprobe") - } - return WriteByteArraysToFile(vars.VideoDecodeTool, FFprobeExeName) -} - -// RemoveFFprobe 删除当前目录下的ffprobe -func RemoveFFprobe() { - if CheckFileIsExist(FFprobeExeName) { - _ = os.Remove(FFprobeExeName) - } -} - -// StreamInfo ffprobe流信息 -type StreamInfo struct { - Index int `json:"index"` - CodecType string `json:"codec_type"` - CodecName string `json:"codec_name"` - Width int `json:"width"` - Height int `json:"height"` -} - -// FormatInfo ffprobe容器信息 -type FormatInfo struct { - Duration string `json:"duration"` - Format string `json:"format_name"` -} - -// ProbeResult ffprobe探测结果 -type ProbeResult struct { - Streams []StreamInfo `json:"streams"` - Format FormatInfo `json:"format"` - raw string -} - -// VideoResolution 返回第一个有效的视频分辨率 -func (p *ProbeResult) VideoResolution() (int, int) { - for _, s := range p.Streams { - if s.CodecType == "video" && s.Width > 0 && s.Height > 0 { - return s.Width, s.Height - } - } - return 0, 0 -} - -// HasAudio 是否包含音频流 -func (p *ProbeResult) HasAudio() bool { - for _, s := range p.Streams { - if s.CodecType == "audio" { - return true - } - } - return false -} - -// VideoCodec 第一个视频流的编码名称 -func (p *ProbeResult) VideoCodec() string { - for _, s := range p.Streams { - if s.CodecType == "video" { - return s.CodecName - } - } - return "" -} - -// AudioCodec 第一个音频流的编码名称 -func (p *ProbeResult) AudioCodec() string { - for _, s := range p.Streams { - if s.CodecType == "audio" { - return s.CodecName - } - } - return "" -} - -// FormatName 容器格式名称 -func (p *ProbeResult) FormatName() string { - return p.Format.Format -} - -// RawJSON 原始ffprobe JSON输出 -func (p *ProbeResult) RawJSON() string { - return p.raw -} - -// DurationSeconds 容器时长(秒),无法解析时返回0 -func (p *ProbeResult) DurationSeconds() float64 { - d, err := strconv.ParseFloat(p.Format.Duration, 64) - if err != nil { - return 0 - } - return d -} - -// ProbeMediaFile 用ffprobe探测媒体文件,一次调用同时获取视频/音频流与时长 -func ProbeMediaFile(ffprobeExecPath string, filePath string) (*ProbeResult, error) { - cmd := exec.Command(ffprobeExecPath, "-v", "error", - "-show_entries", "stream=index,codec_type,codec_name,width,height:format=duration,format_name", - "-of", "json", filePath) - output, err := cmd.Output() - if err != nil { - return nil, fmt.Errorf("ffprobe failed: %w", err) - } - var result ProbeResult - if err := json.Unmarshal(output, &result); err != nil { - return nil, fmt.Errorf("failed to parse ffprobe output: %w", err) - } - result.raw = string(output) - return &result, nil -} - -// IsPotentialMedia 判断是否可能是媒体文件(交由ffprobe进一步确认) -func IsPotentialMedia(fileMime string) bool { - if fileMime == "" || fileMime == "application/octet-stream" { - return true - } - if strings.HasPrefix(fileMime, "text/") { - return false - } - if strings.HasPrefix(fileMime, "video/") || strings.HasPrefix(fileMime, "audio/") || strings.HasPrefix(fileMime, "image/") { - return true - } - switch fileMime { - case "application/zip", "application/gzip", "application/x-gzip", "application/pdf", - "application/json", "application/xml", "application/x-httpd-php", "application/x-sh", - "application/javascript", "application/x-7z-compressed", "application/x-rar-compressed", - "application/x-tar", "application/x-bzip2", "application/x-archive", "application/vnd.sqlite3", - "application/vnd.microsoft.portable-executable", "application/x-dosexec", "application/x-executable", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint": - return false - } - return true -} - -// DoMoveFileToDir 批量移动文件到目录 -func DoMoveFileToDir(filePathList []string, videoDirPath string) { - total := len(filePathList) - var count = 0 - bar := goPrint.NewBar(100) - bar.SetNotice("=== 移动文件到目录:") - bar.SetGraph(">") - pathSeparator := string(os.PathSeparator) - for _, videoFilePath := range filePathList { - moveFileToDir(videoFilePath, videoDirPath+pathSeparator) - vars.GlobalPath2DestDirMap[videoFilePath] = videoDirPath // 记录去向 - count = count + 1 - bar.PrintBar(CalcPercentage(count, total)) - } - bar.PrintEnd("=== Finish") -} - -// 移动文件到目录,跨文件系统时自动 fallback 为 copy+delete,目标文件名使用 uuid+原文件名防止同名覆盖 -func moveFileToDir(sourceFilePath string, targetDirectory string) bool { - fileName := strings.ReplaceAll(uuid.NewV4().String(), "-", "") + "_" + filepath.Base(sourceFilePath) - targetFilePath := targetDirectory + fileName - err := os.Rename(sourceFilePath, targetFilePath) - if err == nil { - return true - } - // 跨文件系统等场景:fallback copy + delete - if err := copyFile(sourceFilePath, targetFilePath); err != nil { - fmt.Printf("=== 复制文件失败, 源: %s, 目标: %s, 错误: %v\n", sourceFilePath, targetFilePath, err) - return false - } - if err := os.Remove(sourceFilePath); err != nil { - fmt.Printf("=== 删除源文件失败: %s, 错误: %v\n", sourceFilePath, err) - // 复制成功但删除失败,文件已存在于两处,返回 true 表示目标已就位 - } - return true -} - -// copyFile 逐块复制文件 -func copyFile(src, dst string) error { - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() - - out, err := os.Create(dst) - if err != nil { - return err - } - defer out.Close() - - if _, err := io.Copy(out, in); err != nil { - return err - } - return out.Close() -} diff --git a/vars/ffprobe_darwin.go b/vars/ffprobe_darwin.go deleted file mode 100644 index 10ce7c9..0000000 --- a/vars/ffprobe_darwin.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build darwin - -package vars - -import _ "embed" - -//go:embed bin/ffprobe_mac -var VideoDecodeTool []byte // 需在 vars/bin/ 下放置 macOS 版 ffprobe(未入库,见 .gitignore) diff --git a/vars/ffprobe_unsupported.go b/vars/ffprobe_unsupported.go deleted file mode 100644 index 93ad773..0000000 --- a/vars/ffprobe_unsupported.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !darwin && !windows - -package vars - -// VideoDecodeTool 当前平台没有内嵌的ffprobe -var VideoDecodeTool []byte diff --git a/vars/ffprobe_windows.go b/vars/ffprobe_windows.go deleted file mode 100644 index edeba9f..0000000 --- a/vars/ffprobe_windows.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build windows - -package vars - -import _ "embed" - -//go:embed bin/ffprobe.exe -var VideoDecodeTool []byte // 需在 vars/bin/ 下放置 Windows 版 ffprobe(未入库,见 .gitignore) diff --git a/vars/global.go b/vars/global.go deleted file mode 100644 index 8449db2..0000000 --- a/vars/global.go +++ /dev/null @@ -1,11 +0,0 @@ -package vars - -var GlobalFilePathList []string // 所有文件的路径 -var GlobalFilePath2MimeInfoMap = make(map[string]string) // 文件路径和mime信息的映射关系 -var GlobalFilePath2FileNameMap = make(map[string]string) // 文件路径和文件名称的映射关系 -var GlobalFilePath2FileExtMap = make(map[string]string) // 文件路径和文件扩展名的映射关系 -var GlobalImagePathList []string // 所有图片文件的路径 -var GlobalMediaPathList []string // 待ffprobe确认的媒体候选文件 -var GlobalAudioPathList []string // 已确认的音频文件路径 -var GlobalPickerDir string // 已整理文件的输出目录(白名单,扫描时跳过) -var GlobalPath2DestDirMap = make(map[string]string) // 文件路径 → 移动到的目录(去向) diff --git a/vars/image.go b/vars/image.go deleted file mode 100644 index 2b360f7..0000000 --- a/vars/image.go +++ /dev/null @@ -1,81 +0,0 @@ -package vars - -var ImageIgnorePathList []string // 忽略的文件路径 -var ImageReadErrorPathList []string // 读取信息异常的路径 -var ImagePath2WidthHeightMap = make(map[string]string) // 图片路径和宽高比 -var ImageGifList []string -var ImagePsdList []string -var ImageHorizontalNormalList []string -var ImageHorizontal1KList []string -var ImageHorizontal2KList []string -var ImageHorizontal3KList []string -var ImageHorizontal4KList []string -var ImageHorizontal5KList []string -var ImageHorizontal6KList []string -var ImageHorizontal7KList []string -var ImageHorizontal8KList []string -var ImageHorizontal9KList []string -var ImageHorizontal10KList []string -var ImageHorizontal11KList []string -var ImageHorizontal12KList []string -var ImageHorizontal13KList []string -var ImageHorizontal14KList []string -var ImageHorizontal15KList []string -var ImageHorizontal16KList []string -var ImageHorizontal17KList []string -var ImageHorizontal18KList []string -var ImageHorizontal19KList []string -var ImageHorizontalHKList []string -var ImageHorizontalStandard720PList []string -var ImageHorizontalStandard1D5KList []string -var ImageHorizontalStandard1080PList []string -var ImageHorizontalStandard2DKList []string -var ImageHorizontalStandard2D5KList []string -var ImageHorizontalStandardUltraWide2List []string -var ImageHorizontalStandardUltraWide3List []string -var ImageHorizontalStandardUltraWide4List []string -var ImageHorizontalStandard4KList []string -var ImageHorizontalStandard4KHList []string -var ImageHorizontalStandard8KList []string -var ImageVerticalNormalList []string -var ImageVertical1KList []string -var ImageVertical2KList []string -var ImageVertical3KList []string -var ImageVertical4KList []string -var ImageVertical5KList []string -var ImageVertical6KList []string -var ImageVertical7KList []string -var ImageVertical8KList []string -var ImageVertical9KList []string -var ImageVertical10KList []string -var ImageVertical11KList []string -var ImageVertical12KList []string -var ImageVertical13KList []string -var ImageVertical14KList []string -var ImageVertical15KList []string -var ImageVertical16KList []string -var ImageVertical17KList []string -var ImageVertical18KList []string -var ImageVertical19KList []string -var ImageVerticalHKList []string -var ImageSquareNormalList []string -var ImageSquare1KList []string -var ImageSquare2KList []string -var ImageSquare3KList []string -var ImageSquare4KList []string -var ImageSquare5KList []string -var ImageSquare6KList []string -var ImageSquare7KList []string -var ImageSquare8KList []string -var ImageSquare9KList []string -var ImageSquare10KList []string -var ImageSquare11KList []string -var ImageSquare12KList []string -var ImageSquare13KList []string -var ImageSquare14KList []string -var ImageSquare15KList []string -var ImageSquare16KList []string -var ImageSquare17KList []string -var ImageSquare18KList []string -var ImageSquare19KList []string -var ImageSquareHKList []string diff --git a/vars/video.go b/vars/video.go deleted file mode 100644 index 3e5436c..0000000 --- a/vars/video.go +++ /dev/null @@ -1,82 +0,0 @@ -package vars - -var VideoIgnorePathList []string // 忽略的文件路径 -var VideoReadErrorPathList []string // 读取信息异常的路径 -var VideoPath2WidthHeightMap = make(map[string]string) // 视频路径和宽高比 -var VideoHorizontalNormalList []string -var VideoHorizontalGifList []string -var VideoHorizontal1KList []string -var VideoHorizontal2KList []string -var VideoHorizontal3KList []string -var VideoHorizontal4KList []string -var VideoHorizontal5KList []string -var VideoHorizontal6KList []string -var VideoHorizontal7KList []string -var VideoHorizontal8KList []string -var VideoHorizontal9KList []string -var VideoHorizontal10KList []string -var VideoHorizontal11KList []string -var VideoHorizontal12KList []string -var VideoHorizontal13KList []string -var VideoHorizontal14KList []string -var VideoHorizontal15KList []string -var VideoHorizontal16KList []string -var VideoHorizontal17KList []string -var VideoHorizontal18KList []string -var VideoHorizontal19KList []string -var VideoHorizontalHKList []string -var VideoHorizontalStandard720PList []string -var VideoHorizontalStandard1D5PList []string -var VideoHorizontalStandard1080PList []string -var VideoHorizontalStandard2DKList []string -var VideoHorizontalStandard2D5KList []string -var VideoHorizontalStandardUltraWide2List []string -var VideoHorizontalStandardUltraWide3List []string -var VideoHorizontalStandardUltraWide4List []string -var VideoHorizontalStandard4KList []string -var VideoHorizontalStandard4KHList []string -var VideoHorizontalStandard8KList []string -var VideoVerticalNormalList []string -var VideoVerticalGifList []string -var VideoVertical1KList []string -var VideoVertical2KList []string -var VideoVertical3KList []string -var VideoVertical4KList []string -var VideoVertical5KList []string -var VideoVertical6KList []string -var VideoVertical7KList []string -var VideoVertical8KList []string -var VideoVertical9KList []string -var VideoVertical10KList []string -var VideoVertical11KList []string -var VideoVertical12KList []string -var VideoVertical13KList []string -var VideoVertical14KList []string -var VideoVertical15KList []string -var VideoVertical16KList []string -var VideoVertical17KList []string -var VideoVertical18KList []string -var VideoVertical19KList []string -var VideoVerticalHKList []string -var VideoSquareNormalList []string -var VideoSquareGifList []string -var VideoSquare1KList []string -var VideoSquare2KList []string -var VideoSquare3KList []string -var VideoSquare4KList []string -var VideoSquare5KList []string -var VideoSquare6KList []string -var VideoSquare7KList []string -var VideoSquare8KList []string -var VideoSquare9KList []string -var VideoSquare10KList []string -var VideoSquare11KList []string -var VideoSquare12KList []string -var VideoSquare13KList []string -var VideoSquare14KList []string -var VideoSquare15KList []string -var VideoSquare16KList []string -var VideoSquare17KList []string -var VideoSquare18KList []string -var VideoSquare19KList []string -var VideoSquareHKList []string