# kenaito-media-picker 媒体分辨率分类整理工具:一键扫描目录下所有图片与视频,按横屏 / 竖屏 / 等比和分辨率自动归类到对应子目录。 ## 功能特性 - **图片 / 视频 / 音频**:可指定任意目录进行扫描(递归),自动识别图片、视频与音频文件;不填目录时默认扫描程序当前运行目录 - **标准分辨率归类**:720P、1080P、2K、2.5K、影院 2K、DCI 4K、8K 及带鱼屏(UltraWide 2K/3K/4K)等标准规格单独建目录 - **非标分辨率归类**:按像素区间(每 1024px 一档)自动归入 1K ~ 19K / 普通 / 原图(原画质)目录 - **方向区分**:横屏、竖屏、等比(正方形)分开整理,GIF 单独处理 - **EXIF 旋转修正**:手机/相机竖拍的图(文件里横着存、带 EXIF 旋转标记)自动识别并转正——找到 ffmpeg 时用 ffmpeg 把像素真正转正;找不到时至少按"显示方向"(宽高对调)归类,不会再误入横屏目录 - **明暗分类**:基于 ITU-R BT.601 亮度公式分析每张图片明暗程度,暗色(亮度 < 80)像素占比为 darkScore、亮色(亮度 > 175)像素占比为 lightScore;以两者差幅判定亮暗主导,在目录名末尾追加 `-dark_N` / `-mid` / `-bright_N`(N 为占比四舍五入到 10% 的档位) - **音频归类**:音频文件单独归入「音频」目录 - **异常兜底**:读取失败、不支持的格式自动归入「读取异常」「已忽略」目录 - **自动整理**:无需人工干预,扫描 → 分析 → 移动一次完成,跨磁盘自动降级为复制后删除 - **全格式支持**:视频/音频以**内嵌的 ffprobe**(Windows 版)作为最终判定依据,**凡是 ffmpeg 支持的格式都能识别**,不再依赖扩展名白名单 - **媒体信息入库**:自动将文件的 MD5 与完整媒体信息(类型/宽高/时长/编码/容器等)存入根目录下的 `media-info.db`(SQLite,xorm 驱动),支持按字段查询与去重 ## 支持的格式 | 类型 | 格式 | |------|------| | 图片 | JPG / JPEG / JPE / JFIF / PNG / GIF / BMP / WEBP / PSB,PSD 单独归入「图片_PSD」;Go 无法解码的图片格式(如 TIFF)由 ffprobe 兜底 | | 视频 | 凡 ffmpeg 支持的容器/编码均可识别(MP4 / MKV / AVI / MOV / TS / FLV / WMV / RM / RMVB / WEBM / M2TS 等),以 ffprobe 探测结果为准 | | 音频 | 凡 ffmpeg 支持的音频(MP3 / WAV / FLAC / AAC / M4A / OGG / WMA 等)归入「音频」目录 | > 判断依据:图片按文件 MIME + 扩展名识别(扩展名大小写不敏感);视频/音频由 ffprobe 读取媒体流判定,支持范围与 ffmpeg 一致。 ## 构建编译 ```shell sh build.sh ``` - 依赖 Go 1.25.0+(`go.mod` 指定 1.25.0) - 构建时内嵌 **Windows 版 ffprobe**:`internal/probe/bin/ffprobe.exe`,需提前放置(未入库,见 `.gitignore`) - `build.sh` 在 **Windows** 下先构建前端、再 `go build` + `upx` 压缩,输出 `kenaito-media-picker.exe`(Web 服务 + 内嵌前端) - 编译所需工具(ffmpeg / upx)压缩包已放在 `doc/` 目录,按需解压使用(upx 放置于项目根目录 `upx`) ## 使用方法 ```text # 双击有风险, 双击需谨慎 将 kenaito-media-picker.exe 拖拽到需要整理的文件夹内,双击运行即可 ``` 程序启动后自动打开浏览器进入 **Web 管理界面**(默认 http://localhost:8080), 媒体整理不再是自动执行,而是在页面上点击 **【扫描整理】** 后开始(整理会移动文件,请先备份)。 ## Web 管理界面 项目提供 **Web 管理界面**(Vue 2 + ElementUI,内嵌进程序二进制),负责触发扫描整理与浏览 `media-info.db` 媒体库。 ### 运行服务 ```shell # 需要 Node.js 的机器上先构建前端(dist 会被 go:embed 打入程序) cd frontend npm install && npm run build # 回到项目根目录编译(Windows 下输出 kenaito-media-picker.exe) go build . ./kenaito-media-picker.exe ``` - 默认监听 `:8080`,启动后自动打开浏览器(配置 `server.autoOpen=false` 可关闭) - 端口 / 数据库路径 / 自动打开浏览器可在 `application.properties` 配置:`server.port` / `database.dsn`(相对路径相对服务启动目录)/ `server.autoOpen` ### 开发模式(无需重复构建前端) 日常开发用 **Vite 开发服务器 + Go 源码运行**,前端改动实时热更新,不需要每次 `npm run build`: ```shell # 终端 1:启动后端(源码运行,端口 8080) go run . # 终端 2:启动前端开发服务器(端口 3000,/api 自动代理到 8080) cd frontend npm install && npm run dev ``` - 浏览器访问 **http://localhost:3000** 即可联调 - 改前端文件 → Vite 热更新自动生效;改后端 → 重启 `go run .` **只有发布时才需要重新构建前端**:执行 `npm run build` 把产物打进 `frontend/dist`,再 `go build .` 重新编译,前端改动才会进入二进制。 ### 页面功能 | 页面 | 功能 | |------|------| | 扫描整理 | 点击「选择目录」浏览服务器磁盘(只列目录),也可手动输入;点击【开始扫描】触发扫描归类,实时显示运行日志与进度 | | 统计看板 | 媒体总数/总体积/重复文件、类型/方向/分辨率/编码/格式分布、体积 TOP10 | | 媒体库 | 按类型/方向/分辨率/编码/格式/文件名筛选,分页 + 列排序 | > 分辨率统计与筛选复用归类规则(标准规格 / 1K~19K / 普通 / 原图),与归类结果一致。 ## 整理规则 执行后,会在当前目录生成一批以 UUID 开头命名的子目录,文件按规则移动(或复制)进去: ### 图片 ```text {uid}-图片_横屏_720P-dark_100 ← 1280×720,暗色主导 {uid}-图片_横屏_1D5KP-mid ← 1600×900,明暗均衡 {uid}-图片_横屏_1080P-bright_100 ← 1920×1080,亮色主导 {uid}-图片_横屏_2DKP-dark_70 ← 2048×1080(影院 2K) {uid}-图片_竖屏_... ← 竖屏按同样规则(普通 / 1K~19K / 原图) {uid}-图片_等比_... ← 等比(正方形)按同样规则 {uid}-图片_GIF ← 所有 GIF 图片 {uid}-图片_PSD ← 所有 PSD 文件 {uid}-图片_读取异常 ← 无法读取尺寸的图片 {uid}-图片_已忽略 ← 不支持格式的图片 ``` ### 视频 / 音频 ```text {uid}-视频_横屏_720P / 1080P / 2DKP / 2D5KP / 4KP / 4KHP / 8KP {uid}-视频_横屏_UltraWide2KP / UltraWide3KP / UltraWide4KP {uid}-视频_横屏_1k ~ 19k / 普通 / 原画质 {uid}-视频_竖屏_1k ~ 19k / 普通 / 原画质 {uid}-视频_等比_1k ~ 19k / 普通 / 原画质 {uid}-视频_横屏_GIF / 视频_竖屏_GIF / 视频_等比_GIF 音频 ← 所有音频文件(ffmpeg 可识别的格式) 视频_读取异常 ← mime 为视频但 ffprobe 读取失败 视频_已忽略 ← 非媒体文件或无法识别 ``` ### 重复文件 ```text 重复文件 ← 与库中已有记录 MD5 相同的图片/视频/音频(不去重入库,单独存放) ``` > 文件按 MD5 去重:已入库的同一内容文件(不论图片、视频还是音频)统一移入「重复文件」目录。 > 视频时长与分辨率通过内嵌 ffprobe 读取(处理时会临时释放到当前目录、完成后自动删除);凡 ffmpeg 支持的视频/音频格式均可识别。 ## 明暗评分算法 ```text brightness = 0.299×R + 0.587×G + 0.114×B (ITU-R BT.601) darkScore = 亮度 < 80 的像素占比 lightScore = 亮度 > 175 的像素占比 ``` - **判定规则**:`lightScore` 高于 `darkScore` 15 个百分点以上归 `-bright_N`,反之归 `-dark_N`;差幅不足 15% 视为中性归 `-mid`(不强制二选一) - **档位命名**:N 为占比四舍五入到 10% 的档位(10~100),如 darkScore 0.70 → `dark_70`,表示占比命中 [N-5%, N+5%) 区间 - **透明处理**:完全透明的像素(alpha=0)不计入统计,全透明图片归 `-mid` - **性能**:大图(>100 万像素)先等比缩放为最长边 512px 的缩略图(纯内存)再统计明暗,内存占用与耗时大幅下降;超 4000 万像素的图片直接跳过明暗分析(按中性 mid 归类)避免 OOM ## 目录结构 ```text ├── main.go # 程序入口(Web 服务:Gin + 内嵌前端产物) ├── internal/ │ ├── scanner/ # 文件扫描、MIME 读取、图片/媒体候选过滤 │ ├── probe/ # 媒体探测:图片尺寸 + ffprobe 解析 + 内嵌解码器释放 │ ├── classify/ # 纯函数归类规则(方向 + 标准分辨率 + K档 + 明暗等级) │ ├── organize/ # 整理编排:探测→归类→收集→移动→记录去向 │ ├── mover/ # 文件移动(rename / 跨盘 copy+delete) │ ├── store/ # SQLite 媒体信息持久化 + 查询/统计 │ ├── service/ # 后台任务编排(扫描整理任务 + 状态/日志) │ ├── config/ # Web 服务配置加载(application.properties) │ ├── api/ # HTTP 接口层(统一响应 / 媒体列表 / 统计 / 扫描触发) │ └── digest/ # 文件 MD5 / 指纹采样 ├── frontend/ # 前端工程(Vue 2 + ElementUI + Vite,dist 内嵌进程序) ├── application.properties # Web 服务配置(端口 / 数据库路径) ├── doc/ # 构建依赖(ffmpeg / upx 压缩包) └── build.sh # 编译脚本(upx 压缩) ``` ## 注意事项 - 默认使用**移动**模式,原文件会从源目录移走;同名文件跨磁盘时自动降级为复制 + 删除 - 已整理文件全部移动到 `KMP已整理` 白名单目录内,重复运行会跳过该目录,并将新文件并入已有的分类目录 - 扫描整理由页面点击触发,整理期间服务会短暂占用较多 CPU/磁盘,属正常现象 - 同一时间只允许一次扫描整理;重复点击会提示「正在运行中」 - GIF 图片单独归入「图片_GIF」;GIF 视频按方向归入「视频_*_GIF」 - ffprobe 内嵌(Windows),运行前释放到当前目录、结束后自动删除 - EXIF 旋转转正需要 ffmpeg:配置 `ffmpeg.path` 指定路径,或让 ffmpeg 在 PATH 里;找不到 ffmpeg 时不转正像素,仍按显示方向归类。可用 `organize.rotateImages=false` 关闭 ## License Apache 2.0