contentSecurity/internal/api/video_scanner.go
2025-10-15 17:01:31 +08:00

252 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"encoding/json"
"fmt"
"time"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/model"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// VideoScanner 视频内容安全扫描器2.0
type VideoScanner struct {
Client *green.Client
Config *conf.Config
}
// VideoServiceType 视频审核服务类型
type VideoServiceType string
const (
// VideoBaselineCheckGlobal 视频通用基线检测
VideoBaselineCheckGlobal VideoServiceType = "baselineCheck_global"
// VideoPostCheckByVLGlobal 视频大小模型融合审核服务
VideoPostCheckByVLGlobal VideoServiceType = "postVideoCheckByVL_global"
)
// NewVideoScanner 创建视频扫描器
func NewVideoScanner(config *conf.Config) (*VideoScanner, error) {
// 获取有效的访问凭证
accessKeyID, accessKeySecret, securityToken := config.GetEffectiveCredentials()
var client *green.Client
var err error
if securityToken != "" {
// 使用STS临时凭证
client, err = green.NewClientWithStsToken(config.Region, accessKeyID, accessKeySecret, securityToken)
} else {
// 使用直接凭证
client, err = green.NewClientWithAccessKey(config.Region, accessKeyID, accessKeySecret)
}
if err != nil {
return nil, fmt.Errorf("创建客户端失败: %v", err)
}
return &VideoScanner{
Client: client,
Config: config,
}, nil
}
// ScanVideoAsync 异步扫描视频2.0版本)
func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string, serviceType VideoServiceType) (*model.VideoScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2022-03-02" // 内容安全2.0视频审核API版本
request.ApiName = "VideoAsyncScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.VideoScanRequest{
Tasks: []model.VideoTask{
{
DataID: dataID,
URL: videoURL,
Interval: 1, // 每秒截取一帧
MaxFrames: 100, // 最多截取100帧
},
},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.VideoScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// GetVideoResult 获取视频审核结果2.0版本)
func (s *VideoScanner) GetVideoResult(taskID string) (*model.VideoResultResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2022-03-02" // 内容安全2.0视频审核API版本
request.ApiName = "VideoAsyncScanResults"
request.QueryParams["RegionId"] = s.Config.Region
requestBody := fmt.Sprintf(`{"taskId": "%s"}`, taskID)
request.Content = []byte(requestBody)
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var resultResponse model.VideoResultResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &resultResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &resultResponse, nil
}
// ScanVideoAndWait 扫描视频并等待结果2.0版本)
func (s *VideoScanner) ScanVideoAndWait(videoURL string, dataID string, serviceType VideoServiceType, maxWaitTime time.Duration) (*model.VideoResultResponse, error) {
// 提交扫描任务
scanResponse, err := s.ScanVideoAsync(videoURL, dataID, serviceType)
if err != nil {
return nil, fmt.Errorf("提交扫描任务失败: %v", err)
}
if len(scanResponse.Data) == 0 {
return nil, fmt.Errorf("未获取到任务ID")
}
taskID := scanResponse.Data[0].TaskID
fmt.Printf("视频扫描任务已提交任务ID: %s\n", taskID)
// 轮询获取结果
startTime := time.Now()
for time.Since(startTime) < maxWaitTime {
result, err := s.GetVideoResult(taskID)
if err != nil {
return nil, fmt.Errorf("获取结果失败: %v", err)
}
if len(result.Data) > 0 {
status := result.Data[0].Status
fmt.Printf("当前状态: %s\n", status)
switch status {
case "success":
return result, nil
case "failure":
return nil, fmt.Errorf("视频审核失败: %s", result.Data[0].Message)
case "running", "pending":
// 继续等待
time.Sleep(5 * time.Second)
default:
return nil, fmt.Errorf("未知状态: %s", status)
}
} else {
time.Sleep(5 * time.Second)
}
}
return nil, fmt.Errorf("等待超时请稍后手动查询结果任务ID: %s", taskID)
}
// PrintScanResult 打印扫描提交结果
func (s *VideoScanner) PrintScanResult(response *model.VideoScanResponse) {
fmt.Println("=== 视频扫描任务提交结果 ===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("数据ID: %s\n", data.DataID)
fmt.Printf("任务ID: %s\n", data.TaskID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
fmt.Printf("视频URL: %s\n", data.URL)
}
}
// PrintResult 打印审核结果2.0版本)
func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
fmt.Println("=== 视频内容安全审核结果2.0版本)===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("\n数据ID: %s\n", data.DataID)
fmt.Printf("任务ID: %s\n", data.TaskID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
fmt.Printf("审核状态: %s\n", data.Status)
if len(data.Results) == 0 {
fmt.Println("✅ 未检测到违规内容")
continue
}
for _, result := range data.Results {
fmt.Printf("场景: %s\n", result.Scene)
fmt.Printf("标签: %s\n", result.Label)
if result.SubLabel != "" {
fmt.Printf("子标签: %s\n", result.SubLabel)
}
fmt.Printf("建议: %s\n", result.Suggestion)
fmt.Printf("置信度: %.2f\n", result.Rate)
// 打印详细信息
if len(result.Details) > 0 {
fmt.Println("详细信息:")
for _, detail := range result.Details {
fmt.Printf(" 内容: %s\n", detail.Context.Context)
if len(detail.Context.Pos) > 0 {
fmt.Printf(" 位置: %v\n", detail.Context.Pos)
}
}
}
// 打印扩展信息
if len(result.Extras) > 0 {
fmt.Println("扩展信息:")
for key, value := range result.Extras {
fmt.Printf(" %s: %v\n", key, value)
}
}
// 打印帧级别的结果
if len(result.Frames) > 0 {
fmt.Println("违规帧详情:")
for _, frame := range result.Frames {
fmt.Printf(" 时间偏移: %d秒\n", frame.Offset)
fmt.Printf(" 帧URL: %s\n", frame.URL)
for _, frameResult := range frame.Results {
fmt.Printf(" 场景: %s, 标签: %s, 建议: %s, 置信度: %.2f\n",
frameResult.Scene, frameResult.Label, frameResult.Suggestion, frameResult.Rate)
if frameResult.SubLabel != "" {
fmt.Printf(" 子标签: %s\n", frameResult.SubLabel)
}
if len(frameResult.Details) > 0 {
fmt.Printf(" 详细信息: %v\n", frameResult.Details)
}
}
}
}
fmt.Println("---")
}
}
}