2025-09-01 21:11:28 +08:00
upd
2025-09-01 21:11:28 +08:00
upd
2025-09-01 21:11:28 +08:00
upd
2025-08-26 21:07:42 +08:00
2025-02-26 21:14:42 +08:00
upd
2025-08-26 21:07:42 +08:00
upd
2025-08-26 21:07:42 +08:00
upd
2025-05-05 21:13:46 +08:00
upd
2025-05-05 21:13:46 +08:00
upd
2025-08-26 21:07:42 +08:00

kenaito-media-picker

媒体文件自动分类整理工具:一键扫描目录下的图片 / 视频 / 音频,按 方向(横屏 / 竖屏 / 等比)+ 分辨率 + 明暗 自动归入对应子目录,并提供 Web 管理界面(实时进度、运行日志、媒体库、统计看板、可选色情识别、可选二次元识别)。

功能特性

  • 全类型识别:图片、视频、音频均可扫描(递归);视频/音频以内嵌 ffprobe 探测媒体流为准,凡 ffmpeg 支持的格式都能识别,不依赖扩展名白名单
  • 方向区分:横屏、竖屏、等比(正方形)分开整理
  • 标准分辨率归类:720P、1D5KP、1080P、2DKP、2D5KP、UltraWide 2K/3K/4K、4KP、4KHP、8KP 等精确匹配单独建目录
  • 非标分辨率归类:按长边每 1024px 一档归入 1K ~ 19K,不足一档归「普通」,超出归「原图」(视频为「原画质」)
  • 明暗分类:基于 ITU-R BT.601 亮度公式,在分辨率目录下追加 dark_N / mid / bright_N 子目录(N 取 20/40/60/80/100 共 5 档)
  • EXIF 旋转修正:带旋转标记的竖拍图自动处理——有 ffmpeg 时物理转正像素,否则至少按显示方向(宽高对调)归类;可在配置中整体关闭
  • 色情识别(可选):本机 Python + NudeNet 离线鉴黄,命中的图片移入 图片_敏感_* 目录并记录检测分,Web 端可逐张预览复核
  • 二次元识别(可选):本机 Python + wd-tagger 识别二次元/动漫风格图片,输出 safe/questionable/explicit 三级分级,命中的图片移入 图片_二次元_* 目录并记录分级标签与 top tags,Web 端可逐张预览复核
  • 异常兜底:读取失败、不支持格式自动归入「读取异常」「已忽略」目录;GIF、PSD 单独归类
  • 断点续传:每个文件归类结果实时写入续传表(scan_log),扫描中断/出错后重跑时,已归类过的文件直接复用上次分类,不再重新探测尺寸 / 算 MD5 / 分析明暗 / 色情识别
  • 媒体信息入库:自动把文件 MD5 与完整媒体信息(类型 / 宽高 / 时长 / 编码 / 容器等)写入 media-info.db(SQLite),支持筛选查询与去重
  • Web 管理界面:页面点击触发扫描整理,WebSocket 实时推送进度与运行日志;支持中止
  • 并行处理:探测 / 归类 / 移动各阶段并发 worker 数可配置(默认 4)

支持的格式

类型 格式
图片 JPG / JPEG / JPE / JFIF / PNG / GIF / BMP / WEBP / PSD 等;Go 无法解码的格式由 ffprobe 兜底
视频 凡 ffmpeg 支持的容器/编码(MP4 / MKV / AVI / MOV / TS / FLV / WMV / RM / RMVB / WEBM / M2TS 等),以 ffprobe 探测结果为准
音频 凡 ffmpeg 支持的音频(MP3 / WAV / FLAC / AAC / M4A / OGG / WMA 等),统一归入「音频」目录

图片按 MIME + 扩展名识别(大小写不敏感);视频/音频由 ffprobe 读取媒体流判定。

快速开始

运行

