// 媒体库的两个接口:查列表、查统计。 // 「媒体库」页面查列表(带筛选和分页),「统计看板」页面查统计。 // 这两个接口只读数据库,不干别的活。 package api import ( "fmt" "net/http" "github.com/dustin/go-humanize" "github.com/gin-gonic/gin" "kenaito-media-picker/internal/classify" "kenaito-media-picker/internal/store" ) // MediaController 媒体库相关接口的"窗口"。 type MediaController struct { store *store.Store // 数据库(只读) } // NewMediaController 创建媒体库控制器,注入数据库。 func NewMediaController(st *store.Store) *MediaController { return &MediaController{store: st} } // mediaListReq 媒体库列表的查询条件(前端「媒体库」页面传过来的)。 type mediaListReq struct { Page int `json:"page"` PageSize int `json:"pageSize"` MediaType string `json:"mediaType"` Orientation string `json:"orientation"` Keyword string `json:"keyword"` Codec string `json:"codec"` Format string `json:"format"` Resolution string `json:"resolution"` SortField string `json:"sortField"` SortOrder string `json:"sortOrder"` } // mediaView 列表里展示的一条记录:在数据库记录的基础上, // 再补几个"现算"的字段(方向、分辨率档位、人类可读的体积和时长),方便页面直接显示。 type mediaView struct { store.MediaInfo Orientation string `json:"orientation"` // 横屏/竖屏/等比 Resolution string `json:"resolution"` // 分辨率档位(如 1080P) SizeText string `json:"sizeText"` // 人类可读体积(如 1.5 MB) DurationText string `json:"durationText"` // 人类可读时长(如 05:30) } // List 处理 POST /api/media/list:按条件分页查媒体库列表。 func (c *MediaController) List(ctx *gin.Context) { var req mediaListReq if err := ctx.ShouldBindJSON(&req); err != nil { Error(ctx, http.StatusBadRequest, "请求参数错误:"+err.Error()) return } if req.Page < 1 { req.Page = 1 } q := store.MediaQuery{ MediaType: req.MediaType, Orientation: req.Orientation, Keyword: req.Keyword, Codec: req.Codec, Format: req.Format, Resolution: req.Resolution, Page: req.Page, PageSize: req.PageSize, SortField: req.SortField, SortOrder: req.SortOrder, } // 查这一页的数据,同时查总条数(分页条要用) list, err := c.store.ListMedia(q) if err != nil { Error(ctx, http.StatusInternalServerError, "查询媒体列表失败:"+err.Error()) return } total, err := c.store.CountMedia(q) if err != nil { Error(ctx, http.StatusInternalServerError, "统计媒体数量失败:"+err.Error()) return } // 每条记录补上"现算"的展示字段再返回 views := make([]interface{}, 0, len(list)) for _, m := range list { views = append(views, toMediaView(m)) } Success(ctx, map[string]interface{}{"list": views, "total": total}) } // Stats 处理 POST /api/media/stats:统计看板要用的全部数据。 func (c *MediaController) Stats(ctx *gin.Context) { st, err := c.store.Stats() if err != nil { Error(ctx, http.StatusInternalServerError, "统计失败:"+err.Error()) return } Success(ctx, st) } // toMediaView 给一条数据库记录补上展示用的派生字段。 func toMediaView(m store.MediaInfo) mediaView { v := mediaView{MediaInfo: m} // 宽高有效才算方向和分辨率档位 if m.Width > 0 && m.Height > 0 { v.Orientation = classify.OrientationOf(m.Width, m.Height).String() v.Resolution = classify.ResLabel(m.Width, m.Height) } if m.FileSize > 0 { v.SizeText = humanize.Bytes(uint64(m.FileSize)) } if m.Duration > 0 { v.DurationText = formatDuration(m.Duration) } return v } // formatDuration 把秒数转成人类可读的时:分:秒。 func formatDuration(sec float64) string { s := int(sec) h := s / 3600 m := (s % 3600) / 60 secs := s % 60 if h > 0 { return fmt.Sprintf("%d:%02d:%02d", h, m, secs) } return fmt.Sprintf("%02d:%02d", m, secs) }