- 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
117 lines
3.2 KiB
Go
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
|
|
}
|