package controller import ( "fmt" "net/http" "strconv" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/service" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type AdminVideoChannelBinding struct { Group string `json:"group"` Family string `json:"family"` ChannelID int `json:"channel_id"` } type adminVideoChannelBindingRequest struct { Bindings []AdminVideoChannelBinding `json:"bindings"` } type validatedAdminVideoChannelBinding struct { AdminVideoChannelBinding ChannelType int } type AdminVideoChannelCandidate struct { ID int `json:"id"` Name string `json:"name"` Type int `json:"type"` } type adminVideoChannelBindingRow struct { Group string `json:"group"` ChannelID int `json:"channel_id"` ChannelName string `json:"channel_name"` ChannelType int `json:"channel_type"` Candidates []AdminVideoChannelCandidate `json:"candidates"` } type adminVideoChannelBindingFamily struct { Key string `json:"key"` Name string `json:"name"` Bindings []adminVideoChannelBindingRow `json:"bindings"` } func videoAssetFamilyFromString(value string) (service.VideoAssetFamily, bool) { family := service.VideoAssetFamily(strings.TrimSpace(value)) for _, candidate := range service.VideoAssetFamilies() { if family == candidate { return family, true } } return "", false } func GetUserVideoChannelBindings(c *gin.Context) { userID, err := strconv.Atoi(c.Param("id")) if err != nil || userID <= 0 { common.ApiErrorMsg(c, "invalid user id") return } if _, err = model.GetUserById(userID, false); err != nil { common.ApiError(c, err) return } groups, err := model.GetUserConcreteTokenGroups(userID) if err != nil { common.ApiError(c, err) return } visibleGroupSet := make(map[string]struct{}, len(groups)) families := make([]adminVideoChannelBindingFamily, 0, len(service.VideoAssetFamilies())) for _, family := range service.VideoAssetFamilies() { rows := make([]adminVideoChannelBindingRow, 0, len(groups)) for _, group := range groups { candidates, err := service.GetVideoAssetChannelCandidates(group, family) if err != nil { common.ApiError(c, err) return } if len(candidates) == 0 { continue } visibleGroupSet[group] = struct{}{} row := adminVideoChannelBindingRow{Group: group, Candidates: make([]AdminVideoChannelCandidate, 0, len(candidates))} for _, channel := range candidates { row.Candidates = append(row.Candidates, AdminVideoChannelCandidate{ID: channel.Id, Name: channel.Name, Type: channel.Type}) } bindings, err := model.GetUserAssetChannelsByTypes(userID, service.VideoAssetChannelTypesForFamily(family), group) if err != nil { common.ApiError(c, err) return } for _, binding := range bindings { for _, candidate := range candidates { if candidate.Id == binding.ChannelId { row.ChannelID = candidate.Id row.ChannelName = candidate.Name row.ChannelType = candidate.Type break } } if row.ChannelID != 0 { break } } rows = append(rows, row) } families = append(families, adminVideoChannelBindingFamily{Key: string(family), Name: strings.ToUpper(string(family[:1])) + string(family[1:]), Bindings: rows}) } visibleGroups := make([]string, 0, len(visibleGroupSet)) for _, group := range groups { if _, ok := visibleGroupSet[group]; ok { visibleGroups = append(visibleGroups, group) } } c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": gin.H{"groups": visibleGroups, "families": families}}) } func SetUserVideoChannelBindings(c *gin.Context) { userID, err := strconv.Atoi(c.Param("id")) if err != nil || userID <= 0 { common.ApiErrorMsg(c, "invalid user id") return } if _, err = model.GetUserById(userID, false); err != nil { common.ApiError(c, err) return } var request adminVideoChannelBindingRequest if err := c.ShouldBindJSON(&request); err != nil { common.ApiErrorMsg(c, err.Error()) return } groups, err := model.GetUserConcreteTokenGroups(userID) if err != nil { common.ApiError(c, err) return } allowedGroups := make(map[string]struct{}, len(groups)) for _, group := range groups { allowedGroups[group] = struct{}{} } requested := make(map[string]validatedAdminVideoChannelBinding, len(request.Bindings)) for _, binding := range request.Bindings { binding.Group = strings.TrimSpace(binding.Group) family, ok := videoAssetFamilyFromString(binding.Family) if !ok || binding.Group == "" || binding.Group == "auto" || binding.ChannelID <= 0 { common.ApiErrorMsg(c, "invalid video channel binding") return } if _, ok := allowedGroups[binding.Group]; !ok { common.ApiErrorMsg(c, "token group is not available for this user") return } key := binding.Group + "\x00" + string(family) if _, exists := requested[key]; exists { common.ApiErrorMsg(c, "duplicate video channel binding") return } candidates, err := service.GetVideoAssetChannelCandidates(binding.Group, family) if err != nil { common.ApiError(c, err) return } channelType := 0 for _, candidate := range candidates { if candidate.Id == binding.ChannelID { channelType = candidate.Type break } } if channelType == 0 { common.ApiErrorMsg(c, "channel is not available for this video family and token group") return } binding.Family = string(family) requested[key] = validatedAdminVideoChannelBinding{AdminVideoChannelBinding: binding, ChannelType: channelType} } if err := model.DB.Transaction(func(tx *gorm.DB) error { for _, group := range groups { for _, family := range service.VideoAssetFamilies() { key := group + "\x00" + string(family) binding, exists := requested[key] channelTypes := service.VideoAssetChannelTypesForFamily(family) if err := model.DeleteUserAssetChannelsByTypesWithTx(tx, userID, channelTypes, group); err != nil { return err } if !exists { continue } if err := model.BindUserAssetChannelWithTx(tx, userID, binding.ChannelType, group, binding.ChannelID); err != nil { return err } } } return nil }); err != nil { common.ApiError(c, err) return } model.RecordLog(userID, model.LogTypeManage, fmt.Sprintf("updated video channel bindings for user %d", userID)) c.JSON(http.StatusOK, gin.H{"success": true, "message": ""}) }