运行编译出的 exe(如 kenaito-media-picker.exe),程序启动后自动打开浏览器进入
Web 管理界面(默认 http://localhost:8080)。

整理不再自动执行——在页面上点击 【扫描整理】 后开始(会移动文件,请先备份)。

Web 页面

页面 功能
扫描整理 选择/输入扫描目录与存放目录,点击开始扫描,实时查看进度、运行日志,可中止
统计看板 媒体总数 / 总体积 / 平均·最大·最小文件 / 总时长,以及类型、方向、分辨率、编码、格式、扩展名等分布与体积 TOP10
媒体库 按类型 / 方向 / 分辨率 / 编码 / 格式 / 文件名筛选,分页 + 列排序
色情识别 展示最近一次开启识别扫描命中的图片:检测分 + 完整路径 + 缩略图预览,便于人工复核
二次元识别 展示最近一次开启识别扫描命中的二次元图片:分级(safe/questionable/explicit)+ 检测分 + top 标签 + 完整路径 + 缩略图预览,便于人工复核

配置(application.properties)

程序运行目录下的 application.properties,用记事本即可修改:

配置项 默认值 说明
server.port 8080 Web 服务端口
server.autoOpen true 启动后是否自动打开浏览器
database.dsn media-info.db 媒体数据库路径(相对路径相对运行目录)
ffmpeg.path 空 ffmpeg 路径,用于 EXIF 旋转转正;留空自动在 PATH 找
organize.rotateImages true 是否处理 EXIF 旋转:true 转正/按显示方向归类;false 完全不处理,按存储方向原样归类
organize.workers 4 探测/归类/移动的并发 worker 数(0 = 用满全部 CPU 核)
nsfw.enabled false 是否开启色情图片识别
nsfw.python 空 python 可执行文件路径;留空自动在 PATH 找
nsfw.threshold 0.5 判定阈值(0~1),敏感部位检测分达到即判为色情
hentai.enabled false 是否开启二次元图片识别
hentai.python 空 python 可执行文件路径;留空自动在 PATH 找(可与 nsfw.python 相同)
hentai.threshold 0.5 判定阈值(0~1),questionable 或 explicit 分数达到即判为二次元敏感

整理规则

整理结果统一放进输出目录下的 KMP已整理 目录(默认在扫描目录下创建,也可在页面指定存放目录)。文件名会加一长串随机前缀(如 a1b2..._照片.jpg),防止重名覆盖。

KMP已整理/
├── 图片_横屏_1080P/
│   ├── bright_20/  ← 偏亮
│   ├── mid/        ← 明暗均衡
│   └── dark_40/    ← 偏暗
├── 图片_竖屏_2K/
├── 图片_等比_普通/          ← 非标分辨率:普通 / 1K~19K / 原图
├── 图片_横屏_UltraWide3KP/
├── 图片_GIF/
├── 图片_PSD/                ← PSD 设计稿原样收走
├── 图片_读取异常/ 图片_已忽略/
├── 图片_敏感_横屏_1080P/bright_20/   ← 判为色情的图片(明暗子目录照常)
├── 图片_二次元_横屏_1080P/bright_20/ ← 判为二次元敏感的图片(明暗子目录照常)
├── 视频_横屏_1080P/ 视频_竖屏_1K/ 视频_等比_原画质/
├── 视频_横屏_GIF/ 视频_竖屏_GIF/ 视频_等比_GIF/
├── 视频_读取异常/ 视频_已忽略/
└── 音频/

标准分辨率档位:720P / 1D5KP / 1080P / 2DKP / 2D5KP / UltraWide2KP / UltraWide3KP / UltraWide4KP / 4KP / 4KHP / 8KP。

明暗评分算法

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 为占比四舍五入到 20% 的档位(20/40/60/80/100 共 5 档),如 darkScore 0.70 → dark_80
  • 透明处理:完全透明的像素(alpha=0)不计入统计,全透明图片归 mid
  • 性能:超过 25 万像素的图先等比缩放为最长边 256px 的缩略图(纯内存)再统计;超过 4000 万像素直接跳过明暗分析(按 mid 归类),避免内存暴涨

去重

与数据库中已有记录 MD5 相同且源路径相同 的文件视为"同一文件被重复扫到",原地忽略(不移动、不重复入库)。MD5 相同但路径不同视为不同文件,照常整理。

断点续传(扫描表)

每次归类一个文件,程序就会把它「源路径 → 目标目录」记进数据库的续传表(scan_log)。之后扫描中断、出错、甚至中途手动中止再重跑时:

  • 已归类过且内容没变的文件 → 跳过全部探测/识别(尺寸、MD5、明暗、色情识别都不再重做),直接复用上次的目标目录,没搬走的这次搬走;
  • 内容变化(文件大小或头尾指纹变了)→ 当作新文件重新探测归类;
  • 大文件(>1MB)用「大小 + 头尾 256KB 指纹」校验内容,小文件只比对大小。

所以重扫基本只花在"剩余没处理的文件"上,不会把整批几小时的工作重做一遍。

色情图片识别(可选)

基于本机 Python + NudeNet 的离线鉴黄,判为色情的图片移入 图片_敏感_* 分类目录(明暗子目录照常保留,如 图片_敏感_横屏_1080P/bright_20),并可在 Web「色情识别」页面查看每张图的检测分、路径与预览。

  • 开启:application.properties 设 nsfw.enabled=true
  • 依赖:本机安装 Python 并 pip install nudenet
  • 模型:优先加载程序运行目录下 lib/human_nwfs/320n.onnx(本仓库已附带,约 11.6MB,离线可用);本地缺失时自动下载到该位置,下载失败再回退用 nudenet 包自带模型
  • 可调:nsfw.python(python 路径)、nsfw.threshold(判定阈值)
  • 敏感类:FEMALE_GENITALIA_EXPOSED / MALE_GENITALIA_EXPOSED / ANUS_EXPOSED / FEMALE_BREAST_EXPOSED,任一分数达标即判定
  • 降级:未装 python / nudenet 时打印警告并跳过识别,图片按普通流程整理,不影响其它功能

二次元图片识别(可选)

基于本机 Python + wd-tagger(SmilingWolf/wd-vit-large-tagger-v3)的二次元图片分级识别,专门针对动漫/二次元风格训练,对 R15/R18 擦边图识别效果优于 NudeNet。判为敏感(questionable/explicit)的图片移入 图片_二次元_* 分类目录(明暗子目录照常保留),并可在 Web「二次元识别」页面查看每张图的分级、检测分、top 标签、路径与预览。

  • 开启:application.properties 设 hentai.enabled=true
  • 依赖:本机安装 Python 并 pip install onnxruntime Pillow numpy
  • 模型:首次启动自动从 HuggingFace 下载到 lib/wd-tagger/(约 1.5GB,需联网);下载完成后离线可用
  • 可调:hentai.python(python 路径)、hentai.threshold(判定阈值)
  • 分级输出:rating:safe / rating:questionable / rating:explicit,questionable 或 explicit 分数达到阈值即判定为敏感
  • 标签输出:同时输出 top 10 高置信度标签(如 1girl, solo, long_hair 等),便于人工复核
  • 降级:未装 python / 依赖或模型下载失败时打印警告并跳过识别,图片按普通流程整理,不影响其它功能
  • 与色情识别的关系:两者独立运行,可同时启用;同一张图优先走色情识别(nsfw > hentai > normal)

构建编译

sh build.sh
  • 依赖 Go 1.25.0+(go.mod 指定)
  • build.sh 先构建前端,再 go build 输出 kenaito-media-picker-release.exe(upx 压缩已注释,可自行启用)
  • 前端产物(frontend/dist)由 go:embed 打进二进制;internal/probe/bin/ffprobe.exe(Windows 版,约 145MB)已随仓库入库并内嵌
  • 项目使用纯 Go 的 SQLite 驱动(无 CGO),因此可以跨平台交叉编译

在 Windows 10 x64 上交叉编译 CentOS x64(Linux amd64)可执行程序

# 方式一:Git Bash / MSYS 终端
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o kenaito-media-picker-linux-amd64 .

# 方式二:PowerShell
$env:CGO_ENABLED="0"; $env:GOOS="linux"; $env:GOARCH="amd64"
go build -o kenaito-media-picker-linux-amd64 .
# 编完恢复环境变量
Remove-Item Env:CGO_ENABLED,Env:GOOS,Env:GOARCH

CGO_ENABLED=0 很关键:本项目 SQLite 用的是纯 Go 驱动,关掉 CGO 才能交叉编译;若用默认开启 CGO 会尝试调 Linux 的 C 编译器而失败。

把二进制拷到 CentOS 后,还要注意:

  1. ffprobe(视频/音频探测):Linux 构建不内嵌 ffprobe(仓库只带了 Windows 版),程序会回退用系统 PATH 里的 ffprobe。CentOS 上先装:
    # CentOS/RHEL 需要先装 EPEL 再装 ffmpeg(自带 ffprobe)
    sudo yum install -y epel-release
    sudo yum install -y ffmpeg
    # 验证:ffprobe -version
    
    没装 ffprobe 时视频/音频整理会跳过,图片照常处理。
  2. 色情识别(可选):Linux 上同样要装 Python 与 nudenet,并在 application.properties 里配 nsfw.python(如 /usr/bin/python3):
    sudo yum install -y python3
    pip3 install nudenet
    
    模型文件 lib/human_nwfs/320n.onnx 需与二进制放在同一目录(没有会自动下载)。
  3. 二次元识别(可选):Linux 上同样要装 Python 与依赖,并在 application.properties 里配 hentai.python(如 /usr/bin/python3):
    pip3 install onnxruntime Pillow numpy
    
    模型文件首次启动会自动从 HuggingFace 下载到 lib/wd-tagger/(约 1.5GB,需联网)。
  4. 其余步骤与 Windows 一致:application.properties、media-info.db 随运行目录自动创建。

前端开发模式(无需重复构建)

# 终端 1:后端源码运行(端口 8080)
go run .

# 终端 2:前端开发服务器(端口 3000,/api 代理到 8080,热更新)
cd frontend
npm install && npm run dev

浏览器访问 http://localhost:3000 联调。只有发布时才需要 npm run build 重新构建前端。

目录结构

├── main.go                # 程序入口(Gin + 内嵌前端产物)
├── application.properties # 运行配置(端口 / 数据库 / 旋转开关 / 色情识别)
├── lib/human_nwfs/320n.onnx   # 色情识别模型(随仓库分发,离线可用)
├── lib/wd-tagger/         # 二次元识别模型(首次启动自动下载,约 1.5GB)
├── build.sh               # 编译脚本
├── internal/
│   ├── scanner/           # 文件扫描、MIME 读取、候选过滤
│   ├── probe/             # 媒体探测:图片尺寸 / ffprobe / EXIF / 明暗分析(含内嵌 ffprobe)
│   ├── classify/          # 纯函数归类规则(方向 + 标准分辨率 + K档 + 明暗等级)
│   ├── organize/          # 整理编排:探测 → 归类 → 收集 → 移动 → 入库
│   ├── mover/             # 文件移动(rename / 跨盘 copy+delete)
│   ├── store/             # SQLite 媒体信息 / 色情记录 / 二次元记录持久化 + 查询/统计
│   ├── service/           # 后台任务编排(扫描整理任务 + 状态/日志/WebSocket)
│   ├── nsfw/              # 色情图片识别(内嵌 python 服务 + NudeNet 本地 HTTP)
│   ├── hentai/            # 二次元图片识别(内嵌 python 服务 + wd-tagger 本地 HTTP)
│   ├── api/               # HTTP 接口层(扫描 / 媒体库 / 统计 / 色情识别 / 二次元识别 / 目录浏览)
│   └── digest/            # 文件 MD5 / 指纹采样
└── frontend/              # 前端工程(Vue 2 + ElementUI + Vite)

注意事项

  • 默认使用移动模式,原文件会从源目录移走;跨磁盘时自动降级为复制 + 删除
  • 已整理文件全部进入 KMP已整理 白名单目录,重复运行会跳过它,并把新文件并入已有分类目录
  • 扫描会自动跳过 KMP已整理 输出目录、media-info.db 数据库与 ffprobe 临时文件
  • 同一时间只允许一次扫描整理;整理期间会占用较多 CPU/磁盘,属正常现象
  • EXIF 旋转转正需要 ffmpeg:配置 ffmpeg.path 指定路径,或让 ffmpeg 在 PATH 里;找不到 ffmpeg 时不转正像素,仍按显示方向归类。完全关闭旋转处理(也不按显示方向调宽高)可在配置里设 organize.rotateImages=false

License

Apache 2.0

S
Description
媒体文件自动分类整理工具:扫描图片/视频/音频,按方向 + 分辨率 + 明暗自动归档,内置 Web 管理界面与 ffprobe 探测
Readme Apache-2.0
493 MiB
Languages
Go 60.1%
Vue 19.5%
Roff 11.6%
Python 7.8%
JavaScript 0.6%
Other 0.3%