From fd8d63bf24d43f1416a8b79874ca2d2ea6977652 Mon Sep 17 00:00:00 2001 From: qydysky Date: Sun, 6 Apr 2025 19:53:37 +0800 Subject: [PATCH] =?utf8?q?Improve=20=E4=BC=98=E5=8C=96fmp4=E4=BF=9D?= =?utf8?q?=E5=AD=98,=E6=9B=B4=E6=96=B0api=20(#185)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * Improve 优化fmp4保存 * Improve 优化fmp4保存 * Improve 依赖更新 * Improve 依赖更新 * Improve 优化停止 * Improve 优化停止 * Improve 依赖更新 * Improve 依赖更新 * Improve 优化提示 * Improve 修正data race * Improve race need cgo * Improve 依赖更新 * Improve set test room * Improve set test room * Improve 更新无效api --- .github/workflows/go.yml | 3 +- .github/workflows/test.yml | 3 +- Reply/F.go | 27 ----- Reply/F/comp.go | 6 ++ Reply/F/rev/rev.go | 53 ++++++++++ Reply/Reply.go | 2 - Reply/fmp4Decode.go | 198 +++++++++++++++---------------------- Reply/stream.go | 66 ++++++++++--- bili_danmu.go | 27 +++-- demo/main.go | 4 +- demo/main_test.go | 16 +++ go.mod | 4 +- go.sum | 8 +- 13 files changed, 239 insertions(+), 178 deletions(-) create mode 100644 Reply/F/rev/rev.go create mode 100644 demo/main_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 731fb08..3ca12a5 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -40,9 +40,10 @@ jobs: run: | sudo apt-get update sudo apt-get install libgtk-3-dev libcairo2-dev libglib2.0-dev + export r=213 go get . go mod vendor - CGO_ENABLED=0 go test -v --cover -coverprofile=coverage ./... + CGO_ENABLED=1 go test -v --cover -coverprofile=coverage -race -count=1 ./... - name: Codecov uses: codecov/codecov-action@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c0d6df..e533bab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,9 +43,10 @@ jobs: run: | sudo apt-get update sudo apt-get install libgtk-3-dev libcairo2-dev libglib2.0-dev + export r=213 go get . go mod vendor - CGO_ENABLED=0 go test -v --cover -coverprofile=coverage ./... + CGO_ENABLED=1 go test -v --cover -coverprofile=coverage -race -count=1 ./... - name: Codecov uses: codecov/codecov-action@v4 diff --git a/Reply/F.go b/Reply/F.go index 8954bf3..de06fab 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -127,33 +127,6 @@ func selfcross2(a []string) (float32, string) { // 功能区 -// 显示营收 -func init() { - if !IsOn("统计营收") { - return - } - go func() { - var ShowRev = make(map[int]float64) - - clog := c.C.Log.Base_add(`营收`) - for { - if _, ok := ShowRev[c.C.Roomid]; !ok && c.C.Roomid != 0 { - ShowRev[c.C.Roomid] = 0 - } - for room, rev := range ShowRev { - if c.C.Roomid != room { - clog.L(`I: `, fmt.Sprintf("%d ¥%.2f", room, c.C.Rev)) - delete(ShowRev, room) - } else if c.C.Rev != rev { - ShowRev[room] = c.C.Rev - clog.L(`I: `, fmt.Sprintf("%d ¥%.2f", room, c.C.Rev)) - } - } - time.Sleep(time.Minute) - } - }() -} - // 获取实例的Common func StreamOCommon(roomid int) (array []*c.Common) { if roomid != -1 { //返回特定房间 diff --git a/Reply/F/comp.go b/Reply/F/comp.go index f75d2b9..06cb920 100644 --- a/Reply/F/comp.go +++ b/Reply/F/comp.go @@ -11,11 +11,17 @@ import ( _ "github.com/qydysky/bili_danmu/Reply/F/danmuEmotes" _ "github.com/qydysky/bili_danmu/Reply/F/danmuji" _ "github.com/qydysky/bili_danmu/Reply/F/parseM3u8" + _ "github.com/qydysky/bili_danmu/Reply/F/rev" _ "github.com/qydysky/bili_danmu/Reply/F/videoFastSeed" comp "github.com/qydysky/part/component2" log "github.com/qydysky/part/log" ) +var Rev = comp.Get[interface { + Init(l *log.Log_interface) + ShowRev(roomid int, rev float64) +}](`rev`) + var DanmuCountPerMin = comp.Get[interface { // will WriteHeader GetRec(savePath string, r *http.Request, w http.ResponseWriter) error diff --git a/Reply/F/rev/rev.go b/Reply/F/rev/rev.go new file mode 100644 index 0000000..30ea9a1 --- /dev/null +++ b/Reply/F/rev/rev.go @@ -0,0 +1,53 @@ +package rev + +import ( + "fmt" + "sync" + "time" + + comp "github.com/qydysky/part/component2" + log "github.com/qydysky/part/log" +) + +func init() { + comp.RegisterOrPanic[interface { + Init(l *log.Log_interface) + ShowRev(roomid int, rev float64) + }](`rev`, &rev{}) +} + +type rev struct { + l *log.Log_interface + currentRoom int + currentRev float64 + lastShow time.Time + sync.Mutex +} + +func (t *rev) Init(l *log.Log_interface) { + t.Lock() + defer t.Unlock() + + t.l = l.Base(`营收`) +} + +func (t *rev) ShowRev(roomid int, rev float64) { + t.Lock() + defer t.Unlock() + + if t.l == nil { + return + } + + if roomid != t.currentRoom { + if t.currentRoom != 0 { + t.l.L(`I: `, fmt.Sprintf("%d ¥%.2f", t.currentRoom, t.currentRev)) + } + t.l.L(`I: `, fmt.Sprintf("%d ¥%.2f", roomid, rev)) + } else if rev != t.currentRev && time.Since(t.lastShow).Minutes() > 1 { + t.lastShow = time.Now() + t.l.L(`I: `, fmt.Sprintf("%d ¥%.2f", roomid, rev)) + } + t.currentRev = rev + t.currentRoom = roomid +} diff --git a/Reply/Reply.go b/Reply/Reply.go index 901913d..b0fa62e 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -5,7 +5,6 @@ import ( "compress/zlib" "context" "encoding/json" - "errors" "fmt" "strconv" "strings" @@ -28,7 +27,6 @@ import ( ) var reply_log = c.C.Log.Base(`Reply`) -var ErrDecode = errors.New(`ErrDecode`) // brotliDecoder type brotliDecoder struct { diff --git a/Reply/fmp4Decode.go b/Reply/fmp4Decode.go index 667769c..b127257 100644 --- a/Reply/fmp4Decode.go +++ b/Reply/fmp4Decode.go @@ -25,6 +25,7 @@ var ( ActionGenFastSeedFmp4 pe.Action = `GenFastSeedFmp4` ActionSeekFmp4 pe.Action = `SeekFmp4` ActionOneFFmp4 pe.Action = `OneFFmp4` + ActionCheckTFail pe.Action = `CheckTFail` ) var boxs map[string]bool @@ -232,25 +233,25 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) } //is t error? - check_set_maxT = func(ts timeStamp, equal func(ts timeStamp) error, larger func(ts timeStamp) error) (err error) { + checkAndSetMaxT = func(ts timeStamp) (err error) { switch ts.handlerType { case 'v': if maxVT == 0 { maxVT = ts.getT() - } else if maxVT == ts.getT() && equal != nil { - err = equal(ts) - } else if maxVT > ts.getT() && larger != nil { - err = larger(ts) + } else if maxVT == ts.getT() { + err = ActionCheckTFail.New("equal VT detect") + } else if maxVT > ts.getT() { + err = ActionCheckTFail.New("lower VT detect") } else { maxVT = ts.getT() } case 'a': if maxAT == 0 { maxAT = ts.getT() - } else if maxAT == ts.getT() && equal != nil { - err = equal(ts) - } else if maxAT > ts.getT() && larger != nil { - err = larger(ts) + } else if maxAT == ts.getT() { + err = ActionCheckTFail.New("equal AT detect") + } else if maxAT > ts.getT() { + err = ActionCheckTFail.New("lower AT detect") } else { maxAT = ts.getT() } @@ -258,6 +259,12 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) } return } + + dropKeyFrame = func(index int) { + t.buf.Reset() + haveKeyframe = false + cu = index + } ) ies, e := decode(buf, "moof") @@ -280,21 +287,13 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) if ts.handlerType == 'v' { if e := checkSampleEntries(m[5].i, m[6].i); e != nil { //skip - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return pe.Join(ErrDealIESkip, e) + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } - if e := check_set_maxT(ts, func(_ timeStamp) error { - return errors.New("skip") - }, func(_ timeStamp) error { - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return errors.New("skip") - }); e != nil { - return pe.Join(ErrDealIESkip, e) + if e := checkAndSetMaxT(ts); e != nil { + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } @@ -304,10 +303,9 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) if keyframeMoof { if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { if e := t.buf.AppendTo(keyframe); e != nil { - return pe.Join(ErrDealIEBreak, e) + return e } - cu = m[0].i - t.buf.Reset() + dropKeyFrame(m[0].i) } haveKeyframe = true } else if !haveKeyframe { @@ -315,7 +313,7 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) } if haveKeyframe { if e := t.buf.Append(buf[m[0].i:m[6].e]); e != nil { - return pe.Join(ErrDealIEBreak, e) + return e } } return nil @@ -338,10 +336,8 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) if handlerType == 'v' { if e := checkSampleEntries(m[5].i, m[6].i); e != nil { //skip - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return pe.Join(ErrDealIESkip, e) + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } switch handlerType { @@ -350,15 +346,9 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) case 's': audio = ts } - if e := check_set_maxT(ts, func(_ timeStamp) error { - return errors.New("skip") - }, func(_ timeStamp) error { - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return errors.New("skip") - }); e != nil { - return pe.Join(ErrDealIESkip, e) + if e := checkAndSetMaxT(ts); e != nil { + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } { @@ -366,10 +356,8 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) if handlerType == 'v' { if e := checkSampleEntries(m[9].i, m[10].i); e != nil { //skip - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return pe.Join(ErrDealIESkip, e) + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } switch handlerType { @@ -378,15 +366,9 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) case 's': audio = ts } - if e := check_set_maxT(ts, func(_ timeStamp) error { - return errors.New("skip") - }, func(_ timeStamp) error { - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return errors.New("skip") - }); e != nil { - return pe.Join(ErrDealIESkip, e) + if e := checkAndSetMaxT(ts); e != nil { + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } @@ -395,7 +377,7 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) t.AVTDiff = 0.1 } if diff := math.Abs(video.getT() - audio.getT()); diff > t.AVTDiff { - return pe.Join(ErrDealIEBreak, fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff)) + return pe.Join(ErrDecode, fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff)) // copy(video.data, F.Itob64(int64(audio.getT()*float64(video.timescale)))) } @@ -403,10 +385,9 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) if keyframeMoof { if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { if e := t.buf.AppendTo(keyframe); e != nil { - return pe.Join(ErrDealIEBreak, e) + return e } - cu = m[0].i - t.buf.Reset() + dropKeyFrame(m[0].i) } haveKeyframe = true } else if !haveKeyframe { @@ -414,7 +395,7 @@ func (t *Fmp4Decoder) Search_stream_fmp4(buf []byte, keyframe *slice.Buf[byte]) } if haveKeyframe { if e := t.buf.Append(buf[m[0].i:m[10].e]); e != nil { - return pe.Join(ErrDealIEBreak, e) + return e } } return nil @@ -497,25 +478,25 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { } //is t error? - check_set_maxT = func(ts timeStamp, equal func(ts timeStamp) error, larger func(ts timeStamp) error) (err error) { + checkAndSetMaxT = func(ts timeStamp) (err error) { switch ts.handlerType { case 'v': if maxVT == 0 { maxVT = ts.getT() - } else if maxVT == ts.getT() && equal != nil { - err = equal(ts) - } else if maxVT > ts.getT() && larger != nil { - err = larger(ts) + } else if maxVT == ts.getT() { + err = ActionCheckTFail.New("equal VT detect") + } else if maxVT > ts.getT() { + err = ActionCheckTFail.New("lower VT detect") } else { maxVT = ts.getT() } case 'a': if maxAT == 0 { maxAT = ts.getT() - } else if maxAT == ts.getT() && equal != nil { - err = equal(ts) - } else if maxAT > ts.getT() && larger != nil { - err = larger(ts) + } else if maxAT == ts.getT() { + err = ActionCheckTFail.New("equal AT detect") + } else if maxAT > ts.getT() { + err = ActionCheckTFail.New("lower AT detect") } else { maxAT = ts.getT() } @@ -523,6 +504,12 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { } return } + + dropKeyFrame = func(index int) { + t.buf.Reset() + haveKeyframe = false + cu = index + } ) ies, e := decode(buf, "moof") @@ -530,7 +517,7 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { return 0, e } - var ErrNormal = pe.New("ErrNormal", "ErrNormal") + var ErrNormal pe.Action = "ErrNormal" err = deals(ies, []dealIE{ @@ -548,24 +535,16 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { if ts.handlerType == 'v' { if e := checkSampleEntries(m[5].i, m[6].i); e != nil { //skip - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return pe.Join(ErrDealIESkip.New(), e) + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } if handlerType == 'v' { video = ts } - if e := check_set_maxT(ts, func(_ timeStamp) error { - return errors.New("skip") - }, func(_ timeStamp) error { - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return errors.New("skip") - }); e != nil { - return pe.Join(ErrDealIESkip.New(), e) + if e := checkAndSetMaxT(ts); e != nil { + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } @@ -574,13 +553,12 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { //deal frame if keyframeMoof { if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { - cu = m[0].i if haveKeyframe && len(w) > 0 { err = w[0](video.getT(), cu, t.buf) - t.buf.Reset() + dropKeyFrame(m[0].i) return ErrNormal } - t.buf.Reset() + dropKeyFrame(m[0].i) } haveKeyframe = true } else if !haveKeyframe { @@ -588,7 +566,7 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { } if haveKeyframe { if e := t.buf.Append(buf[m[0].i:m[6].e]); e != nil { - return pe.Join(ErrDealIEBreak.New(), e) + return e } } return nil @@ -609,10 +587,8 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { if handlerType == 'v' { if e := checkSampleEntries(m[5].i, m[6].i); e != nil { //skip - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return pe.Join(ErrDealIESkip, e) + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } switch handlerType { @@ -621,15 +597,9 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { case 's': audio = ts } - if e := check_set_maxT(ts, func(_ timeStamp) error { - return errors.New("skip") - }, func(_ timeStamp) error { - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return errors.New("skip") - }); e != nil { - return pe.Join(ErrDealIESkip, e) + if e := checkAndSetMaxT(ts); e != nil { + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } { @@ -637,10 +607,8 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { if handlerType == 'v' { if e := checkSampleEntries(m[9].i, m[10].i); e != nil { //skip - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return pe.Join(ErrDealIESkip, e) + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } switch handlerType { @@ -649,15 +617,9 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { case 's': audio = ts } - if e := check_set_maxT(ts, func(_ timeStamp) error { - return errors.New("skip") - }, func(_ timeStamp) error { - t.buf.Reset() - haveKeyframe = false - cu = m[0].i - return errors.New("skip") - }); e != nil { - return pe.Join(ErrDealIESkip, e) + if e := checkAndSetMaxT(ts); e != nil { + dropKeyFrame(m[0].e) + return pe.Join(ErrDecode, e) } } @@ -666,20 +628,19 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { t.AVTDiff = 0.1 } if diff := math.Abs(video.getT() - audio.getT()); diff > t.AVTDiff { - return pe.Join(ErrDealIEBreak, fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff)) + return pe.Join(ErrDecode, fmt.Errorf("时间戳不匹配 %v %v (或许应调整fmp4音视频时间戳容差s>%.2f)", video.timeStamp, audio.timeStamp, diff)) // copy(video.data, F.Itob64(int64(audio.getT()*float64(video.timescale)))) } //deal frame if keyframeMoof { if v, e := t.buf.HadModified(bufModified); e == nil && v && !t.buf.IsEmpty() { - cu = m[0].i if haveKeyframe && len(w) > 0 { err = w[0](video.getT(), cu, t.buf) - t.buf.Reset() + dropKeyFrame(m[0].i) return ErrNormal } - t.buf.Reset() + dropKeyFrame(m[0].i) } haveKeyframe = true } else if !haveKeyframe { @@ -687,7 +648,7 @@ func (t *Fmp4Decoder) oneF(buf []byte, w ...dealFMp4) (cu int, err error) { } if haveKeyframe { if e := t.buf.Append(buf[m[0].i:m[10].e]); e != nil { - return pe.Join(ErrDealIEBreak, e) + return e } } return nil @@ -840,11 +801,6 @@ func (t *Fmp4Decoder) GenFastSeed(reader io.Reader, save func(seedTo time.Durati return } -var ( - ErrDealIEBreak = pe.Action(`ErrDealIEBreak`) - ErrDealIESkip = pe.Action(`ErrDealIESkip`) -) - type dealIE struct { matchCounts int boxNames []string @@ -871,7 +827,7 @@ func deal(ies []ie, dealIEf dealIE) (err error) { func deals(ies []ie, dealIEs []dealIE) (err error) { for cu := 0; cu < len(ies) && len(dealIEs) != 0; cu++ { for i := 0; i < len(dealIEs); i++ { - if e := dealIEs[i].deal(ies, cu); e != nil && !pe.Catch(e, ErrDealIESkip) { + if e := dealIEs[i].deal(ies, cu); e != nil { return e } } diff --git a/Reply/stream.go b/Reply/stream.go index cc49ea6..4f620e8 100644 --- a/Reply/stream.go +++ b/Reply/stream.go @@ -43,6 +43,7 @@ import ( pstring "github.com/qydysky/part/strings" pu "github.com/qydysky/part/util" pweb "github.com/qydysky/part/web" + "slices" ) const ( @@ -141,7 +142,7 @@ func (t *m4s_link_item) getNo() (int, error) { } var ( - AEFDCTO perrors.Action = `ActionErrFmp4DownloadCareTO` + ActionErrFmp4DownloadCareTO perrors.Action = `ActionErrFmp4DownloadCareTO` ) func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf.Rval) (err error) { @@ -164,7 +165,7 @@ func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf. return e } else { if int64(reqConfig.Timeout) < r.UsedTime.Milliseconds()+3000 { - err = perrors.New(fmt.Sprintf("fmp4切片下载超时s(%d)或许应该大于%d", reqConfig.Timeout/1000, (r.UsedTime.Milliseconds()+4000)/1000), AEFDCTO) + err = ActionErrFmp4DownloadCareTO.New(fmt.Sprintf("fmp4切片下载超时s(%d)或许应该大于%d", reqConfig.Timeout/1000, (r.UsedTime.Milliseconds()+4000)/1000)) } link.status = 2 // 设置切片状态为下载完成 return @@ -675,6 +676,8 @@ func (t *M4SStream) getSavepath() { } } +var ErrDecode = perrors.Action("ErrDecode") + func (t *M4SStream) saveStream() (e error) { // 清除初始值 t.first_buf = nil @@ -1134,7 +1137,7 @@ func (t *M4SStream) saveStreamM4s() (e error) { `Connection`: `close`, }, }) - if perrors.Catch(e, AEFDCTO) { + if ActionErrFmp4DownloadCareTO.Catch(e) { t.log.L(`W: `, e.Error()) } else if e != nil { downErr.Store(true) @@ -1181,12 +1184,12 @@ func (t *M4SStream) saveStreamM4s() (e error) { } } t.putM4s(cu) - download_seq = append(download_seq[:k], download_seq[k+1:]...) + download_seq = slices.Delete(download_seq, k, k+1) k -= 1 continue } else if t.first_buf == nil { t.putM4s(cu) - download_seq = append(download_seq[:k], download_seq[k+1:]...) + download_seq = slices.Delete(download_seq, k, k+1) k -= 1 continue } @@ -1195,7 +1198,7 @@ func (t *M4SStream) saveStreamM4s() (e error) { t.log.L(`E: `, e) } t.putM4s(cu) - download_seq = append(download_seq[:k], download_seq[k+1:]...) + download_seq = slices.Delete(download_seq, k, k+1) k -= 1 buff, unlock := buf.GetPureBufRLock() @@ -1203,18 +1206,18 @@ func (t *M4SStream) saveStreamM4s() (e error) { unlock() if err != nil && !errors.Is(err, io.EOF) { - t.log.L(`E: `, err) - //丢弃所有数据 - buf.Reset() - e = err - if skipErrFrame { + if ErrDecode.Catch(err) && skipErrFrame { + t.log.L(`W: `, err) // 将此切片服务器设置停用 // if u, e := url.Parse(cu.Url); e == nil { t.common.DisableLiveAutoByUuid(cu.SerUuid) // t.common.DisableLiveAuto(u.Host) // } } else { - return + e = err + download_seq = download_seq[:0] + _ = pctx.CallCancel(t.Status) + break } } @@ -1427,6 +1430,45 @@ func (t *M4SStream) Start() bool { //保存弹幕 go StartRecDanmu(ctx1, ms.GetSavePath()) + //指定房间录制回调 + if v, ok := ms.common.K_v.LoadV("指定房间录制回调").([]any); ok && len(v) > 0 { + l := l.Base(`录制回调`) + for i := 0; i < len(v); i++ { + if vm, ok := v[i].(map[string]any); ok { + if roomid, ok := vm["roomid"].(float64); ok && int(roomid) == ms.common.Roomid { + var ( + durationS, _ = vm["durationS"].(float64) + start, _ = vm["start"].([]any) + ) + if len(start) >= 2 && durationS >= 0 { + go func() { + ctx2, done2 := pctx.WaitCtx(ctx1) + defer done2() + select { + case <-ctx2.Done(): + case <-time.After(time.Second * time.Duration(durationS)): + var cmds []string + for i := 0; i < len(start); i++ { + if cmd, ok := start[i].(string); ok && cmd != "" { + cmds = append(cmds, strings.ReplaceAll(cmd, "{type}", ms.GetStreamType())) + } + } + + cmd := exec.Command(cmds[0], cmds[1:]...) + cmd.Dir = ms.GetSavePath() + l.L(`I: `, "启动", cmd.Args) + if e := cmd.Run(); e != nil { + l.L(`E: `, e) + } + l.L(`I: `, "结束") + } + }() + } + } + } + } + } + path := ms.GetSavePath() + `0.` + ms.GetStreamType() startT := time.Now() if e := ms.PusherToFile(ctx1, path, startf, stopf); e != nil { diff --git a/bili_danmu.go b/bili_danmu.go index bd40b98..e283a55 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -30,7 +30,7 @@ import ( ws "github.com/qydysky/part/websocket" ) -func Start() { +func Start(rootCtx context.Context) { danmulog := c.C.Log.Base(`bilidanmu`) danmulog.L(`I: `, `当前PID:`, c.C.PID) danmulog.L(`I: `, "version: ", c.C.Version) @@ -97,6 +97,10 @@ func Start() { reply.KeepMedalLight(mainCtx, c.C) //ass初始化 replyFunc.Ass.Init(c.C.K_v.LoadV("Ass")) + //rev初始化 + if c.C.IsOn(`统计营收`) { + replyFunc.Rev.Init(danmulog) + } // 指定房间录制区间 if _, err := recStartEnd.InitF.Run(mainCtx, c.C); err != nil { danmulog.Base("功能", "指定房间录制区间").L(`E: `, err) @@ -149,6 +153,8 @@ func Start() { common, ok := c.Commons.LoadV(c.C.Roomid).(*c.Common) if ok { common.Rev += rev.Rev + // 显示营收 + replyFunc.Rev.ShowRev(common.Roomid, common.Rev) } } return false @@ -234,7 +240,7 @@ func Start() { common.Rev = 0.0 // 营收 } - exitSign = entryRoom(mainCtx, danmulog.BaseAdd(common.Roomid), common) + exitSign = entryRoom(rootCtx, mainCtx, danmulog.BaseAdd(common.Roomid), common) common.InIdle = true @@ -248,7 +254,7 @@ func Start() { } } -func entryRoom(mainCtx context.Context, danmulog *part.Log_interface, common *c.Common) (exitSign bool) { +func entryRoom(rootCtx, mainCtx context.Context, danmulog *part.Log_interface, common *c.Common) (exitSign bool) { //附加功能 自动发送即将过期礼物 go reply.AutoSend_silver_gift(common) //获取热门榜 @@ -381,14 +387,14 @@ func entryRoom(mainCtx context.Context, danmulog *part.Log_interface, common *c. replyFunc.Danmuji.Danmuji_auto(mainCtx, c.C.K_v.LoadV(`自动弹幕机_内容`).([]any), c.C.K_v.LoadV(`自动弹幕机_发送间隔s`).(float64), reply.Msg_senddanmu) } { //附加功能 进房间发送弹幕 直播流保存 每日签到 + F.RoomEntryAction(common.Roomid) // go F.Dosign() - go reply.Entry_danmu(common) + reply.Entry_danmu(common) if _, e := recStartEnd.RecStartCheck.Run(mainCtx, common); e == nil { - go reply.StreamOStart(common, common.Roomid) + reply.StreamOStart(common, common.Roomid) } else { danmulog.Base("功能", "指定房间录制区间").L(`I: `, common.Roomid, e) } - go F.RoomEntryAction(common.Roomid) } //当前ws @@ -443,9 +449,16 @@ func entryRoom(mainCtx context.Context, danmulog *part.Log_interface, common *c. }, }) + danmulog.L(`T: `, "启动完成", common.Uname, `(`, common.Roomid, `)`) + { cancel, c := wsmsg.Pull_tag_chan(`exit`, 1, mainCtx) - <-c + select { + case <-c: + case <-rootCtx.Done(): + common.Danmu_Main_mq.Push_tag(`interrupt`, nil) + <-c + } cancel() } diff --git a/demo/main.go b/demo/main.go index 9f9bb43..5068fc0 100644 --- a/demo/main.go +++ b/demo/main.go @@ -1,9 +1,11 @@ package main import ( + "context" + q "github.com/qydysky/bili_danmu" ) func main() { - q.Start() + q.Start(context.Background()) } diff --git a/demo/main_test.go b/demo/main_test.go new file mode 100644 index 0000000..29f0874 --- /dev/null +++ b/demo/main_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "context" + "testing" + "time" + + q "github.com/qydysky/bili_danmu" +) + +// go test -run ^TestMain$ github.com/qydysky/bili_danmu/demo -race -count=1 -v -r xxx +func TestMain(m *testing.T) { + ctx, c := context.WithTimeout(context.Background(), time.Second*40) + defer c() + q.Start(ctx) +} diff --git a/go.mod b/go.mod index 2d8bfff..18d2b8f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24 require ( github.com/gotk3/gotk3 v0.6.4 github.com/mdp/qrterminal/v3 v3.2.0 - github.com/qydysky/part v0.28.20250313160332 + github.com/qydysky/part v0.28.20250330170611 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 golang.org/x/text v0.23.0 // indirect @@ -13,7 +13,7 @@ require ( require ( github.com/google/uuid v1.6.0 - github.com/qydysky/biliApi v0.0.0-20240725184407-15076dddb6fb + github.com/qydysky/biliApi v0.0.0-20250406112014-bf8c070170f6 github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9 golang.org/x/exp v0.0.0-20250215185904-eff6e970281f ) diff --git a/go.sum b/go.sum index 1e19422..add27d5 100644 --- a/go.sum +++ b/go.sum @@ -42,12 +42,12 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/qydysky/biliApi v0.0.0-20240725184407-15076dddb6fb h1:dtSpNF9hLQa09TUfR+xbYFkHcx2breAFsXeU7e599gE= -github.com/qydysky/biliApi v0.0.0-20240725184407-15076dddb6fb/go.mod h1:om024vfxALQ5vxsbaGoMm8IS0esLYBnEOpJI8FsGoDg= +github.com/qydysky/biliApi v0.0.0-20250406112014-bf8c070170f6 h1:eWklz9YhqcLnJeHxWSlJZmL2V5rRyyEVqLKgLY3ipQs= +github.com/qydysky/biliApi v0.0.0-20250406112014-bf8c070170f6/go.mod h1:1FbgCj+aOwIvuRRuX/l5uTLb3JIwWyJSa0uEfwpYV/8= github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9 h1:k451T+bpsLr+Dq9Ujo+Qtx0iomRA1XXS5ttlEojvfuQ= github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9/go.mod h1:cI8/gy/wjy2Eb+p2IUj2ZuDnC8R5Vrx3O0VMPvMvphA= -github.com/qydysky/part v0.28.20250313160332 h1:3YwhIZwtIrnULREneqmjdQnL7vB1SxYZGVAZUiJAVZU= -github.com/qydysky/part v0.28.20250313160332/go.mod h1:RHYTy8EbqCP6OioVf6BkvFcfWLNO0S220zl0DDlY84Y= +github.com/qydysky/part v0.28.20250330170611 h1:8ll4oVALYXi0wFce12r8BkYRdlw8U50VZs7FI6AZTog= +github.com/qydysky/part v0.28.20250330170611/go.mod h1:RHYTy8EbqCP6OioVf6BkvFcfWLNO0S220zl0DDlY84Y= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -- 2.39.2