You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
6.9 KiB
290 lines
6.9 KiB
package archive |
|
|
|
import ( |
|
"sync" |
|
xtime "time" |
|
|
|
"go-common/library/log" |
|
"go-common/library/time" |
|
) |
|
|
|
// const Video Status |
|
const ( |
|
// video xcode and dispatch state. |
|
VideoUploadInfo = int8(0) |
|
VideoXcodeSDFail = int8(1) |
|
VideoXcodeSDFinish = int8(2) |
|
VideoXcodeHDFail = int8(3) |
|
VideoXcodeHDFinish = int8(4) |
|
VideoDispatchRunning = int8(5) |
|
VideoDispatchFinish = int8(6) |
|
// video status. |
|
VideoStatusOpen = int16(0) |
|
VideoStatusAccess = int16(10000) |
|
VideoStatusWait = int16(-1) |
|
VideoStatusRecicle = int16(-2) |
|
VideoStatusLock = int16(-4) |
|
VideoStatusXcodeFail = int16(-16) |
|
VideoStatusSubmit = int16(-30) |
|
VideoStatusUploadSubmit = int16(-50) |
|
VideoStatusDelete = int16(-100) |
|
// xcode fail |
|
XcodeFailZero = 0 |
|
) |
|
|
|
// Video is archive_video model. |
|
type Video struct { |
|
ID int64 `json:"-"` |
|
Aid int64 `json:"aid"` |
|
Title string `json:"title"` |
|
Desc string `json:"desc"` |
|
Filename string `json:"filename"` |
|
SrcType string `json:"src_type"` |
|
Cid int64 `json:"cid"` |
|
Sid int64 `json:"-"` |
|
Duration int64 `json:"duration"` |
|
Filesize int64 `json:"-"` |
|
Resolutions string `json:"-"` |
|
Index int `json:"index"` |
|
Playurl string `json:"-"` |
|
Status int16 `json:"status"` |
|
FailCode int8 `json:"fail_code"` |
|
XcodeState int8 `json:"xcode_state"` |
|
Attribute int32 `json:"-"` |
|
RejectReason string `json:"reject_reason"` |
|
CTime time.Time `json:"ctime"` |
|
MTime time.Time `json:"-"` |
|
Dimension *Dimension `json:"dimension"` |
|
} |
|
|
|
// Dimension Archive video dimension |
|
type Dimension struct { |
|
Width int64 `json:"width"` |
|
Height int64 `json:"height"` |
|
Rotate int64 `json:"rotate"` |
|
} |
|
|
|
// SimpleVideo for Archive History |
|
type SimpleVideo struct { |
|
Cid int64 `json:"cid"` |
|
Index int `json:"part_id"` |
|
Title string `json:"part_name"` |
|
Status int16 `json:"status"` |
|
MTime time.Time `json:"dm_modified"` |
|
} |
|
|
|
// VideoFn for Archive Video table filename check |
|
type VideoFn struct { |
|
Cid int64 `json:"cid"` |
|
Filename string `json:"filename"` |
|
CTime time.Time `json:"ctime"` |
|
MTime time.Time `json:"mtime"` |
|
} |
|
|
|
type param struct { |
|
undoneCount int // 待完成数总和 |
|
failCount int // 错误数总和 |
|
timeout *xtime.Timer |
|
} |
|
|
|
/*VideosEditor 处理超多分p的稿件编辑 |
|
* 0.对该稿件的编辑操作加锁 |
|
* 1.将所有视频分组分事务更新 |
|
* 2.全部更新成功后发送成功信号量 |
|
* 3.更新错误的分组多次尝试,超时或超过次数后失败 |
|
* 4.收到更新成功信号量进行回调-(1.同步信息给视频云 2.解锁该稿件编辑) |
|
* 5.收到更新失败信号量进行回调- (1.记录错误日志信息 2.推送错误消息 3.解锁该稿件编辑) |
|
*/ |
|
type VideosEditor struct { |
|
sync.Mutex |
|
|
|
failTH int |
|
closeFlag bool |
|
wg sync.WaitGroup |
|
params map[int64]*param |
|
cbSuccess map[int64]func() |
|
cbFail map[int64]func() |
|
sigSuccess chan int64 |
|
sigFail chan int64 |
|
chanRetry chan func() (int64, int, int, error) |
|
closechan, cbclose chan struct{} |
|
} |
|
|
|
// NewEditor new VideosEditor |
|
func NewEditor(failTH int) *VideosEditor { |
|
editor := &VideosEditor{ |
|
failTH: failTH, |
|
wg: sync.WaitGroup{}, |
|
params: make(map[int64]*param), |
|
cbSuccess: make(map[int64]func()), |
|
cbFail: make(map[int64]func()), |
|
sigSuccess: make(chan int64, 10), |
|
sigFail: make(chan int64, 10), |
|
chanRetry: make(chan func() (int64, int, int, error), 100), |
|
closechan: make(chan struct{}, 1), |
|
cbclose: make(chan struct{}), |
|
} |
|
editor.wg.Add(1) |
|
go editor.consumerRetry(&editor.wg) |
|
editor.wg.Add(1) |
|
go editor.consumercb(&editor.wg) |
|
|
|
return editor |
|
} |
|
|
|
// Close 等待所有消息消费完才退出 |
|
func (m *VideosEditor) Close() { |
|
m.closeFlag = true |
|
m.closechan <- struct{}{} |
|
m.wg.Wait() |
|
} |
|
|
|
// Add add to editor |
|
func (m *VideosEditor) Add(aid int64, cbSuccess, cbFail func(), timeout xtime.Duration, retrys ...func() (int64, int, int, error)) { |
|
if m.closeFlag { |
|
log.Warn("VideosEditor closed") |
|
return |
|
} |
|
log.Info("VideosEditor Add(%d) len(%d)", aid, len(retrys)) |
|
|
|
timer := xtime.AfterFunc(timeout, func() { m.notifyTimeout(aid) }) |
|
m.params[aid] = ¶m{ |
|
undoneCount: len(retrys), |
|
failCount: 0, |
|
timeout: timer, |
|
} |
|
m.cbSuccess[aid] = cbSuccess |
|
m.cbFail[aid] = cbFail |
|
for _, ry := range retrys { |
|
m.addRetry(ry, 0) |
|
} |
|
} |
|
|
|
// NotifySuccess notify success |
|
func (m *VideosEditor) notifySuccess(aid int64) { |
|
m.Lock() |
|
defer m.Unlock() |
|
param, ok := m.params[aid] |
|
if ok { |
|
param.undoneCount-- |
|
if param.undoneCount <= 0 { |
|
log.Info("notifySuccess(%d) undoneCount(%d)", aid, param.undoneCount) |
|
m.sigSuccess <- aid |
|
} |
|
} |
|
} |
|
|
|
// NotifyFail 返回值表示达到触发阈值 |
|
func (m *VideosEditor) notifyFail(aid int64) (retry bool) { |
|
m.Lock() |
|
defer m.Unlock() |
|
param, ok := m.params[aid] |
|
if ok { |
|
retry = true |
|
param.failCount++ |
|
if param.failCount >= m.failTH { |
|
log.Warn("notifyFail(%d) failCount(%d)", aid, param.failCount) |
|
retry = false |
|
if _, ok := m.cbFail[aid]; ok { |
|
m.sigFail <- aid |
|
} |
|
} |
|
} |
|
return |
|
} |
|
|
|
func (m *VideosEditor) notifyTimeout(aid int64) { |
|
log.Warn("notifyTimeout(%d)", aid) |
|
m.Lock() |
|
defer m.Unlock() |
|
_, ok := m.params[aid] |
|
if ok { |
|
if _, ok := m.cbFail[aid]; ok { |
|
m.sigFail <- aid |
|
} |
|
} |
|
} |
|
|
|
func (m *VideosEditor) consumercb(g *sync.WaitGroup) { |
|
defer g.Done() |
|
|
|
for { |
|
select { |
|
case aid, ok := <-m.sigSuccess: |
|
if !ok { |
|
log.Info("consumercb close") |
|
return |
|
} |
|
if f, ok := m.cbSuccess[aid]; ok { |
|
f() |
|
m.release(aid) |
|
} |
|
case aid, ok := <-m.sigFail: |
|
if !ok { |
|
log.Info("consumercb close") |
|
return |
|
} |
|
if f, ok := m.cbFail[aid]; ok { |
|
f() |
|
m.release(aid) |
|
} |
|
case <-m.closechan: |
|
if len(m.cbFail) == 0 && len(m.cbSuccess) == 0 { |
|
m.cbclose <- struct{}{} |
|
log.Info("consumercb closechan") |
|
return |
|
} |
|
xtime.Sleep(50 * xtime.Millisecond) |
|
m.closechan <- struct{}{} |
|
} |
|
} |
|
} |
|
|
|
func (m *VideosEditor) consumerRetry(g *sync.WaitGroup) { |
|
defer g.Done() |
|
|
|
for { |
|
log.Info("consumerRetry") |
|
select { |
|
case <-m.cbclose: |
|
log.Info("consumerRetry closed") |
|
return |
|
case f, ok := <-m.chanRetry: |
|
if !ok { |
|
log.Info("m.chanRetry closed") |
|
break |
|
} |
|
id, head, tail, err := f() |
|
log.Info("consumerRetry(%d) head(%d) tail(%d) err(%v) ", id, head, tail, err) |
|
if err != nil { |
|
if m.notifyFail(id) { |
|
go func() { |
|
xtime.Sleep(3 * xtime.Second) |
|
m.addRetry(f, 3) |
|
}() |
|
} |
|
} else { |
|
m.notifySuccess(id) |
|
} |
|
} |
|
} |
|
} |
|
|
|
func (m *VideosEditor) addRetry(f func() (int64, int, int, error), asynctime int) { |
|
if asynctime == 0 { |
|
m.chanRetry <- f |
|
return |
|
} |
|
go func() { |
|
xtime.Sleep(3 * xtime.Second) |
|
m.chanRetry <- f |
|
}() |
|
} |
|
|
|
func (m *VideosEditor) release(aid int64) { |
|
if p, ok := m.params[aid]; ok { |
|
p.timeout.Stop() |
|
} |
|
delete(m.cbFail, aid) |
|
delete(m.cbSuccess, aid) |
|
}
|
|
|