From: qydysky Date: Sat, 18 Nov 2023 04:29:14 +0000 (+0800) Subject: Add 指定房间录制区间 X-Git-Tag: v0.11.6~7 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=3c102ddf48546758678ab638d63056666cc711c7;p=bili_danmu%2F.git Add 指定房间录制区间 --- diff --git a/README.md b/README.md index a3de1c1..e09f236 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,35 @@ ### 说明 本项目使用github action自动构建,构建过程详见[yml](https://github.com/qydysky/bili_danmu/blob/master/.github/workflows/go.yml) +#### 指定房间录制区间 +配置文件中添加配置项`指定房间录制区间`。 + +指定roomid的房间在指定时间段内将会开启录制。 + +- `start`时检查是否在直播,是则开始录制,如已在录制则切片。 +- `end`时如已在录制则停止录制。 +- 在开播时,若在`start`与`end`之间,则录制,不在则不录制(仅检查同时有`start`,`end`的`fromTo`)。 +- 5s内只能触发一个`fromTo`,所以同房间各`start`、`end`之间间隔不要少于5s。 + +```json +{ + "指定房间录制区间":[ + { + "roomid":0, + "fromTo":[ + { + "start": "12:01:00", + "end": "12:03:00" + }, + { + "start": "12:02:00" + } + ] + } + ] +} +``` + #### 保存日志至DB 配置文件中添加配置项`保存日志至db`。大部分可以参考保存弹幕至db,但有些许不同: diff --git a/Reply/F.go b/Reply/F.go index 4bedb3a..0d62204 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -33,10 +33,10 @@ import ( c "github.com/qydysky/bili_danmu/CV" F "github.com/qydysky/bili_danmu/F" _ "github.com/qydysky/bili_danmu/Reply/F" + "github.com/qydysky/bili_danmu/Reply/F/danmuXml" send "github.com/qydysky/bili_danmu/Send" p "github.com/qydysky/part" - comp "github.com/qydysky/part/component" pctx "github.com/qydysky/part/ctx" file "github.com/qydysky/part/file" pio "github.com/qydysky/part/io" @@ -1576,7 +1576,7 @@ func init() { return } else if !file.New(v+"0.xml", 0, true).IsExist() { type empty struct{} - if e := comp.Run(comp.Sign[empty](`SerF`, `player`, `ws`), context.Background(), &v); e != nil { + if e := danmuXml.DanmuXml.Run(context.Background(), &v); e != nil { msglog.L(`E: `, e) } } @@ -1665,7 +1665,7 @@ func init() { } type empty struct{} - if e := comp.Run(comp.Sign[empty](`SerF`, `player`, `xml`), context.Background(), &v); e != nil { + if e := danmuXml.DanmuXml.Run(context.Background(), &v); e != nil { msglog.L(`E: `, e) } } @@ -1705,8 +1705,8 @@ func StartRecDanmu(ctx context.Context, filePath string) { // 弹幕录制结束 type empty struct{} - if e := comp.Run[string](comp.Sign[empty](`startRecDanmu`, `stop`), context.Background(), &filePath); e != nil { - f.L(`E: `, e) + if e := danmuXml.DanmuXml.Run(context.Background(), &filePath); e != nil { + msglog.L(`E: `, e) } Recoder.Stop() diff --git a/Reply/F/comp.go b/Reply/F/comp.go index 39cf657..05df269 100644 --- a/Reply/F/comp.go +++ b/Reply/F/comp.go @@ -1,30 +1 @@ package f - -import ( - "github.com/qydysky/bili_danmu/Reply/F/danmuXml" - "github.com/qydysky/bili_danmu/Reply/F/liveOver" - "github.com/qydysky/bili_danmu/Reply/F/reSetMp4TimeStamp" - comp "github.com/qydysky/part/component" -) - -func init() { - var linkMap = map[string][]string{ - "github.com/qydysky/bili_danmu/Reply.startRecDanmu.stop": { - comp.Sign[danmuXml.Sign](`toXml`), - comp.Sign[reSetMp4TimeStamp.Sign](`_resetTS`), - // comp.Sign[fmp4Tomp4.Sign](`conver`), - }, - "github.com/qydysky/bili_danmu/Reply.SerF.player.ws": { - comp.Sign[danmuXml.Sign](`toXml`), - }, - "github.com/qydysky/bili_danmu/Reply.SerF.player.xml": { - comp.Sign[danmuXml.Sign](`toXml`), - }, - "github.com/qydysky/bili_danmu/Reply.preparing": { - comp.Sign[liveOver.Sign](`sumup`), - }, - } - if e := comp.Link(linkMap); e != nil { - panic(e) - } -} diff --git a/Reply/F/danmuXml/danmuXml.go b/Reply/F/danmuXml/danmuXml.go index 5e5a12d..f1025b9 100644 --- a/Reply/F/danmuXml/danmuXml.go +++ b/Reply/F/danmuXml/danmuXml.go @@ -13,19 +13,8 @@ import ( file "github.com/qydysky/part/file" ) -type Sign struct { - // path: csv所在目录,末尾无 - toXml func(ctx context.Context, path *string) error -} - -func init() { - sign := Sign{ - toXml: toXml, - } - if e := comp.Put[string](comp.Sign[Sign](`toXml`), sign.toXml); e != nil { - panic(e) - } -} +// path +var DanmuXml = comp.NewComp(toXml) type danmu struct { XMLName xml.Name `xml:"i"` diff --git a/Reply/F/fmp4Tomp4/fmp4Tomp4.go b/Reply/F/fmp4Tomp4/fmp4Tomp4.go deleted file mode 100644 index 0c15fa5..0000000 --- a/Reply/F/fmp4Tomp4/fmp4Tomp4.go +++ /dev/null @@ -1,601 +0,0 @@ -package fmp4Tomp4 - -import ( - "bytes" - "context" - "errors" - "fmt" - "time" - - comp "github.com/qydysky/part/component" - file "github.com/qydysky/part/file" -) - -// 直接保存下来的mp4在chrome上无法直接播放 -// -// https://serverfault.com/questions/738881/chrome-makes-way-too-many-requests-22000-while-downloading-mp4-video-34mb -type Sign struct { - // 重设mp4的时间戳 - conver func(ctx context.Context, ptr *string) error -} - -func init() { - sign := Sign{ - conver: conver, - } - if e := comp.Put[string](comp.Sign[Sign](`conver`), sign.conver); e != nil { - panic(e) - } -} - -var ( - ErrParse = errors.New("ErrParse") -) - -func conver(ctx context.Context, ptr *string) error { - be := time.Now() - fmt.Println("conver") - defer func() { fmt.Printf("conver fin (%v)\n", time.Since(be)) }() - - sf := file.New(*ptr+"0.mp4", 0, false) - if !sf.IsExist() { - return nil - } - defer sf.Close() - - r, _ := NewReader(sf) - if e := r.Parse(); e != nil || len(r.Boxs) == 0 { - return e - } - traks, e := r.ParseTrun() - if e != nil { - return e - } - fmt.Printf("conver parse ok (%v)\n", time.Since(be)) - - boxReader := r.Read(r.getN("mvhd", 1)[0], 30) - timescale := boxReader.I32(20) - scaleDur := boxReader.I32(24) - mainDuration := float64(scaleDur) / float64(timescale) - - tf := file.New(*ptr+"1.mp4", 0, false) - if tf.IsExist() { - _ = tf.Delete() - } - defer tf.Close() - - w, e := NewBoxWriter(tf) - if e != nil { - return e - } - - // ftyp - { - ftyp := w.Box("ftyp") - ftyp.Write([]byte("isom")) - ftyp.Write(itob32(512)) - ftyp.Write([]byte("isom")) - ftyp.Write([]byte("iso2")) - ftyp.Write([]byte("avc1")) - ftyp.Write([]byte("mp41")) - if e := ftyp.Close(); e != nil { - return e - } - } - - // moov - { - moov := w.Box("moov") - // mvhd - { - mvhd := moov.Box("mvhd") - mvhd.Write(make([]byte, 12)) - mvhd.Write(itob32(1000)) - mvhd.Write(itob32(int32(mainDuration * 1000))) - mvhd.Write(r.Read(r.getN("mvhd", 1)[0], -1).Buf[28:]) - if e := mvhd.Close(); e != nil { - return e - } - } - // trak - var trakCount = -1 - for trakId, trakSum := range traks { - fmt.Printf("conver traks (%v)(%v)\n", trakId, time.Since(be)) - - trakCount++ - trak := moov.Box("trak") - // tkhd - { - if boxs := r.getN("trak", 2); len(boxs) != 2 { - return errors.Join(ErrParse, fmt.Errorf("trak")) - } else { - tkhd := trak.Box("tkhd") - tkhd.Write([]byte{0, 0, 0, 3}) - tkhd.Write(make([]byte, 8)) - tkhd.Write(itob32(trakId)) - tkhd.Write(make([]byte, 4)) - tkhd.Write(itob32(int32(mainDuration * 1000))) - tkhd.Write(r.Read(boxs[trakCount], -1).Buf[32:]) - if e := tkhd.Close(); e != nil { - return e - } - } - } - // mdia - { - mdia := trak.Box("mdia") - // mdhd - { - if boxs := r.getN("mdhd", 2); len(boxs) != 2 { - return errors.Join(ErrParse, fmt.Errorf("mdhd")) - } else { - mdhd := mdia.Box("mdhd") - mdhd.Write(r.Read(boxs[trakCount], -1).Buf) - if e := mdhd.Close(); e != nil { - return e - } - } - } - // hdlr - var handlerType = make([]byte, 4) - { - if boxs := r.getN("hdlr", 2); len(boxs) != 2 { - return errors.Join(ErrParse, fmt.Errorf("hdlr")) - } else { - hdlr := mdia.Box("hdlr") - boxReader := r.Read(boxs[trakCount], -1) - copy(handlerType, boxReader.Buf[16:20]) - hdlr.Write(boxReader.Buf) - if e := hdlr.Close(); e != nil { - return e - } - } - } - // minf - { - minf := mdia.Box("minf") - // vmhd - if bytes.Equal(handlerType, []byte("vide")) { - if boxs := r.getN("vmhd", 1); len(boxs) != 1 { - return errors.Join(ErrParse, fmt.Errorf("vmhd")) - } else { - vmhd := minf.Box("vmhd") - vmhd.Write(r.Read(boxs[0], -1).Buf) - if e := vmhd.Close(); e != nil { - return e - } - } - } - // dinf - { - if boxs := r.getN("dinf", 2); len(boxs) != 2 { - return errors.Join(ErrParse, fmt.Errorf("dinf")) - } else { - dinf := minf.Box("dinf") - dinf.Write(r.Read(boxs[trakCount], -1).Buf) - if e := dinf.Close(); e != nil { - return e - } - } - } - // stbl - { - stbl := minf.Box("stbl") - // stsd - { - if boxs := r.getN("stsd", 2); len(boxs) != 2 { - return errors.Join(ErrParse, fmt.Errorf("stsd")) - } else { - stsd := stbl.Box("stsd") - stsd.Write(r.Read(boxs[trakCount], -1).Buf) - if e := stsd.Close(); e != nil { - return e - } - } - } - // stts - { - stts := stbl.Box("stts") - stts.Write([]byte{0, 0, 0, 0}) - stts.Write(itob32(int32(len(trakSum.dur)))) - for k, v := range trakSum.dur { - stts.Write(itob32(int32(k))) - stts.Write(itob32(v)) - } - if e := stts.Close(); e != nil { - return e - } - } - // stsc - { - stsc := stbl.Box("stsc") - stsc.Write([]byte{0, 0, 0, 0}) - stsc.Write(itob32(int32(len(trakSum.sampleCount)))) - for k, v := range trakSum.sampleCount { - stsc.Write(itob32(int32(k))) - stsc.Write(itob32(v)) - stsc.Write([]byte{0, 0, 0, 1}) - } - if e := stsc.Close(); e != nil { - return e - } - } - // stsz - { - stsz := stbl.Box("stsz") - stsz.Write([]byte{0, 0, 0, 0}) - stsz.Write(itob32(int32(len(trakSum.size)))) - for _, v := range trakSum.size { - stsz.Write(itob32(v)) - } - if e := stsz.Close(); e != nil { - return e - } - } - // co64 - { - co64 := stbl.Box("co64") - co64.Write([]byte{0, 0, 0, 0}) - co64.Write(itob32(int32(len(trakSum.chunkSize)))) - - cuIndex, _ := tf.CurIndex() - cuIndex += int64(8*len(trakSum.chunkSize)) + 8 - - co64.Write(itob64(cuIndex)) - for i := 0; i < len(trakSum.chunkSize)-1; i++ { - co64.Write(itob64(cuIndex + trakSum.chunkSize[i])) - } - if e := co64.Close(); e != nil { - return e - } - } - if e := stbl.Close(); e != nil { - return e - } - } - if e := minf.Close(); e != nil { - return e - } - } - if e := mdia.Close(); e != nil { - return e - } - } - if e := trak.Close(); e != nil { - return e - } - } - if e := moov.Close(); e != nil { - return e - } - fmt.Printf("conver moov fin (%v)\n", time.Since(be)) - } - - // mdat - { - mdat := w.Box("mdat") - for i := 0; i < len(r.Boxs); i++ { - box := r.Boxs[i] - if box.Name == "mdat" { - if e := sf.SeekIndex(box.Index+box.HeaderSize, file.AtOrigin); e != nil { - return e - } - mdat.CopyFrom(sf, uint64(box.Size-box.HeaderSize)) - } - } - if e := mdat.Close(); e != nil { - return e - } - } - return nil -} - -// func bufChange(buf []byte, size int) { -// if n := size - len(buf); n > 0 { -// if size <= cap(buf) { -// buf = buf[:size] -// } else { -// buf = append(buf, make([]byte, n)...) -// } -// } else if n < 0 { -// buf = buf[:size] -// } -// clear(buf) -// } - -// type track struct { -// chunkSize []int64 -// sampleCount []int32 -// dur []int32 -// size []int32 -// } - -// type wt struct { -// sf *reader -// f *file.File -// buf []byte -// m map[string]float64 -// } - -// func NewWt(sf *reader, tf *file.File) *wt { -// return &wt{ -// sf: sf, -// f: tf, -// buf: make([]byte, 1<<20), -// m: make(map[string]float64), -// } -// } - -// func (t *wt) start(boxName string, wrongPanic bool) (wSize int) { -// if _, e := t.f.Write([]byte{0, 0, 0, 1}, false); e != nil { -// panic(e) -// } -// if _, e := t.f.Write([]byte(boxName), false); e != nil { -// panic(e) -// } -// if _, e := t.f.Write(make([]byte, 8), false); e != nil { -// panic(e) -// } -// wSize = 16 -// return -// } - -// func (t *wt) skipt(size int) (n int) { -// if size == 0 { -// return -// } -// t.bufChange(size) -// if n, e := io.Writer(t.f.File()).Write(t.buf); e != nil { -// panic(e) -// } else { -// return n -// } -// } - -// func (t *wt) skips(size int64) (n int64) { -// if size == 0 { -// return -// } -// if e := t.sf.f.SeekIndex(size, file.AtCurrent); e != nil { -// panic(e) -// } -// return size -// } - -// func (t *wt) w(p []byte) (n int) { -// if n, e := t.f.Write(p, false); e != nil { -// panic(e) -// } else { -// return n -// } -// } - -// func (t *wt) r(size int) (n int64) { -// t.bufChange(size) -// if n, e := t.sf.f.Read(t.buf); e != nil { -// panic(e) -// } else { -// return int64(n) -// } -// } - -// func (t *wt) bufChange(size int) { -// if n := size - len(t.buf); n > 0 { -// if size <= cap(t.buf) { -// t.buf = t.buf[:size] -// } else { -// t.buf = append(t.buf, make([]byte, n)...) -// } -// } else if n < 0 { -// t.buf = t.buf[:size] -// } -// clear(t.buf) -// } - -// func (t *wt) fin(size int) int { -// if e := t.f.SeekIndex(-int64(size), file.AtCurrent); e != nil { -// panic(e) -// } -// if e := t.f.SeekIndex(8, file.AtCurrent); e != nil { -// panic(e) -// } -// if _, e := t.f.Write(itob64(int64(size)), false); e != nil { -// panic(e) -// } -// if e := t.f.SeekIndex(int64(size-16), file.AtCurrent); e != nil { -// panic(e) -// } -// return size -// } - -// func (t *wt) copyBox(boxName string) int { -// wSize := t.start(boxName, true) -// t.r(int(lSize)) -// wSize += t.w(t.buf) -// return t.fin(wSize) -// } - -// func (t *wt) ftyp() { -// n := t.start("ftyp", true) -// n += t.w([]byte("isom")) -// n += t.w(itob32(512)) -// n += t.w([]byte("isom")) -// n += t.w([]byte("iso2")) -// n += t.w([]byte("avc1")) -// n += t.w([]byte("mp41")) -// t.fin(n) -// } - -// func (t *wt) moov() { -// n, _ := t.start("moov", true) -// n += t.mvhd() -// n += t.trak() -// t.fin(n) -// } - -// func (t *wt) mvhd() int { -// wSize, lSize := t.start("mvhd", true) -// lSize -= t.skips(12) -// lSize -= t.r(4) -// timescale := btoi32(t.buf, 0) -// lSize -= t.r(4) -// duration := btoi32(t.buf, 0) -// t.m["mainDuration"] = float64(duration) / float64(timescale) -// wSize += t.skipt(12) -// wSize += t.w(itob32(1000)) -// wSize += t.w(itob32(timescale * duration / 1000)) -// lSize -= t.r(56) -// wSize += t.w(t.buf) -// t.skips(lSize) -// return t.fin(wSize) -// } - -// func (t *wt) trak() int { -// wSize, _ := t.start("trak", false) -// if wSize == 0 { -// return 0 -// } - -// wSize += t.tkhd() -// wSize += t.mdia() - -// return t.fin(wSize) -// } - -// func (t *wt) tkhd() int { -// wSize, lSize := t.start("mvhd", true) -// lSize -= t.r(12) -// wSize += t.w(t.buf) -// lSize -= t.r(4) -// wSize += t.w(t.buf) -// wSize += t.skipt(4) -// wSize += t.w(itob32(int32(t.m["mainDuration"] / 1000))) -// wSize += t.skipt(10) -// lSize -= t.skips(18) -// lSize -= t.r(52) -// wSize += t.w(t.buf) -// t.skips(lSize) -// return t.fin(wSize) -// } - -// func (t *wt) mdia() int { -// wSize, _ := t.start("mdia", false) -// if wSize == 0 { -// return 0 -// } - -// wSize += t.mdhd() -// wSize += t.copyBox(`hdlr`) -// wSize += t.minf() - -// return t.fin(wSize) -// } - -// func (t *wt) mdhd() int { -// wSize, lSize := t.start("mdhd", true) -// lSize -= t.r(16) -// wSize += t.w(t.buf) -// lSize -= t.skips(8) -// wSize += t.w(itob32(1000)) -// wSize += t.w(itob32(int32(t.m["mainDuration"] / 1000))) -// lSize -= t.r(4) -// wSize += t.w(t.buf) -// t.skips(lSize) -// return t.fin(wSize) -// } - -// func (t *wt) minf() int { -// wSize, _ := t.start("mdia", false) -// if wSize == 0 { -// return 0 -// } - -// wSize += t.copyBox(`vmhd`) -// wSize += t.copyBox(`dinf`) -// wSize += t.stbl() - -// return t.fin(wSize) -// } - -// func (t *wt) stbl() int { -// wSize, _ := t.start("stbl", false) -// if wSize == 0 { -// return 0 -// } - -// wSize += t.copyBox(`stsd`) -// wSize += t.stts() - -// return t.fin(wSize) -// } - -// func (t *wt) stts() int { -// wSize, lSize := t.start("stts", true) -// for { -// lSize -= t.r(1 << 20) -// } -// // lSize -= t.r(16) -// // wSize += t.w(t.buf) -// // lSize -= t.skips(8) -// // wSize += t.w(itob32(1000)) -// // wSize += t.w(itob32(int32(t.m["mainDuration"] / 1000))) -// // lSize -= t.r(4) -// // wSize += t.w(t.buf) -// t.skips(lSize) -// return t.fin(wSize) -// } - -func btoi64(b []byte, offset int) int64 { - s := 8 - bu := make([]byte, s) - l := len(b) - offset - if l > s { - l = s - } - for i := 0; i < s && i < l; i++ { - bu[i+s-l] = b[offset+i] - } - - //binary.BigEndian.Uint64 - return int64(uint64(bu[7]) | uint64(bu[6])<<8 | uint64(bu[5])<<16 | uint64(bu[4])<<24 | - uint64(bu[3])<<32 | uint64(bu[2])<<40 | uint64(bu[1])<<48 | uint64(bu[0])<<56) -} - -func btoi32(b []byte, offset int) int32 { - s := 4 - bu := make([]byte, s) - l := len(b) - offset - if l > s { - l = s - } - for i := 0; i < s && i < l; i++ { - bu[i+s-l] = b[offset+i] - } - - //binary.BigEndian.Uint32 - return int32((uint32(bu[3]) | uint32(bu[2])<<8 | uint32(bu[1])<<16 | uint32(bu[0])<<24)) -} - -func itob64(v int64) []byte { - //binary.BigEndian.PutUint64 - b := make([]byte, 8) - b[0] = byte(v >> 56) - b[1] = byte(v >> 48) - b[2] = byte(v >> 40) - b[3] = byte(v >> 32) - b[4] = byte(v >> 24) - b[5] = byte(v >> 16) - b[6] = byte(v >> 8) - b[7] = byte(v) - return b -} - -func itob32(v int32) []byte { - //binary.BigEndian.PutUint32 - b := make([]byte, 4) - b[0] = byte(v >> 24) - b[1] = byte(v >> 16) - b[2] = byte(v >> 8) - b[3] = byte(v) - return b -} diff --git a/Reply/F/fmp4Tomp4/fmp4Tomp4_test.go b/Reply/F/fmp4Tomp4/fmp4Tomp4_test.go deleted file mode 100644 index 38c3809..0000000 --- a/Reply/F/fmp4Tomp4/fmp4Tomp4_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package fmp4Tomp4 - -// func Test_conver(t *testing.T) { -// path := "/codefile/testdata/" -// if e := conver(context.Background(), &path); e != nil { -// t.Fatal(e) -// } -// } diff --git a/Reply/F/fmp4Tomp4/reader.go b/Reply/F/fmp4Tomp4/reader.go deleted file mode 100644 index 9ed2249..0000000 --- a/Reply/F/fmp4Tomp4/reader.go +++ /dev/null @@ -1,241 +0,0 @@ -package fmp4Tomp4 - -import ( - "bytes" - "errors" - "fmt" - - file "github.com/qydysky/part/file" - "golang.org/x/exp/slices" -) - -type Box struct { - Name string - Size int64 - HeaderSize int64 - Index int64 -} - -type reader struct { - f *file.File - Boxs []Box - buf []byte -} - -func NewReader(f *file.File) (*reader, error) { - if f.Config.AutoClose { - return nil, errors.New("file AutoClose must false") - } - return &reader{f: f}, nil -} - -func (t *reader) getN(name string, n int) (ls []Box) { - for i := 0; i < len(t.Boxs); i++ { - box := t.Boxs[i] - if box.Name == name { - ls = append(ls, box) - if len(ls) >= n { - return - } - } - } - return -} - -type BoxReader struct { - Buf []byte -} - -func (t *BoxReader) I32(offset int) int32 { - return btoi32(t.Buf, offset) -} -func (t *BoxReader) I64(offset int) int64 { - return btoi64(t.Buf, offset) -} - -func (t *reader) Read(box Box, size int) BoxReader { - if size == -1 { - t.bufChange(int(box.Size)) - } else { - t.bufChange(size) - } - _ = t.f.SeekIndex(box.Index, file.AtOrigin) - _, _ = t.f.Read(t.buf) - return BoxReader{t.buf} -} - -func (t *reader) bufChange(size int) { - if n := size - len(t.buf); n > 0 { - if size <= cap(t.buf) { - t.buf = t.buf[:size] - } else { - t.buf = append(t.buf, make([]byte, n)...) - } - } else if n < 0 { - t.buf = t.buf[:size] - } - clear(t.buf) -} - -// 正常将返回io.EOF -func (t *reader) Parse() error { - stat, e := t.f.Stat() - if e != nil { - return e - } - - var ( - b4 = make([]byte, 4) - b8 = make([]byte, 8) - parseInside = []string{"moov", "trak", "mdia", "minf", "stbl", "moof", "traf"} - ) - for i := int64(0); i < stat.Size(); { - boxHeaderSize := 0 - if n, e := t.f.Read(b4); e != nil { - return e - } else { - boxHeaderSize += n - } - size := int64(btoi32(b4, 0)) - if n, e := t.f.Read(b4); e != nil { - return e - } else { - boxHeaderSize += n - } - name := string(b4) - if size == 1 { - if n, e := t.f.Read(b8); e != nil { - return e - } else { - boxHeaderSize += n - } - size = btoi64(b8, 0) - } - t.Boxs = append(t.Boxs, Box{ - Name: name, - Size: size, - Index: i, - HeaderSize: int64(boxHeaderSize), - }) - if !slices.Contains(parseInside, name) { - seedSize := size - int64(boxHeaderSize) - if e := t.f.SeekIndex(seedSize, file.AtCurrent); e != nil { - return e - } - i += size - } else { - i += int64(boxHeaderSize) - } - } - return nil -} - -type Track struct { - chunkSize []int64 - sampleCount []int32 - dur []int32 - size []int32 -} - -func (t *reader) ParseTrun() (tracks map[int32]*Track, err error) { - tracks = make(map[int32]*Track) - - var ( - b4 = make([]byte, 4) - b8 = make([]byte, 8) - b12 = make([]byte, 12) - ) - - for i := 0; i < len(t.Boxs); i++ { - box := t.Boxs[i] - if box.Name == "tfhd" { - _ = t.f.SeekIndex(box.Index+box.HeaderSize, file.AtOrigin) - - if _, e := t.f.Read(b8); e != nil { - err = e - return - } - trackID := btoi32(b8, 4) - defaultSampleDuration := int32(0) - { - var offset int64 - if b8[3]&0x01 == 0x01 { - offset += 8 - } - if b8[3]&0x02 == 0x02 { - offset += 4 - } - if b8[3]&0x08 == 0x08 { - _ = t.f.SeekIndex(offset, file.AtCurrent) - if _, e := t.f.Read(b4); e != nil { - err = e - return - } - } - defaultSampleDuration = btoi32(b4, 0) - } - - trackO, ok := tracks[trackID] - if !ok { - tracks[trackID] = &Track{} - trackO = tracks[trackID] - } - - for ; i < len(t.Boxs); i++ { - box := t.Boxs[i] - if box.Name == "trun" { - _ = t.f.SeekIndex(box.Index+box.HeaderSize, file.AtOrigin) - if _, e := t.f.Read(b8); e != nil { - err = e - return - } - - _ = t.f.SeekIndex(8, file.AtCurrent) - - var chunkSize int64 - if bytes.Equal(b8[:4], []byte{0x01, 0x00, 0x0b, 0x05}) { - sampleCount := btoi32(b8, 4) - if sampleCount*12 != int32(box.Size-24) { - err = errors.New("wrong trun trunSize not match sampleCount") - return - } - trackO.sampleCount = append(trackO.sampleCount, sampleCount) - for i := int32(0); i < sampleCount; i++ { - if _, e := t.f.Read(b12); e != nil { - err = e - return - } - trackO.dur = append(trackO.dur, btoi32(b12, 0)) - trackO.size = append(trackO.size, btoi32(b12, 4)) - chunkSize += int64(btoi32(b12, 4)) - } - } else if bytes.Equal(b8[:4], []byte{0x01, 0x00, 0x02, 0x05}) { - sampleCount := btoi32(b8, 4) - if sampleCount*4 != int32(box.Size-24) { - err = errors.New("wrong trun trunSize not match sampleCount") - return - } - trackO.sampleCount = append(trackO.sampleCount, sampleCount) - for i := int32(0); i < sampleCount; i++ { - if _, e := t.f.Read(b4); e != nil { - err = e - return - } - trackO.dur = append(trackO.dur, defaultSampleDuration) - trackO.size = append(trackO.size, btoi32(b4, 0)) - chunkSize += int64(btoi32(b4, 0)) - } - } else { - err = fmt.Errorf("wrong trun tr_flag(%v)", b8[:4]) - return - } - trackO.chunkSize = append(trackO.chunkSize, chunkSize) - - break - } - } - } - } - - return -} diff --git a/Reply/F/fmp4Tomp4/reader_test.go b/Reply/F/fmp4Tomp4/reader_test.go deleted file mode 100644 index 5f4bb4c..0000000 --- a/Reply/F/fmp4Tomp4/reader_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package fmp4Tomp4 - -// func Test_parse(t *testing.T) { -// var read = reader{ -// f: file.New("/codefile/testdata/0.mp4", 0, false), -// } -// if e := read.Parse(); e != nil && !errors.Is(e, io.EOF) { -// t.Fatal(e) -// } -// t.Log(len(read.Boxs)) - -// m, e := read.ParseTrun() -// t.Log(m) -// t.Log(e) -// // for i := 0; i < len(read.boxs); i++ { -// // t.Log(read.boxs[i].name) -// // } -// } diff --git a/Reply/F/fmp4Tomp4/writer.go b/Reply/F/fmp4Tomp4/writer.go deleted file mode 100644 index 2e4edc4..0000000 --- a/Reply/F/fmp4Tomp4/writer.go +++ /dev/null @@ -1,80 +0,0 @@ -package fmp4Tomp4 - -import ( - "errors" - "fmt" - - file "github.com/qydysky/part/file" - pio "github.com/qydysky/part/io" -) - -var ( - ErrFileAutoClose = errors.New("file AutoClose must false") - ErrSeed = errors.New("ErrSeed") - ErrWrite = errors.New("ErrWrite") -) - -type boxWriter struct { - f *file.File - wn int64 - e error - p *boxWriter -} - -func NewBoxWriter(f *file.File) (t *boxWriter, err error) { - if f.Config.AutoClose { - return nil, ErrFileAutoClose - } - t = &boxWriter{f: f} - return -} - -func (t *boxWriter) Box(name string) (tc *boxWriter) { - if t.e != nil { - return - } - tc = &boxWriter{f: t.f, p: t, e: t.e} - tc.Write([]byte{0, 0, 0, 1}) - tc.Write([]byte(name)) - tc.wn = 0 - tc.Write(make([]byte, 8)) - return -} - -func (t *boxWriter) Write(b []byte) (tc *boxWriter) { - if t.e != nil { - return t - } - n := 0 - n, t.e = t.f.Write(b, false) - t.wn += int64(n) - return t -} - -func (t *boxWriter) CopyFrom(f *file.File, size uint64) (tc *boxWriter) { - if t.e != nil { - return t - } - t.e = f.CopyTo(t.f, pio.CopyConfig{MaxByte: size}, false) - t.wn += int64(size) - return t -} - -func (t *boxWriter) Close() error { - if t.e != nil { - return t.e - } - t.e = t.f.SeekIndex(-t.wn, file.AtCurrent) - if t.e != nil { - return errors.Join(ErrSeed, t.e, fmt.Errorf("Arg %v", -t.wn)) - } - _, t.e = t.f.Write(itob64(t.wn), false) - if t.e != nil { - return errors.Join(ErrWrite, t.e, fmt.Errorf("Arg %v", -t.wn)) - } - t.e = t.f.SeekIndex(t.wn-8, file.AtCurrent) - if t.p != nil { - t.p.wn += t.wn - } - return t.e -} diff --git a/Reply/F/fmp4Tomp4/writer_test.go b/Reply/F/fmp4Tomp4/writer_test.go deleted file mode 100644 index 25ed19e..0000000 --- a/Reply/F/fmp4Tomp4/writer_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package fmp4Tomp4 - -// func Test_w(t *testing.T) { -// tf := file.New("1.mp4", 0, false) -// if tf.IsExist() { -// _ = tf.Delete() -// } -// defer tf.Close() - -// w, e := NewBoxWriter(tf) -// if e != nil { -// t.Fatal(e) -// } - -// // ftyp -// { -// ftyp := w.Box("ftyp") -// ftyp.Write([]byte("isom")) -// ftyp.Write(itob32(512)) -// ftyp.Write([]byte("isom")) -// ftyp.Write([]byte("iso2")) -// ftyp.Write([]byte("avc1")) -// ftyp.Write([]byte("mp41")) -// if e := ftyp.Close(); e != nil { -// t.Fatal(e) -// } -// } -// } diff --git a/Reply/F/liveOver/liveOver.go b/Reply/F/liveOver/liveOver.go index d579ced..6ffea3f 100644 --- a/Reply/F/liveOver/liveOver.go +++ b/Reply/F/liveOver/liveOver.go @@ -9,19 +9,8 @@ import ( comp "github.com/qydysky/part/component" ) -type Sign struct { - // 下播总结 - sumup func(ctx context.Context, ptr *c.Common) error -} - -func init() { - sign := Sign{ - sumup: sumup, - } - if e := comp.Put[c.Common](comp.Sign[Sign](`sumup`), sign.sumup); e != nil { - panic(e) - } -} +// *c.Common +var Sumup = comp.NewComp(sumup) func sumup(ctx context.Context, ptr *c.Common) error { dura := time.Since(ptr.Live_Start_Time).Round(time.Second) diff --git a/Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go b/Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go deleted file mode 100644 index 4222e17..0000000 --- a/Reply/F/reSetMp4TimeStamp/reSetMp4TimeStamp.go +++ /dev/null @@ -1,285 +0,0 @@ -package reSetMp4TimeStamp - -import ( - "context" - "errors" - "fmt" - "io" - "time" - - comp "github.com/qydysky/part/component" - file "github.com/qydysky/part/file" -) - -// 直接保存下来的mp4在chrome上无法直接播放 -// -// https://serverfault.com/questions/738881/chrome-makes-way-too-many-requests-22000-while-downloading-mp4-video-34mb -type Sign struct { - // 重设mp4的时间戳 - resetTS func(ctx context.Context, ptr *string) error -} - -func init() { - sign := Sign{ - resetTS: resetTS, - } - if e := comp.Put[string](comp.Sign[Sign](`resetTS`), sign.resetTS); e != nil { - panic(e) - } -} - -func resetTS(ctx context.Context, ptr *string) error { - be := time.Now() - fmt.Println("resetTS") - defer func() { fmt.Printf("resetTS fin (%v)\n", time.Since(be)) }() - - f := file.New(*ptr+"0.mp4", 0, false) - if !f.IsExist() { - return nil - } - defer f.Close() - - var ( - byte4 = make([]byte, 4) - byte16 = make([]byte, 16) - bgdts = make(map[int32]int64) - eddts = make(map[int32]int64) - zdts = make(map[int32]*guessDts) - // timescale = make(map[int32]int64) - ) - - for { - if e := f.SeekUntil([]byte("tkhd"), file.AtCurrent, 1<<17, 1<<22); e != nil { - if errors.Is(e, file.ErrMaxReadSizeReach) { - break - } - if errors.Is(e, io.EOF) { - break - } - return e - } - _ = f.SeekIndex(16, file.AtCurrent) - if _, e := f.Read(byte4); e != nil { - return e - } - trackId := btoi32(byte4, 0) - - bgdts[trackId] = -1 - eddts[trackId] = 0 - zdts[trackId] = &guessDts{0, 1} - } - - // _ = f.SeekIndex(0, file.AtOrigin) - // for { - // if e := f.SeekUntil([]byte("tkhd"), file.AtCurrent, 1<<17, 1<<22); e != nil { - // if errors.Is(e, file.ErrMaxReadSizeReach) { - // break - // } - // if errors.Is(e, io.EOF) { - // break - // } - // return e - // } - // _ = f.SeekIndex(16, file.AtCurrent) - // if _, e := f.Read(byte4); e != nil { - // return e - // } - // trackId := btoi32(byte4, 0) - - // if e := f.SeekUntil([]byte("mdhd"), file.AtCurrent, 1<<17, 1<<22); e != nil { - // if errors.Is(e, file.ErrMaxReadSizeReach) { - // break - // } - // if errors.Is(e, io.EOF) { - // break - // } - // return e - // } - // _ = f.SeekIndex(16, file.AtCurrent) - // if _, e := f.Read(byte4); e != nil { - // return e - // } - - // timescale[trackId] = int64(btoi32(byte4, 0)) - // } - - // rewrite dts - { - _ = f.SeekIndex(0, file.AtOrigin) - for { - if e := f.SeekUntil([]byte("tfhd"), file.AtCurrent, 1<<17, 1<<20); e != nil { - if errors.Is(e, file.ErrMaxReadSizeReach) { - continue - } - if errors.Is(e, io.EOF) { - break - } - return e - } - _ = f.SeekIndex(8, file.AtCurrent) - if _, e := f.Read(byte4); e != nil { - return e - } - trackID := btoi32(byte4, 0) - - if e := f.SeekUntil([]byte("tfdt"), file.AtCurrent, 1<<17, 1<<20); e != nil { - if errors.Is(e, file.ErrMaxReadSizeReach) { - continue - } - if errors.Is(e, io.EOF) { - break - } - return e - } - if _, e := f.Read(byte16); e != nil { - return e - } - switch byte16[4] { - case 0: - ts := int64(btoi32(byte16, 12)) - if e := f.SeekIndex(-4, file.AtCurrent); e != nil { - return e - } - if bgdts[trackID] == -1 { - bgdts[trackID] = ts - } - if _, e := f.Write(itob32(int32(ts-bgdts[trackID])), false); e != nil { - return e - } - eddts[trackID] = ts - case 1: - ts := btoi64(byte16, 8) - if e := f.SeekIndex(-8, file.AtCurrent); e != nil { - return e - } - if bgdts[trackID] == -1 { - bgdts[trackID] = ts - } - if _, e := f.Write(itob64(ts-bgdts[trackID]), false); e != nil { - return e - } - eddts[trackID] = ts - default: - return fmt.Errorf("unknow tfdt version %x", byte16[8]) - } - } - } - - // var duration int32 - // // for k, v := range bgdts { - // // duration = int32((eddts[k] - v) / timescale[k]) - // // break - // // } - - // _ = f.SeekIndex(0, file.AtOrigin) - // { - // if e := f.SeekUntil([]byte("moov"), file.AtCurrent, 1<<17, 1<<22); e != nil { - // return e - // } - // } - - // // write mvhd - // _ = f.SeekIndex(0, file.AtOrigin) - // { - // if e := f.SeekUntil([]byte("mvhd"), file.AtCurrent, 1<<17, 1<<22); e != nil { - // return e - // } - // _ = f.SeekIndex(20, file.AtCurrent) - // if _, e := f.Write(itob32(duration), false); e != nil { - // return e - // } - // } - - // // write tkhd mdhd - // _ = f.SeekIndex(0, file.AtOrigin) - // for { - // if e := f.SeekUntil([]byte("tkhd"), file.AtCurrent, 1<<17, 1<<20); e != nil { - // if errors.Is(e, file.ErrMaxReadSizeReach) { - // break - // } - // if errors.Is(e, io.EOF) { - // break - // } - // return e - // } - // _ = f.SeekIndex(24, file.AtCurrent) - // if _, e := f.Write(itob32(duration), false); e != nil { - // return e - // } - - // if e := f.SeekUntil([]byte("mdhd"), file.AtCurrent, 1<<17, 1<<22); e != nil { - // if errors.Is(e, file.ErrMaxReadSizeReach) { - // continue - // } - // if errors.Is(e, io.EOF) { - // break - // } - // return e - // } - // _ = f.SeekIndex(20, file.AtCurrent) - // if _, e := f.Write(itob32(duration), false); e != nil { - // return e - // } - // } - return nil -} - -type guessDts struct { - zdts int64 - count int64 -} - -func btoi64(b []byte, offset int) int64 { - s := 8 - bu := make([]byte, s) - l := len(b) - offset - if l > s { - l = s - } - for i := 0; i < s && i < l; i++ { - bu[i+s-l] = b[offset+i] - } - - //binary.BigEndian.Uint64 - return int64(uint64(bu[7]) | uint64(bu[6])<<8 | uint64(bu[5])<<16 | uint64(bu[4])<<24 | - uint64(bu[3])<<32 | uint64(bu[2])<<40 | uint64(bu[1])<<48 | uint64(bu[0])<<56) -} - -func btoi32(b []byte, offset int) int32 { - s := 4 - bu := make([]byte, s) - l := len(b) - offset - if l > s { - l = s - } - for i := 0; i < s && i < l; i++ { - bu[i+s-l] = b[offset+i] - } - - //binary.BigEndian.Uint32 - return int32((uint32(bu[3]) | uint32(bu[2])<<8 | uint32(bu[1])<<16 | uint32(bu[0])<<24)) -} - -func itob64(v int64) []byte { - //binary.BigEndian.PutUint64 - b := make([]byte, 8) - b[0] = byte(v >> 56) - b[1] = byte(v >> 48) - b[2] = byte(v >> 40) - b[3] = byte(v >> 32) - b[4] = byte(v >> 24) - b[5] = byte(v >> 16) - b[6] = byte(v >> 8) - b[7] = byte(v) - return b -} - -func itob32(v int32) []byte { - //binary.BigEndian.PutUint32 - b := make([]byte, 4) - b[0] = byte(v >> 24) - b[1] = byte(v >> 16) - b[2] = byte(v >> 8) - b[3] = byte(v) - return b -} diff --git a/Reply/F/recStartEnd/recStartEnd.go b/Reply/F/recStartEnd/recStartEnd.go new file mode 100644 index 0000000..699c5a2 --- /dev/null +++ b/Reply/F/recStartEnd/recStartEnd.go @@ -0,0 +1,166 @@ +package recStartEnd + +import ( + "context" + "errors" + "fmt" + "math" + "time" + + c "github.com/qydysky/bili_danmu/CV" + comp "github.com/qydysky/part/component" + log "github.com/qydysky/part/log" + "golang.org/x/exp/slices" +) + +var ( + InitF = comp.NewComp(initf) + RecStartCheck = comp.NewComp(recStartCheck) + LoopCheck = comp.NewComp(loopCheck) +) + +type dur struct { + start int + end int +} + +var ( + logg *log.Log_interface + roomSetting map[int][]dur + timePoints []int +) + +func initf(ctx context.Context, ptr *c.Common) (err error) { + if list, ok := ptr.K_v.LoadV("指定房间录制区间").([]any); ok { + logg = ptr.Log.Base("功能", "指定房间录制区间") + defer func() { + if err != nil { + clear(roomSetting) + clear(timePoints) + } + }() + if roomSetting == nil { + roomSetting = make(map[int][]dur) + } + clear(roomSetting) + for _, v := range list { + if vm, ok := v.(map[string]any); ok { + if roomid, ok := vm["roomid"].(float64); ok && int(roomid) > 0 { + var durs []dur + if sts, ok := vm["fromTo"].([]any); ok { + for _, v := range sts { + if vm, ok := v.(map[string]any); ok { + var durv dur + if start, ok := vm["start"].(string); ok { + if tt, e := time.Parse(time.TimeOnly, start); e != nil { + err = e + return + } else { + durv.start = tt.Hour()*3600 + tt.Minute()*60 + tt.Second() + 1 + timePoints = append(timePoints, durv.start) + } + } + if end, ok := vm["end"].(string); ok { + if tt, e := time.Parse(time.TimeOnly, end); e != nil { + err = e + return + } else { + durv.end = tt.Hour()*3600 + tt.Minute()*60 + tt.Second() + 1 + timePoints = append(timePoints, durv.end) + } + } + durs = append(durs, durv) + } + } + } + logg.L(`T: `, "加载规则", fmt.Sprintf("%d %d条", int(roomid), len(durs))) + roomSetting[int(roomid)] = durs + } + } + } + slices.Sort(timePoints) + } + return nil +} + +func recStartCheck(ctx context.Context, ptr *c.Common) error { + if setting, ok := roomSetting[ptr.Roomid]; ok { + now := time.Now() + t := now.Hour()*3600 + now.Minute()*60 + now.Second() + 1 + for _, v := range setting { + if v.start != 0 && v.end != 0 && t <= v.end && t >= v.start { + return nil + } + } + return errors.New("当前不在设定时间段内") + } + return nil +} + +type StreamCtl struct { + C *c.Common + State func(int) bool + Start func(int) + End func(int) + Cut func(int) +} + +var streamCtl StreamCtl + +func loopCheck(ctx context.Context, ptr StreamCtl) error { + streamCtl = ptr + setNextFunc() + return nil +} + +func setNextFunc() { + if len(timePoints) == 0 { + return + } + + now := time.Now() + t := now.Hour()*3600 + now.Minute()*60 + now.Second() + 1 + + var tmp []int + for i := 0; i < len(timePoints); i++ { + if t > timePoints[i] { + tmp = append(tmp, timePoints[i]+60*60*24-t) + } else { + tmp = append(tmp, timePoints[i]-t) + } + } + slices.Sort(tmp) + + // logg.L(`T: `, "下个时间点", time.Now().Add(time.Second*time.Duration(tmp[0])).Format(time.DateTime)) + + time.AfterFunc(time.Second*time.Duration(tmp[0]), func() { + if streamCtl.C.Liveing { + if setting, ok := roomSetting[streamCtl.C.Roomid]; ok { + now := time.Now() + t := now.Hour()*3600 + now.Minute()*60 + now.Second() + 1 + for _, v := range setting { + if v.start != 0 && math.Abs(float64(t-v.start)) < 5 { + if streamCtl.State(streamCtl.C.Roomid) { + logg.L(`T: `, "切片", streamCtl.C.Roomid) + streamCtl.Cut(streamCtl.C.Roomid) + } else { + logg.L(`T: `, "开始", streamCtl.C.Roomid) + streamCtl.Start(streamCtl.C.Roomid) + } + time.Sleep(time.Second * 5) + break + } + if v.end != 0 && math.Abs(float64(t-v.end)) < 5 { + if streamCtl.State(streamCtl.C.Roomid) { + logg.L(`T: `, "结束", streamCtl.C.Roomid) + streamCtl.End(streamCtl.C.Roomid) + } + time.Sleep(time.Second * 5) + break + } + } + } + } + setNextFunc() + }) +} diff --git a/Reply/Reply.go b/Reply/Reply.go index ee3005f..1acf3bb 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -13,10 +13,11 @@ import ( brotli "github.com/andybalholm/brotli" c "github.com/qydysky/bili_danmu/CV" F "github.com/qydysky/bili_danmu/F" + "github.com/qydysky/bili_danmu/Reply/F/liveOver" + "github.com/qydysky/bili_danmu/Reply/F/recStartEnd" ws_msg "github.com/qydysky/bili_danmu/Reply/ws_msg" send "github.com/qydysky/bili_danmu/Send" p "github.com/qydysky/part" - comp "github.com/qydysky/part/component" mq "github.com/qydysky/part/msgq" pstrings "github.com/qydysky/part/strings" ) @@ -772,7 +773,7 @@ func (replyF) preparing(s string) { StreamOStop(roomId) // 下播总结 type empty struct{} - if e := comp.Run(comp.Sign[empty](`preparing`), context.Background(), c.C); e != nil { + if e := liveOver.Sumup.Run(context.Background(), c.C); e != nil { msglog.L(`E: `, e) } } @@ -804,7 +805,15 @@ func (replyF) live(s string) { if v, ok := c.C.K_v.LoadV(`仅保存当前直播间流`).(bool); ok && v { StreamOStop(-2) //停止其他房间录制 } - StreamOStart(c.C.Roomid) + if e := recStartEnd.RecStartCheck.Run(context.Background(), c.C); e == nil { + if StreamOStatus(c.C.Roomid) { + StreamOCut(c.C.Roomid) + } else { + StreamOStart(c.C.Roomid) + } + } else { + msglog.L(`W: `, "房间", type_item.Roomid, e) + } //有时不返回弹幕 开播刷新弹幕 c.C.Danmu_Main_mq.Push_tag(`flash_room`, nil) }() diff --git a/bili_danmu.go b/bili_danmu.go index 3823438..d8a74e3 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -15,6 +15,7 @@ import ( c "github.com/qydysky/bili_danmu/CV" F "github.com/qydysky/bili_danmu/F" reply "github.com/qydysky/bili_danmu/Reply" + "github.com/qydysky/bili_danmu/Reply/F/recStartEnd" send "github.com/qydysky/bili_danmu/Send" Cmd "github.com/qydysky/bili_danmu/cmd" sys "github.com/qydysky/part/sys" @@ -87,6 +88,10 @@ func Start() { F.Dosign() // 附加功能 savetojson reply.SaveToJson.Init() + // 指定房间录制区间 + if err := recStartEnd.InitF.Run(context.Background(), c.C); err != nil { + danmulog.Base("功能", "指定房间录制区间").L(`E: `, err) + } //使用带tag的消息队列在功能间传递消息 { @@ -285,9 +290,20 @@ func Start() { reply.Danmuji_auto() } { //附加功能 进房间发送弹幕 直播流保存 每日签到 + _ = recStartEnd.LoopCheck.Run(context.Background(), recStartEnd.StreamCtl{ + C: c.C, + State: reply.StreamOStatus, + Start: reply.StreamOStart, + End: reply.StreamOStop, + Cut: func(i int) { reply.StreamOCut(i) }, + }) go F.Dosign() go reply.Entry_danmu() - go reply.StreamOStart(c.C.Roomid) + if e := recStartEnd.RecStartCheck.Run(context.Background(), c.C); e == nil { + go reply.StreamOStart(c.C.Roomid) + } else { + danmulog.Base("功能", "指定房间录制区间").L(`I: `, c.C.Roomid, e) + } go F.RoomEntryAction(c.C.Roomid) } diff --git a/demo/config/config_K_v.json b/demo/config/config_K_v.json index c217672..531e455 100644 --- a/demo/config/config_K_v.json +++ b/demo/config/config_K_v.json @@ -104,6 +104,21 @@ "after":["ffmpeg","-i","0.{type}","-y","-c","copy","1.{type}"] } ], + "指定房间录制区间-help":"指定roomid的房间在指定时间段内将会开启录制.start时检查是否在直播,是则开始录制,如已在录制则切片.end时停止录制.在开播时,若在start与end之间,则录制,5s内只能触发一个fromTo", + "指定房间录制区间":[ + { + "roomid":0, + "fromTo":[ + { + "start": "12:01:00", + "end": "12:03:00" + }, + { + "start": "12:02:00" + } + ] + } + ], "Web服务地址-help":"填写本程序各组件所用的服务地址 例0.0.0.0:10000 为空时不启动Web服务", "Web服务地址":"0.0.0.0:10000", "Web服务连接限制-help": "限制回放连接数,<0无限制,=0禁止,>0最大数量", diff --git a/go.mod b/go.mod index c89d018..64df009 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/gotk3/gotk3 v0.6.2 github.com/mdp/qrterminal/v3 v3.2.0 - github.com/qydysky/part v0.28.1-0.20231109160627-cb7ab257995b + github.com/qydysky/part v0.28.1-0.20231118041002-d67071eadd31 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.14.0 diff --git a/go.sum b/go.sum index fa1df2a..1374fea 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= 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/part v0.28.1-0.20231109160627-cb7ab257995b h1:nKmP2PJdgpFBVWl9O92zKNS9k7MgBGXUGH0jdXpd7Xo= -github.com/qydysky/part v0.28.1-0.20231109160627-cb7ab257995b/go.mod h1:twb1IuSmUJ3hllGLwWTBjXRkHjsgmiYi3B9H2ENgIf0= +github.com/qydysky/part v0.28.1-0.20231118041002-d67071eadd31 h1:JDZ3yCv4bO1WQP05Le1KwhQL4uopNASbmb6Aovr9yd4= +github.com/qydysky/part v0.28.1-0.20231118041002-d67071eadd31/go.mod h1:NyKyjpBCSjcHtKlC+fL5lCidm57UCnwEgufiBDs5yxA= 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=