Files
kenaito-media-picker/internal/classify/classify.go
T
odboy e053b378fb feat: 增加 Web 管理界面,扫描整理改为页面点击触发
- main.go 改为 Web 服务入口(Gin + 内嵌前端 dist + 自动打开浏览器)
- internal/service: 新增扫描整理后台任务,支持状态机与实时日志捕获
- internal/api: 新增统一响应、媒体列表/统计、扫描开始/状态接口
- internal/store: 追加 ListMedia/CountMedia/Stats 查询,MediaInfo 补 JSON tag
- internal/config: application.properties 配置加载(端口/数据库/自动开浏览器)
- frontend: Vue2 + ElementUI + Vite 工程,含扫描整理/统计看板/媒体库三页
- build.sh 整合前端构建,输出单一 kenaito-media-picker.exe
2025-04-21 21:08:02 +08:00

117 lines
3.2 KiB
Go

// Package classify 提供纯函数的分辨率归类规则,不依赖任何状态。
package classify
import (
"fmt"
"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-dark_70" / "图片_横屏_1080P-mid" / "图片_GIF"。
// darkScore/lightScore 用于明暗分类(GIF 不做明暗分类,忽略这两项)。
func ImageDir(ext string, width, height int, darkScore, lightScore float64) string {
if strings.EqualFold(ext, ".gif") {
return "图片_GIF"
}
dir := resolutionDir("图片", ext, width, height)
return dir + "-" + BrightnessName(darkScore, lightScore)
}
// 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
}
// ResLabel 返回分辨率档位标签(标准后缀 / 1K~19K / 普通 / 原图),无有效尺寸时返回空串。
// 供媒体库统计与筛选使用,图片/视频统一以"原图"作为超档标签。
func ResLabel(width, height int) string {
if width <= 0 || height <= 0 {
return ""
}
if suffix := matchStandard(width, height); suffix != "" {
return suffix
}
return levelName(longEdge(width, height), "原图")
}
// ResRange 解析分辨率档位标签,返回 (长边下限 minLong, 长边上限 maxLong(不含), 精确宽 exactW, 精确高 exactH)。
// minLong<0 表示未知标签;maxLong 为 -1 表示无上限;exactW/exactH 非零时表示标准分辨率精确匹配。
// 供数据层构造 SQL 过滤条件,规则与归类逻辑保持一致。
func ResRange(label string) (minLong, maxLong, exactW, exactH int) {
if label == "" {
return -1, -1, 0, 0
}
for _, r := range standardResolutions {
if r.suffix == label {
return 0, 0, r.width, r.height
}
}
if label == "普通" {
return 0, bucketStep, 0, 0
}
if label == "原图" {
return (maxLevel + 1) * bucketStep, -1, 0, 0
}
if len(label) > 1 && label[len(label)-1] == 'K' {
var level int
if _, err := fmt.Sscanf(label, "%dK", &level); err == nil && level >= 1 && level <= maxLevel {
return level * bucketStep, (level + 1) * bucketStep, 0, 0
}
}
return -1, -1, 0, 0
}