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, }) }