ci 内容审核
This commit is contained in:
145
ci.go
145
ci.go
@@ -4,15 +4,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CIService service
|
||||
|
||||
type PicOperations struct {
|
||||
IsPicInfo int `json:"is_pic_info,omitempty"`
|
||||
Rules []PicOperationsRules `json:"rules,omitemtpy"`
|
||||
}
|
||||
|
||||
type PicOperationsRules struct {
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
FileId string `json:"fileid"`
|
||||
@@ -20,6 +20,9 @@ type PicOperationsRules struct {
|
||||
}
|
||||
|
||||
func EncodePicOperations(pic *PicOperations) string {
|
||||
if pic == nil {
|
||||
return ""
|
||||
}
|
||||
bs, err := json.Marshal(pic)
|
||||
if err != nil {
|
||||
return ""
|
||||
@@ -27,18 +30,16 @@ func EncodePicOperations(pic *PicOperations) string {
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
type CloudImageReuslt struct {
|
||||
type ImageProcessResult struct {
|
||||
XMLName xml.Name `xml:"UploadResult"`
|
||||
OriginalInfo *PicOriginalInfo `xml:"OriginalInfo,omitempty"`
|
||||
ProcessObject *PicProcessObject `xml:"ProcessResults>Object,omitempty"`
|
||||
}
|
||||
|
||||
type PicOriginalInfo struct {
|
||||
Key string `xml:"Key,omitempty"`
|
||||
Location string `xml:"Location,omitempty"`
|
||||
ImageInfo *PicImageInfo `xml:"ImageInfo,omitempty"`
|
||||
}
|
||||
|
||||
type PicImageInfo struct {
|
||||
Format string `xml:"Format,omitempty"`
|
||||
Width int `xml:"Width,omitempty"`
|
||||
@@ -46,7 +47,6 @@ type PicImageInfo struct {
|
||||
Size int `xml:"Size,omitempty"`
|
||||
Quality int `xml:"Quality,omitempty"`
|
||||
}
|
||||
|
||||
type PicProcessObject struct {
|
||||
Key string `xml:"Key,omitempty"`
|
||||
Location string `xml:"Location,omitempty"`
|
||||
@@ -57,24 +57,42 @@ type PicProcessObject struct {
|
||||
Quality int `xml:"Quality,omitempty"`
|
||||
}
|
||||
|
||||
type CloudImageOptions struct {
|
||||
type picOperationsHeader struct {
|
||||
PicOperations string `header:"Pic-Operations" xml:"-" url:"-"`
|
||||
}
|
||||
|
||||
func (s *ObjectService) PostCI(ctx context.Context, name string, opt *CloudImageOptions) (*CloudImageReuslt, *Response, error) {
|
||||
var res CloudImageReuslt
|
||||
type ImageProcessOptions = PicOperations
|
||||
|
||||
// 云上数据处理 https://cloud.tencent.com/document/product/460/18147
|
||||
func (s *CIService) ImageProcess(ctx context.Context, name string, opt *ImageProcessOptions) (*ImageProcessResult, *Response, error) {
|
||||
header := &picOperationsHeader{
|
||||
PicOperations: EncodePicOperations(opt),
|
||||
}
|
||||
var res ImageProcessResult
|
||||
sendOpt := sendOptions{
|
||||
baseURL: s.client.BaseURL.BucketURL,
|
||||
uri: "/" + encodeURIComponent(name) + "?image_process",
|
||||
method: http.MethodPost,
|
||||
optHeader: opt,
|
||||
optHeader: header,
|
||||
result: &res,
|
||||
}
|
||||
resp, err := s.client.send(ctx, &sendOpt)
|
||||
return &res, resp, err
|
||||
}
|
||||
|
||||
type CloudImageRecognitionInfo struct {
|
||||
type ImageRecognitionOptions struct {
|
||||
CIProcess string `url:"ci-process,omitempty"`
|
||||
DetectType string `url:"detect-type,omitempty"`
|
||||
}
|
||||
|
||||
type ImageRecognitionResult struct {
|
||||
XMLName xml.Name `xml:"RecognitionResult"`
|
||||
PornInfo *RecognitionInfo `xml:"PornInfo,omitempty"`
|
||||
TerroristInfo *RecognitionInfo `xml:"TerroristInfo,omitempty"`
|
||||
PoliticsInfo *RecognitionInfo `xml:"PoliticsInfo,omitempty"`
|
||||
AdsInfo *RecognitionInfo `xml:"AdsInfo,omitempty"`
|
||||
}
|
||||
type RecognitionInfo struct {
|
||||
Code int `xml:"Code,omitempty"`
|
||||
Msg string `xml:"Msg,omitempty"`
|
||||
HitFlag int `xml:"HitFlag,omitempty"`
|
||||
@@ -83,18 +101,99 @@ type CloudImageRecognitionInfo struct {
|
||||
Count int `xml:"Count,omitempty"`
|
||||
}
|
||||
|
||||
type CloudImageRecognitionResult struct {
|
||||
PornInfo *CloudImageRecognitionInfo `xml:"PornInfo,omitempty"`
|
||||
TerroristInfo *CloudImageRecognitionInfo `xml:"TerroristInfo,omitempty"`
|
||||
PoliticsInfo *CloudImageRecognitionInfo `xml:"PoliticsInfo,omitempty"`
|
||||
AdsInfo *CloudImageRecognitionInfo `xml:"AdsInfo,omitempty"`
|
||||
// 图片审核 https://cloud.tencent.com/document/product/460/37318
|
||||
func (s *CIService) ImageRecognition(ctx context.Context, name string, opt *ImageRecognitionOptions) (*ImageRecognitionResult, *Response, error) {
|
||||
if opt != nil && opt.CIProcess == "" {
|
||||
opt.CIProcess = "sensitive-content-recognition"
|
||||
}
|
||||
var res ImageRecognitionResult
|
||||
sendOpt := sendOptions{
|
||||
baseURL: s.client.BaseURL.BucketURL,
|
||||
uri: "/" + encodeURIComponent(name),
|
||||
method: http.MethodGet,
|
||||
optQuery: opt,
|
||||
result: &res,
|
||||
}
|
||||
resp, err := s.client.send(ctx, &sendOpt)
|
||||
return &res, resp, err
|
||||
}
|
||||
|
||||
func GetRecognitionResult(body io.ReadCloser) *CloudImageRecognitionResult {
|
||||
var res CloudImageRecognitionResult
|
||||
err := xml.NewDecoder(body).Decode(&res)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil
|
||||
}
|
||||
return &res
|
||||
type PutVideoAuditingJobOptions struct {
|
||||
XMLName xml.Name `xml:"Request"`
|
||||
InputObject string `xml:"Input>Object"`
|
||||
Conf *VideoAuditingJobConf `xml:"Conf"`
|
||||
}
|
||||
type VideoAuditingJobConf struct {
|
||||
DetectType string `xml:",omitempty"`
|
||||
Snapshot *PutVideoAuditingJobSnapshot `xml:",omitempty"`
|
||||
Callback string `xml:",omitempty"`
|
||||
}
|
||||
type PutVideoAuditingJobSnapshot struct {
|
||||
Mode string `xml:",omitempty"`
|
||||
Count int `xml:",omitempty"`
|
||||
TimeInterval float32 `xml:",omitempty"`
|
||||
Start float32 `xml:",omitempty"`
|
||||
}
|
||||
|
||||
type PutVideoAuditingJobResult struct {
|
||||
XMLName xml.Name `xml:"Response"`
|
||||
JobsDetail struct {
|
||||
JobId string `xml:"JobId,omitempty"`
|
||||
State string `xml:"State,omitempty"`
|
||||
CreationTime string `xml:"CreationTime,omitempty"`
|
||||
Object string `xml:"Object,omitempty"`
|
||||
} `xml:"JobsDetail,omitempty"`
|
||||
}
|
||||
|
||||
func (s *CIService) PutVideoAuditingJob(ctx context.Context, opt *PutVideoAuditingJobOptions) (*PutVideoAuditingJobResult, *Response, error) {
|
||||
var res PutVideoAuditingJobResult
|
||||
sendOpt := sendOptions{
|
||||
baseURL: s.client.BaseURL.CIURL,
|
||||
uri: "/video/auditing",
|
||||
method: http.MethodPost,
|
||||
body: opt,
|
||||
result: &res,
|
||||
}
|
||||
resp, err := s.client.send(ctx, &sendOpt)
|
||||
return &res, resp, err
|
||||
}
|
||||
|
||||
type GetVideoAuditingJobResult struct {
|
||||
XMLName xml.Name `xml:"Response"`
|
||||
JobsDetail *VideoAuditingJobDetail `xml:",omitempty"`
|
||||
NonExistJobIds string `xml:",omitempty"`
|
||||
}
|
||||
type VideoAuditingJobDetail struct {
|
||||
Code string `xml:",omitempty"`
|
||||
Message string `xml:",omitempty"`
|
||||
JobId string `xml:",omitempty"`
|
||||
State string `xml:",omitempty"`
|
||||
CreationTime string `xml:",omitempty"`
|
||||
Object string `xml:",omitempty"`
|
||||
SnapshotCount string `xml:",omitempty"`
|
||||
result int `xml:",omitempty"`
|
||||
PornInfo *RecognitionInfo `xml:",omitempty"`
|
||||
TerrorismInfo *RecognitionInfo `xml:",omitempty"`
|
||||
PoliticsInfo *RecognitionInfo `xml:",omitempty"`
|
||||
AdsInfo *RecognitionInfo `xml:",omitempty"`
|
||||
Snapshot *GetVideoAuditingJobSnapshot `xml:",omitempty"`
|
||||
}
|
||||
type GetVideoAuditingJobSnapshot struct {
|
||||
Url string `xml:",omitempty"`
|
||||
PornInfo *RecognitionInfo `xml:",omitempty"`
|
||||
TerrorismInfo *RecognitionInfo `xml:",omitempty"`
|
||||
PoliticsInfo *RecognitionInfo `xml:",omitempty"`
|
||||
AdsInfo *RecognitionInfo `xml:",omitempty"`
|
||||
}
|
||||
|
||||
func (s *CIService) GetVideoAuditingJob(ctx context.Context, jobid string) (*GetVideoAuditingJobResult, *Response, error) {
|
||||
var res GetVideoAuditingJobResult
|
||||
sendOpt := sendOptions{
|
||||
baseURL: s.client.BaseURL.CIURL,
|
||||
uri: "/video/auditing/" + jobid,
|
||||
method: http.MethodGet,
|
||||
result: &res,
|
||||
}
|
||||
resp, err := s.client.send(ctx, &sendOpt)
|
||||
return &res, resp, err
|
||||
}
|
||||
|
||||
8
cos.go
8
cos.go
@@ -42,6 +42,8 @@ type BaseURL struct {
|
||||
ServiceURL *url.URL
|
||||
// 访问 job API 的基础 URL (不包含 path 部分): http://example.com
|
||||
BatchURL *url.URL
|
||||
// 访问 CI 的基础 URL
|
||||
CIURL *url.URL
|
||||
}
|
||||
|
||||
// NewBucketURL 生成 BaseURL 所需的 BucketURL
|
||||
@@ -82,6 +84,7 @@ type Client struct {
|
||||
Bucket *BucketService
|
||||
Object *ObjectService
|
||||
Batch *BatchService
|
||||
CI *CIService
|
||||
}
|
||||
|
||||
type service struct {
|
||||
@@ -99,6 +102,7 @@ func NewClient(uri *BaseURL, httpClient *http.Client) *Client {
|
||||
baseURL.BucketURL = uri.BucketURL
|
||||
baseURL.ServiceURL = uri.ServiceURL
|
||||
baseURL.BatchURL = uri.BatchURL
|
||||
baseURL.CIURL = uri.CIURL
|
||||
}
|
||||
if baseURL.ServiceURL == nil {
|
||||
baseURL.ServiceURL, _ = url.Parse(defaultServiceBaseURL)
|
||||
@@ -114,6 +118,7 @@ func NewClient(uri *BaseURL, httpClient *http.Client) *Client {
|
||||
c.Bucket = (*BucketService)(&c.common)
|
||||
c.Object = (*ObjectService)(&c.common)
|
||||
c.Batch = (*BatchService)(&c.common)
|
||||
c.CI = (*CIService)(&c.common)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -244,9 +249,6 @@ func (c *Client) send(ctx context.Context, opt *sendOptions) (resp *Response, er
|
||||
}
|
||||
|
||||
resp, err = c.doAPI(ctx, req, opt.result, !opt.disableCloseBody)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func setup() {
|
||||
server = httptest.NewServer(mux)
|
||||
|
||||
u, _ := url.Parse(server.URL)
|
||||
client = NewClient(&BaseURL{u, u, u}, nil)
|
||||
client = NewClient(&BaseURL{u, u, u, u}, nil)
|
||||
}
|
||||
|
||||
// teardown closes the test HTTP server.
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -48,7 +47,7 @@ func main() {
|
||||
},
|
||||
})
|
||||
|
||||
pic := &cos.PicOperations{
|
||||
opt := &cos.ImageProcessOptions{
|
||||
IsPicInfo: 1,
|
||||
Rules: []cos.PicOperationsRules{
|
||||
{
|
||||
@@ -57,12 +56,8 @@ func main() {
|
||||
},
|
||||
},
|
||||
}
|
||||
opt := &cos.CloudImageOptions{
|
||||
PicOperations: cos.EncodePicOperations(pic),
|
||||
}
|
||||
name := "test.jpg"
|
||||
res, _, err := c.Object.PostCI(context.Background(), name, opt)
|
||||
data, _ := xml.Marshal(res)
|
||||
fmt.Printf("%+v\n", string(data))
|
||||
res, _, err := c.CI.ImageProcess(context.Background(), name, opt)
|
||||
log_status(err)
|
||||
fmt.Printf("%+v\n", res)
|
||||
}
|
||||
@@ -45,18 +45,12 @@ func main() {
|
||||
},
|
||||
},
|
||||
})
|
||||
opt := &cos.ObjectGetOptions{
|
||||
CIProcess: "sensitive-content-recognition",
|
||||
CIDetectType: "porn,terrorist,politics,ads",
|
||||
opt := &cos.ImageRecognitionOptions{
|
||||
DetectType: "porn,terrorist,politics",
|
||||
}
|
||||
|
||||
// Case1 Download object into ReadCloser(). the body needs to be closed
|
||||
name := "test.jpg"
|
||||
resp, err := c.Object.Get(context.Background(), name, opt)
|
||||
res, _, err := c.CI.ImageRecognition(context.Background(), name, opt)
|
||||
log_status(err)
|
||||
resp.Body.Close()
|
||||
res := cos.GetRecognitionResult(resp.Body)
|
||||
if res != nil {
|
||||
fmt.Printf("%+v\n", res)
|
||||
}
|
||||
fmt.Printf("%+v\n", res)
|
||||
}
|
||||
71
example/object/ci_video_auditing_job.go
Normal file
71
example/object/ci_video_auditing_job.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/tencentyun/cos-go-sdk-v5"
|
||||
"github.com/tencentyun/cos-go-sdk-v5/debug"
|
||||
)
|
||||
|
||||
func log_status(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if cos.IsNotFoundError(err) {
|
||||
// WARN
|
||||
fmt.Println("WARN: Resource is not existed")
|
||||
} else if e, ok := cos.IsCOSError(err); ok {
|
||||
fmt.Printf("ERROR: Code: %v\n", e.Code)
|
||||
fmt.Printf("ERROR: Message: %v\n", e.Message)
|
||||
fmt.Printf("ERROR: Resource: %v\n", e.Resource)
|
||||
fmt.Printf("ERROR: RequestId: %v\n", e.RequestID)
|
||||
// ERROR
|
||||
} else {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
// ERROR
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
bu, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com")
|
||||
cu, _ := url.Parse("https://test-1259654469.ci.ap-guangzhou.myqcloud.com")
|
||||
b := &cos.BaseURL{BucketURL: bu, CIURL: cu}
|
||||
c := cos.NewClient(b, &http.Client{
|
||||
Transport: &cos.AuthorizationTransport{
|
||||
SecretID: os.Getenv("COS_SECRETID"),
|
||||
SecretKey: os.Getenv("COS_SECRETKEY"),
|
||||
Transport: &debug.DebugRequestTransport{
|
||||
RequestHeader: true,
|
||||
RequestBody: true,
|
||||
ResponseHeader: true,
|
||||
ResponseBody: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
opt := &cos.PutVideoAuditingJobOptions{
|
||||
InputObject: "demo.mp4",
|
||||
Conf: &cos.VideoAuditingJobConf{
|
||||
DetectType: "Porn,Terrorism,Politics,Ads",
|
||||
Snapshot: &cos.PutVideoAuditingJobSnapshot{
|
||||
Mode: "Interval",
|
||||
Start: 0.5,
|
||||
TimeInterval: 50.5,
|
||||
Count: 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
res, _, err := c.CI.PutVideoAuditingJob(context.Background(), opt)
|
||||
log_status(err)
|
||||
fmt.Printf("%+v\n", res)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
res2, _, err := c.CI.GetVideoAuditingJob(context.Background(), res.JobsDetail.JobId)
|
||||
log_status(err)
|
||||
fmt.Printf("%+v\n", res2)
|
||||
}
|
||||
20
object.go
20
object.go
@@ -35,10 +35,6 @@ type ObjectGetOptions struct {
|
||||
XCosSSECustomerKeyMD5 string `header:"x-cos-server-side-encryption-customer-key-MD5,omitempty" url:"-" xml:"-"`
|
||||
|
||||
XCosTrafficLimit int `header:"x-cos-traffic-limit,omitempty" url:"-" xml:"-"`
|
||||
|
||||
// CI 图片审核
|
||||
CIProcess string `header:"-" url:"ci-process" xml:"-"`
|
||||
CIDetectType string `header:"-" url:"detect-type" xml:"-"`
|
||||
}
|
||||
|
||||
// presignedURLTestingOptions is the opt of presigned url
|
||||
@@ -501,6 +497,7 @@ type Chunk struct {
|
||||
OffSet int64
|
||||
Size int64
|
||||
Done bool
|
||||
ETag string
|
||||
}
|
||||
|
||||
// jobs
|
||||
@@ -662,8 +659,9 @@ func (s *ObjectService) checkUploadedParts(ctx context.Context, name, UploadID,
|
||||
defer fd.Close()
|
||||
// 某个分块出错, 重置chunks
|
||||
ret := func(e error) error {
|
||||
for _, chunk := range chunks {
|
||||
chunk.Done = false
|
||||
for i, _ := range chunks {
|
||||
chunks[i].Done = false
|
||||
chunks[i].ETag = ""
|
||||
}
|
||||
return e
|
||||
}
|
||||
@@ -683,6 +681,7 @@ func (s *ObjectService) checkUploadedParts(ctx context.Context, name, UploadID,
|
||||
return ret(errors.New(fmt.Sprintf("CheckSum Failed in Part[%d]", part.PartNumber)))
|
||||
}
|
||||
chunks[partNumber].Done = true
|
||||
chunks[partNumber].ETag = part.ETag
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -706,6 +705,7 @@ func (s *ObjectService) Upload(ctx context.Context, name string, filepath string
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// filesize=0 , use simple upload
|
||||
if partNum == 0 {
|
||||
var opt0 *ObjectPutOptions
|
||||
if opt.OptIni != nil {
|
||||
@@ -787,7 +787,13 @@ func (s *ObjectService) Upload(ctx context.Context, name string, filepath string
|
||||
close(chjobs)
|
||||
|
||||
// 5.Recv the resp etag to complete
|
||||
for i := 1; i <= partNum; i++ {
|
||||
for i := 0; i < partNum; i++ {
|
||||
if chunks[i].Done {
|
||||
optcom.Parts = append(optcom.Parts, Object{
|
||||
PartNumber: chunks[i].Number, ETag: chunks[i].ETag},
|
||||
)
|
||||
continue
|
||||
}
|
||||
res := <-chresults
|
||||
// Notice one part fail can not get the etag according.
|
||||
if res.Resp == nil || res.err != nil {
|
||||
|
||||
Reference in New Issue
Block a user