291 lines
6.9 KiB
Go
291 lines
6.9 KiB
Go
|
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)
|
|||
|
}
|