feat: 二次元识别功能 + 模型目录整理(nwfs/wd-tagger 子目录),修复 wd-tagger 输入维度(NHWC)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"kenaito-media-picker/internal/config"
|
||||
"kenaito-media-picker/internal/store"
|
||||
)
|
||||
|
||||
// hentaiDetectorReadyKey 记录最近一次扫描时二次元检测服务是否真的起来了。
|
||||
const hentaiDetectorReadyKey = "hentai.detectorReady"
|
||||
|
||||
// HentaiController 二次元识别相关接口的控制器。
|
||||
type HentaiController struct {
|
||||
store *store.Store
|
||||
}
|
||||
|
||||
// NewHentaiController 创建二次元识别控制器。
|
||||
func NewHentaiController(st *store.Store) *HentaiController {
|
||||
return &HentaiController{store: st}
|
||||
}
|
||||
|
||||
// hentaiView 列表里展示的一条记录。
|
||||
type hentaiView struct {
|
||||
Id int64 `json:"id"`
|
||||
FileName string `json:"fileName"`
|
||||
Score float64 `json:"score"`
|
||||
Rating string `json:"rating"`
|
||||
Threshold float64 `json:"threshold"`
|
||||
Path string `json:"path"`
|
||||
TopTags string `json:"topTags"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
// List 处理 GET /api/hentai/list:查全部二次元识别记录(按检测分从高到低)。
|
||||
func (c *HentaiController) List(ctx *gin.Context) {
|
||||
list, err := c.store.ListHentai()
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusInternalServerError, "查询二次元识别记录失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
views := make([]hentaiView, 0, len(list))
|
||||
for _, r := range list {
|
||||
path := r.MovedPath
|
||||
if path == "" {
|
||||
path = r.FilePath
|
||||
}
|
||||
views = append(views, hentaiView{
|
||||
Id: r.Id,
|
||||
FileName: r.FileName,
|
||||
Score: r.Score,
|
||||
Rating: r.Rating,
|
||||
Threshold: r.Threshold,
|
||||
Path: path,
|
||||
TopTags: r.TopTags,
|
||||
CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
Success(ctx, map[string]interface{}{
|
||||
"list": views,
|
||||
"total": len(views),
|
||||
"hentaiEnabled": config.GetAppConfig().HentaiEnabled,
|
||||
"hentaiDetector": c.store == nil || c.store.GetConfig(hentaiDetectorReadyKey) != "0",
|
||||
})
|
||||
}
|
||||
|
||||
// Preview 处理 GET /api/hentai/preview?path=xxx:把指定图片文件返回给浏览器做预览。
|
||||
func (c *HentaiController) Preview(ctx *gin.Context) {
|
||||
path := strings.TrimSpace(ctx.Query("path"))
|
||||
if path == "" {
|
||||
Error(ctx, http.StatusBadRequest, "缺少 path 参数")
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
Error(ctx, http.StatusNotFound, "图片不存在或已被移动: "+path)
|
||||
return
|
||||
}
|
||||
ctx.File(path)
|
||||
}
|
||||
|
||||
// Tags 处理 GET /api/hentai/tags?id=xxx:查某条记录的标签详情。
|
||||
func (c *HentaiController) Tags(ctx *gin.Context) {
|
||||
id := ctx.Query("id")
|
||||
if id == "" {
|
||||
Error(ctx, http.StatusBadRequest, "缺少 id 参数")
|
||||
return
|
||||
}
|
||||
var idInt int64
|
||||
if _, err := json.Number(id).Int64(); err != nil {
|
||||
Error(ctx, http.StatusBadRequest, "id 格式错误")
|
||||
return
|
||||
}
|
||||
idInt, _ = json.Number(id).Int64()
|
||||
|
||||
record, err := c.store.GetHentaiById(idInt)
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusInternalServerError, "查询记录失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
if record == nil {
|
||||
Error(ctx, http.StatusNotFound, "记录不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var tags []map[string]interface{}
|
||||
if record.TopTags != "" {
|
||||
if err := json.Unmarshal([]byte(record.TopTags), &tags); err != nil {
|
||||
tags = nil
|
||||
}
|
||||
}
|
||||
|
||||
Success(ctx, map[string]interface{}{
|
||||
"id": record.Id,
|
||||
"topTags": tags,
|
||||
})
|
||||
}
|
||||
@@ -38,6 +38,12 @@ func NewRouter(st *store.Store) *gin.Engine {
|
||||
apiGroup.GET("/nsfw/list", nc.List)
|
||||
apiGroup.GET("/nsfw/preview", nc.Preview)
|
||||
|
||||
// 二次元识别(查看分级结果 + 图片预览 + 标签详情)
|
||||
hc := NewHentaiController(st)
|
||||
apiGroup.GET("/hentai/list", hc.List)
|
||||
apiGroup.GET("/hentai/preview", hc.Preview)
|
||||
apiGroup.GET("/hentai/tags", hc.Tags)
|
||||
|
||||
// 扫描整理(开始 / 中止 / 查进度 / 实时进度)
|
||||
sc := NewScanController(service.NewScanService(st))
|
||||
apiGroup.POST("/scan/start", sc.Start)
|
||||
|
||||
Reference in New Issue
Block a user