201 lines
5.7 KiB
Go
201 lines
5.7 KiB
Go
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 视频内容安全扫描器
|
||
type VideoScanner struct {
|
||
Client *green.Client
|
||
Config *conf.Config
|
||
}
|
||
|
||
// NewVideoScanner 创建视频扫描器
|
||
func NewVideoScanner(config *conf.Config) (*VideoScanner, error) {
|
||
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建客户端失败: %v", err)
|
||
}
|
||
|
||
return &VideoScanner{
|
||
Client: client,
|
||
Config: config,
|
||
}, nil
|
||
}
|
||
|
||
// ScanVideoAsync 异步扫描视频
|
||
func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string) (*model.VideoScanResponse, error) {
|
||
request := requests.NewCommonRequest()
|
||
request.Method = "POST"
|
||
request.Scheme = "https"
|
||
request.Domain = s.Config.Endpoint
|
||
request.Version = "2018-05-09"
|
||
request.ApiName = "VideoAsyncScan"
|
||
request.QueryParams["RegionId"] = s.Config.Region
|
||
|
||
scanRequest := model.VideoScanRequest{
|
||
Tasks: []model.VideoTask{
|
||
{
|
||
DataID: dataID,
|
||
URL: videoURL,
|
||
Interval: 1, // 每秒截取一帧
|
||
MaxFrames: 100, // 最多截取100帧
|
||
},
|
||
},
|
||
Scenes: []string{"porn", "terrorism", "ad", "live"},
|
||
}
|
||
|
||
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 获取视频审核结果
|
||
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 = "2018-05-09"
|
||
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 扫描视频并等待结果
|
||
func (s *VideoScanner) ScanVideoAndWait(videoURL string, dataID string, maxWaitTime time.Duration) (*model.VideoResultResponse, error) {
|
||
// 提交扫描任务
|
||
scanResponse, err := s.ScanVideoAsync(videoURL, dataID)
|
||
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 打印审核结果
|
||
func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
|
||
fmt.Println("=== 视频内容安全审核结果 ===")
|
||
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)
|
||
fmt.Printf("建议: %s\n", result.Suggestion)
|
||
fmt.Printf("置信度: %.2f\n", result.Rate)
|
||
|
||
// 打印帧级别的结果
|
||
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)
|
||
}
|
||
}
|
||
}
|
||
fmt.Println("---")
|
||
}
|
||
}
|
||
}
|