From: qydysky Date: Mon, 8 Jul 2024 18:23:58 +0000 (+0000) Subject: Improve 独立Common X-Git-Tag: v0.14.7~5 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=29330933d61acb03733920998c6a1be973c7f990;p=bili_danmu%2F.git Improve 独立Common --- diff --git a/CV/Var.go b/CV/Var.go index 9ac13f0..d8535bd 100644 --- a/CV/Var.go +++ b/CV/Var.go @@ -43,37 +43,38 @@ type StreamType struct { } type Common struct { - PID int `json:"pid"` //进程id - Version string `json:"version"` //版本 - Uid int `json:"-"` //client uid - Live []*LiveQn `json:"live"` //直播流链接 - Live_qn int `json:"liveQn"` //当前直播流质量 - Live_want_qn int `json:"-"` //期望直播流质量 - Roomid int `json:"roomid"` //房间ID - Cookie syncmap.Map `json:"-"` //Cookie - Title string `json:"title"` //直播标题 - Uname string `json:"uname"` //主播名 - UpUid int `json:"upUid"` //主播uid - Rev float64 `json:"rev"` //营收 - Renqi int `json:"renqi"` //人气 - Watched int `json:"watched"` //观看人数 - OnlineNum int `json:"onlineNum"` //在线人数 - GuardNum int `json:"guardNum"` //舰长数 - ParentAreaID int `json:"parentAreaID"` //父分区 - AreaID int `json:"areaID"` //子分区 - Locked bool `json:"locked"` //直播间封禁 - Note string `json:"note"` //分区排行 - Live_Start_Time time.Time `json:"liveStartTime"` //直播开始时间 - Liveing bool `json:"liveing"` //是否在直播 - Wearing_FansMedal int `json:"-"` //当前佩戴的粉丝牌 - Token string `json:"-"` //弹幕钥 - WSURL []string `json:"-"` //弹幕链接 - LiveBuvidUpdated time.Time `json:"-"` //LIVE_BUVID更新时间 - Stream_url *url.URL `json:"-"` //直播Web服务 - Proxy string `json:"-"` //全局代理 - SerLocation int `json:"-"` //服务器时区 - AcceptQn map[int]string `json:"-"` //允许的直播流质量 - Qn map[int]string `json:"-"` //全部直播流质量 + InIdle bool `json:"-"` //闲置中? + PID int `json:"-"` //进程id + Version string `json:"-"` //版本 + Uid int `json:"-"` //client uid + Live []*LiveQn `json:"live"` //直播流链接 + Live_qn int `json:"liveQn"` //当前直播流质量 + Live_want_qn int `json:"-"` //期望直播流质量 + Roomid int `json:"-"` //房间ID + Cookie syncmap.Map `json:"-"` //Cookie + Title string `json:"title"` //直播标题 + Uname string `json:"uname"` //主播名 + UpUid int `json:"upUid"` //主播uid + Rev float64 `json:"rev"` //营收 + Renqi int `json:"renqi"` //人气 + Watched int `json:"watched"` //观看人数 + OnlineNum int `json:"onlineNum"` //在线人数 + GuardNum int `json:"guardNum"` //舰长数 + ParentAreaID int `json:"parentAreaID"` //父分区 + AreaID int `json:"areaID"` //子分区 + Locked bool `json:"locked"` //直播间封禁 + Note string `json:"note"` //分区排行 + Live_Start_Time time.Time `json:"-"` //直播开始时间 + Liveing bool `json:"liveing"` //是否在直播 + Wearing_FansMedal int `json:"WearingFansMedal"` //当前佩戴的粉丝牌 + Token string `json:"-"` //弹幕钥 + WSURL []string `json:"-"` //弹幕链接 + LiveBuvidUpdated time.Time `json:"-"` //LIVE_BUVID更新时间 + Stream_url *url.URL `json:"-"` //直播Web服务 + Proxy string `json:"-"` //全局代理 + SerLocation int `json:"-"` //服务器时区 + AcceptQn map[int]string `json:"-"` //允许的直播流质量 + Qn map[int]string `json:"-"` //全部直播流质量 // StreamType StreamType `json:"streamType"` //当前直播流类型 AllStreamType map[string]StreamType `json:"-"` //直播流类型 K_v syncmap.Map `json:"-"` //配置文件 @@ -86,6 +87,40 @@ type Common struct { Cache syncmap.MapExceeded[string, any] `json:"-"` //缓存 } +func (t *Common) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Live []*LiveQn + LiveQn int + Title string + Uname string + UpUid int + Rev float64 + Watched int + OnlineNum int + GuardNum int + ParentAreaID int + AreaID int + Locked bool + Note string + LiveStartTime string + }{ + Live: append([]*LiveQn{}, t.Live...), + LiveQn: t.Live_qn, + Title: t.Title, + Uname: t.Uname, + UpUid: t.UpUid, + Rev: t.Rev, + Watched: t.Watched, + OnlineNum: t.OnlineNum, + GuardNum: t.GuardNum, + ParentAreaID: t.ParentAreaID, + AreaID: t.AreaID, + Locked: t.Locked, + Note: t.Note, + LiveStartTime: t.Live_Start_Time.Format(time.DateTime), + }) +} + type LiveQn struct { Url string `json:"-"` Uuid string `json:"-"` @@ -156,6 +191,7 @@ func (t *Common) IsOn(key string) bool { func (t *Common) Copy() *Common { var c = Common{ + InIdle: t.InIdle, PID: t.PID, Version: t.Version, Uid: t.Uid, @@ -440,7 +476,10 @@ func (t *Common) Init() *Common { streams := make(map[int]any) - StreamO.Range(func(key, value any) bool { + Commons.Range(func(key, value any) bool { + if common, ok := value.(*Common); ok && common.InIdle { + return true + } streams[key.(int)] = value return true }) @@ -624,6 +663,12 @@ var C = new(Common).Init() // fmp4 // https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming var StreamO = new(sync.Map) +var Commons = new(syncmap.Map) +var CommonsLoadOrStore = syncmap.LoadOrStoreFunc[Common]{ + Init: func() *Common { + return C.Copy() + }, +} // 消息队列 type Danmu_Main_mq_item struct { diff --git a/F/api.go b/F/api.go index aa284e3..42a9961 100644 --- a/F/api.go +++ b/F/api.go @@ -292,6 +292,18 @@ func (t *GetFunc) Get(key string) { } } +// 房间实际id +func GetRoomRealId(roomid int) int { + if err, res := biliApi.GetRoomBaseInfo(roomid); err == nil { + return res.RoomID + } else if err, res := biliApi.GetInfoByRoom(roomid); err == nil { + return res.RoomID + } else { + apilog.Base_add(`房间实际id`).L(`E: `, err) + return roomid + } +} + func (t *GetFunc) GetUid() (missKey []string) { if uid, ok := t.Cookie.LoadV(`DedeUserID`).(string); !ok { //cookie中无DedeUserID missKey = append(missKey, `Cookie`) diff --git a/Reply/F.go b/Reply/F.go index b91b5de..50b2f67 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -37,6 +37,7 @@ import ( p "github.com/qydysky/part" pctx "github.com/qydysky/part/ctx" file "github.com/qydysky/part/file" + fctrl "github.com/qydysky/part/funcCtrl" pio "github.com/qydysky/part/io" limit "github.com/qydysky/part/limit" msgq "github.com/qydysky/part/msgq" @@ -354,19 +355,19 @@ func StreamOStop(roomid int) { } // 实例切断 -func StreamOCut(roomid int) (setTitle func(string)) { +func StreamOCut(roomid int) (setTitle func(title ...string)) { if v, ok := c.StreamO.Load(roomid); ok { if !pctx.Done(v.(*M4SStream).Status) { v.(*M4SStream).Cut() flog.L(`I: `, `已切片 `+strconv.Itoa(roomid)) - return func(title string) { - if title != "" { - v.(*M4SStream).Common().Title = title + return func(title ...string) { + if len(title) > 0 { + v.(*M4SStream).Common().Title = title[0] } } } } - return func(s string) {} + return func(s ...string) {} } // type Obs struct { @@ -967,37 +968,45 @@ func (t *saveToJson) Close() { var SaveToJson saveToJson // 进入房间发送弹幕 -func Entry_danmu() { +func Entry_danmu(common *c.Common) { flog := flog.Base_add(`进房弹幕`) //检查与切换粉丝牌,只在cookie存在时启用 - F.Get(c.C).Get(`CheckSwitch_FansMedal`) + F.Get(common).Get(`CheckSwitch_FansMedal`) - if v, _ := c.C.K_v.LoadV(`进房弹幕_有粉丝牌时才发`).(bool); v && c.C.Wearing_FansMedal == 0 { + if v, _ := common.K_v.LoadV(`进房弹幕_有粉丝牌时才发`).(bool); v && common.Wearing_FansMedal == 0 { flog.L(`T: `, `无粉丝牌`) return } - if v, _ := c.C.K_v.LoadV(`进房弹幕_仅发首日弹幕`).(bool); v { - res := F.Get_weared_medal(c.C.Uid, c.C.UpUid) + if v, _ := common.K_v.LoadV(`进房弹幕_仅发首日弹幕`).(bool); v { + res := F.Get_weared_medal(common.Uid, common.UpUid) if res.TodayIntimacy > 0 { flog.L(`T: `, `今日已发弹幕`) return } } - if array, ok := c.C.K_v.LoadV(`进房弹幕_内容`).([]interface{}); ok && len(array) != 0 { + if array, ok := common.K_v.LoadV(`进房弹幕_内容`).([]interface{}); ok && len(array) != 0 { rand := p.Rand().MixRandom(0, int64(len(array)-1)) - send.Danmu_s(array[rand].(string), c.C.Roomid) + send.Danmu_s(array[rand].(string), common.Roomid) } } +var fc_Keep_medal_light fctrl.SkipFunc + // 保持所有牌子点亮 -func Keep_medal_light() { - if v, _ := c.C.K_v.LoadV(`保持牌子亮着`).(bool); !v { +func Keep_medal_light(common *c.Common) { + if fc_Keep_medal_light.NeedSkip() { + return + } else { + defer fc_Keep_medal_light.UnSet() + } + + if v, _ := common.K_v.LoadV(`保持牌子亮着`).(bool); !v { return } flog := flog.Base_add(`保持亮牌`) - array, ok := c.C.K_v.LoadV(`进房弹幕_内容`).([]interface{}) + array, ok := common.K_v.LoadV(`进房弹幕_内容`).([]interface{}) if !ok || len(array) == 0 { flog.L(`I: `, `进房弹幕_内容 为 空,退出`) return @@ -1065,20 +1074,28 @@ func Keep_medal_light() { } } +var fc_AutoSend_silver_gift fctrl.SkipFunc + // 自动发送即将过期的银瓜子礼物 -func AutoSend_silver_gift() { - day, _ := c.C.K_v.LoadV(`发送还有几天过期的礼物`).(float64) +func AutoSend_silver_gift(common *c.Common) { + if fc_AutoSend_silver_gift.NeedSkip() { + return + } else { + defer fc_AutoSend_silver_gift.UnSet() + } + + day, _ := common.K_v.LoadV(`发送还有几天过期的礼物`).(float64) if day <= 0 { return } - if c.C.UpUid == 0 { - F.Get(c.C).Get(`UpUid`) + if common.UpUid == 0 { + F.Get(common).Get(`UpUid`) } for _, v := range F.Gift_list() { if time.Now().Add(time.Hour*time.Duration(24*int(day))).Unix() > int64(v.Expire_at) { - send.Send_gift(v.Gift_id, v.Bag_id, v.Gift_num) + send.Send_gift(common, v.Gift_id, v.Bag_id, v.Gift_num) } } diff --git a/Reply/Reply.go b/Reply/Reply.go index adbcc51..0f670f1 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -360,7 +360,13 @@ func (t replyF) user_toast_msg(s string) { } if price != 0 { sh_log = append(sh, "ï¿¥", price/1000) //不在界面显示价格 - t.Common.Danmu_Main_mq.Push_tag(`c.Rev_add`, float64(price)/1000) + t.Common.Danmu_Main_mq.Push_tag(`c.Rev_add`, struct { + Roomid int + Rev float64 + }{ + Roomid: t.Roomid, + Rev: float64(price) / 1000, + }) } { //语言tts t.Common.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 @@ -417,7 +423,13 @@ func (t replyF) heartbeat(s int) { if v, ok := t.Common.K_v.LoadV("下播后不记录人气观看人数").(bool); ok && v && !t.Common.Liveing { return } - t.Common.Danmu_Main_mq.Push_tag(`c.Renqi`, s) //使用连续付费的新舰长无法区分,刷新舰长数 + t.Common.Danmu_Main_mq.Push_tag(`c.Renqi`, struct { + Roomid int + Renqi int + }{ + Roomid: t.Roomid, + Renqi: s, + }) //使用连续付费的新舰长无法区分,刷新舰长数 if s == 1 { return } //人气为1,不输出 @@ -505,6 +517,7 @@ func (t replyF) watched_change(s string) { if data.Data.Num == t.Common.Watched { return } + // fmt.Printf("\t观看人数:%d\n", data.Data.Num) t.Common.Watched = data.Data.Num var pperm = float64(t.Common.Watched) / float64(time.Since(t.Common.Live_Start_Time)/time.Minute) msglog.Base_add("房").Log_show_control(false).L(`I: `, "观看人数", data.Data.Num, fmt.Sprintf(" avg:%.1f人/分", pperm)) @@ -756,7 +769,13 @@ func (t replyF) send_gift(s string) { if total_coin != 0 { allprice = float64(total_coin) / 1000 sh_log = append(sh, fmt.Sprintf("ï¿¥%.1f", allprice)) //不在界面显示价格 - t.Common.Danmu_Main_mq.Push_tag(`c.Rev_add`, allprice) + t.Common.Danmu_Main_mq.Push_tag(`c.Rev_add`, struct { + Roomid int + Rev float64 + }{ + Roomid: t.Roomid, + Rev: allprice, + }) } if len(sh) == 0 { @@ -916,7 +935,13 @@ func (t replyF) super_chat_message(s string) { if price != 0 { sh = append(sh, "\n") //界面不显示价格 logg = append(logg, "ï¿¥", price) - t.Common.Danmu_Main_mq.Push_tag(`c.Rev_add`, float64(price)) + t.Common.Danmu_Main_mq.Push_tag(`c.Rev_add`, struct { + Roomid int + Rev float64 + }{ + Roomid: t.Roomid, + Rev: float64(price), + }) } fmt.Println("====") fmt.Println(sh...) diff --git a/Reply/stream.go b/Reply/stream.go index 71b8fe7..18633b6 100644 --- a/Reply/stream.go +++ b/Reply/stream.go @@ -178,8 +178,6 @@ func (link *m4s_link_item) download(reqPool *pool.Buf[reqf.Req], reqConfig reqf. } func (t *M4SStream) MarshalJSON() ([]byte, error) { - t.common.Rev = c.C.Rev - t.common.Watched = c.C.Watched return json.MarshalIndent(t.common, "", " ") } diff --git a/Send/Send_gift.go b/Send/Send_gift.go index a2cb3fa..a540170 100644 --- a/Send/Send_gift.go +++ b/Send/Send_gift.go @@ -16,40 +16,40 @@ import ( // 每2s一个令牌,最多等10秒 var gift_limit = limit.New(1, "2s", "10s") -func Send_gift(gift_id, bag_id, gift_num int) { - log := c.C.Log.Base_add(`发送礼物`) +func Send_gift(common *c.Common, gift_id, bag_id, gift_num int) { + log := common.Log.Base_add(`发送礼物`) if gift_limit.TO() { log.L(`W: `, "超时") return } - if c.C.UpUid == 0 { + if common.UpUid == 0 { log.L(`W: `, "还未获取到Up主uid") return } - if c.C.Locked { + if common.Locked { log.L(`W: `, "房间被封禁") return } { //发送请求(银瓜子礼物) - csrf, _ := c.C.Cookie.LoadV(`bili_jct`).(string) + csrf, _ := common.Cookie.LoadV(`bili_jct`).(string) if csrf == `` { log.L(`E: `, "Cookie错误,无bili_jct=") return } - var sendStr = `uid=` + strconv.Itoa(c.C.Uid) + `&` + + var sendStr = `uid=` + strconv.Itoa(common.Uid) + `&` + `gift_id=` + strconv.Itoa(gift_id) + `&` + - `ruid=` + strconv.Itoa(c.C.UpUid) + `&` + + `ruid=` + strconv.Itoa(common.UpUid) + `&` + `send_ruid=0&` + `gift_num=` + strconv.Itoa(gift_num) + `&` + `bag_id=` + strconv.Itoa(bag_id) + `&` + `platform=pc&` + `biz_code=live&` + - `biz_id=` + strconv.Itoa(c.C.Roomid) + `&` + + `biz_id=` + strconv.Itoa(common.Roomid) + `&` + `rnd=` + strconv.Itoa(int(time.Now().Unix())) + `&` + `storm_beat_id=0&` + `metadata=&` + @@ -59,18 +59,18 @@ func Send_gift(gift_id, bag_id, gift_num int) { `visit_id=` Cookie := make(map[string]string) - c.C.Cookie.Range(func(k, v interface{}) bool { + common.Cookie.Range(func(k, v interface{}) bool { Cookie[k.(string)] = v.(string) return true }) - req := c.C.ReqPool.Get() - defer c.C.ReqPool.Put(req) + req := common.ReqPool.Get() + defer common.ReqPool.Put(req) if e := req.Reqf(reqf.Rval{ Url: `https://api.live.bilibili.com/xlive/revenue/v2/gift/sendBag`, PostStr: url.PathEscape(sendStr), Timeout: 10 * 1000, - Proxy: c.C.Proxy, + Proxy: common.Proxy, Header: map[string]string{ `Host`: `api.vc.bilibili.com`, `User-Agent`: c.UA, @@ -102,7 +102,7 @@ func Send_gift(gift_id, bag_id, gift_num int) { return } for i := 0; i < len(res.Data.GiftList); i++ { - log.L(`I: `, `给`, c.C.Roomid, `赠送了`, res.Data.GiftList[i].GiftNum, `个`, res.Data.GiftList[i].GiftName) + log.L(`I: `, `给`, common.Roomid, `赠送了`, res.Data.GiftList[i].GiftNum, `个`, res.Data.GiftList[i].GiftName) } } } diff --git a/bili_danmu.go b/bili_danmu.go index 2026f82..ba47c95 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -20,6 +20,7 @@ import ( send "github.com/qydysky/bili_danmu/Send" Cmd "github.com/qydysky/bili_danmu/cmd" pctx "github.com/qydysky/part/ctx" + part "github.com/qydysky/part/log" sys "github.com/qydysky/part/sys" msgq "github.com/qydysky/part/msgq" @@ -112,7 +113,7 @@ func Start() { // 指定弹幕重启录制 if _, err := danmuReLiveTriger.Init.Run(mainCtx, danmuReLiveTriger.DanmuReLiveTriger{ StreamCut: func(i int, title ...string) { - reply.StreamOCut(i)(title[0]) + reply.StreamOCut(i)(title...) }, C: c.C, }); err != nil { @@ -128,25 +129,39 @@ func Start() { //使用带tag的消息队列在功能间传递消息 { var cancelfunc = c.C.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ - `change_room`: func(_ any) bool { //房间改变 - c.C.Rev = 0.0 // 营收 - c.C.Renqi = 1 // 人气置1 - c.C.Watched = 0 // 观看人数 - c.C.OnlineNum = 0 // 在线人数 - c.C.GuardNum = 0 // 舰长数 - c.C.Note = `` // 分区排行 - c.C.Uname = `` // 主播id - c.C.Title = `` - c.C.Wearing_FansMedal = 0 - return false - }, + // `change_room`: func(_ any) bool { //房间改变 + // c.C.Rev = 0.0 // 营收 + // c.C.Renqi = 1 // 人气置1 + // c.C.Watched = 0 // 观看人数 + // c.C.OnlineNum = 0 // 在线人数 + // c.C.GuardNum = 0 // 舰长数 + // c.C.Note = `` // 分区排行 + // c.C.Uname = `` // 主播id + // c.C.Title = `` + // c.C.Wearing_FansMedal = 0 + // return false + // }, `c.Rev_add`: func(data any) bool { //收入 - c.C.Rev += data.(float64) + if rev, ok := data.(struct { + Roomid int + Rev float64 + }); ok { + common, ok := c.Commons.LoadV(c.C.Roomid).(*c.Common) + if ok { + common.Rev += rev.Rev + } + } return false }, `c.Renqi`: func(data any) bool { //人气更新 - if tmp, ok := data.(int); ok { - c.C.Renqi = tmp + if tmp, ok := data.(struct { + Roomid int + Renqi int + }); ok { + common, ok := c.Commons.LoadV(c.C.Roomid).(*c.Common) + if ok { + common.Renqi = tmp.Renqi + } } return false }, @@ -167,22 +182,20 @@ func Start() { } for exitSign := false; !exitSign; { - common := c.C.Copy() - - if common.Roomid == 0 { + if c.C.Roomid == 0 { fmt.Println("回车查看指令") ctx, cancel := context.WithCancel(mainCtx) - cancel1, c := common.Danmu_Main_mq.Pull_tag_chan(`change_room`, 1, ctx) + cancel1, ch := c.C.Danmu_Main_mq.Pull_tag_chan(`change_room`, 1, ctx) select { - case roomid := <-c: - common.Roomid = roomid.(int) + case roomid := <-ch: + c.C.Roomid = roomid.(int) case <-interrupt_chan: exitSign = true } cancel1() cancel() } else { - fmt.Print("房间号: ", strconv.Itoa(common.Roomid), "\n") + fmt.Print("房间号: ", strconv.Itoa(c.C.Roomid), "\n") } if exitSign { @@ -190,234 +203,255 @@ func Start() { } danmulog.L(`T: `, "准备") + //如果连接中断,则等待 F.KeepConnect() - //附加功能 保持牌子点亮 - go reply.Keep_medal_light() - //附加功能 自动发送即将过期礼物 - go reply.AutoSend_silver_gift() - //获取热门榜 - F.Get(common).Get(`Note`) - - danmulog.L(`I: `, "连接到房间", common.Roomid) - - Cookie := make(map[string]string) - common.Cookie.Range(func(k, v any) bool { - Cookie[k.(string)] = v.(string) - return true - }) - // 检查与切换粉丝牌,只在cookie存在时启用 - F.Get(common).Get(`CheckSwitch_FansMedal`) - - // 对每个弹幕服务器尝试 - F.Get(common).Get(`WSURL`) - aliveT := time.Now().Add(3 * time.Hour) - for i, exitloop := 0, false; !exitloop && i < len(common.WSURL) && time.Now().Before(aliveT); { - v := common.WSURL[i] - //ws启动 - danmulog.L(`T: `, "连接 "+v) - u, _ := url.Parse(v) - ws_c, err := ws.New_client(&ws.Client{ - Url: v, - TO: 35 * 1000, - Proxy: common.Proxy, - Func_abort_close: func() { danmulog.L(`I: `, `服务器连接中断`) }, - Func_normal_close: func() { danmulog.L(`I: `, `服务器连接关闭`) }, - Header: map[string]string{ - `Cookie`: reqf.Map_2_Cookies_String(Cookie), - `Host`: u.Hostname(), - `User-Agent`: c.UA, - `Accept`: `*/*`, - `Accept-Language`: `zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2`, - `Origin`: `https://live.bilibili.com`, - `Pragma`: `no-cache`, - `Cache-Control`: `no-cache`, - }, - }) - if err != nil { - danmulog.L(`E: `, "连接错误", err) - i += 1 - continue - } - wsmsg, err := ws_c.Handle() - if err != nil { - danmulog.L(`E: `, "连接错误", err) - i += 1 - continue - } - if ws_c.Isclose() { - danmulog.L(`E: `, "连接错误", ws_c.Error()) - i += 1 - continue - } + // 获取房间实际id + c.C.Roomid = F.GetRoomRealId(c.C.Roomid) - // auth - { - wsmsg.PushLock_tag(`send`, &ws.WsMsg{ - Msg: F.HelloGen(common.Roomid, common.Token), - }) - waitCheckAuth, cancel := context.WithTimeout(mainCtx, 5*time.Second) - doneAuth := wsmsg.Pull_tag_only(`rec`, func(wm *ws.WsMsg) (disable bool) { - if F.HelloChe(wm.Msg) { - cancel() - } - return true - }) - <-waitCheckAuth.Done() - doneAuth() - if err := waitCheckAuth.Err(); errors.Is(err, context.DeadlineExceeded) { - danmulog.L(`E: `, "连接验证失败") - i += 1 - continue - } - } + common, loaded := c.CommonsLoadOrStore.LoadOrStoreP(c.Commons, c.C.Roomid) - danmulog.L(`I: `, "已连接到房间", common.Uname, `(`, common.Roomid, `)`) - reply.Gui_show(`进入直播间: `+common.Uname+` (`+strconv.Itoa(common.Roomid)+`)`, `0room`) - if common.Title != `` { - danmulog.L(`I: `, `房间标题: `+common.Title) - reply.Gui_show(`房间标题: `+common.Title, `0room`) - } + if loaded { + common.InIdle = false + common.Rev = 0.0 // 营收 + } + + exitSign = entryRoom(mainCtx, danmulog.BaseAdd(common.Roomid), common) + + common.InIdle = true + + time.Sleep(time.Second) + } + + { //附加功能 直播流停止 ws信息保存 + reply.SaveToJson.Close() + reply.StreamOStopAll() + } + } +} + +func entryRoom(mainCtx context.Context, danmulog *part.Log_interface, common *c.Common) (exitSign bool) { + //附加功能 保持牌子点亮 + go reply.Keep_medal_light(common) + //附加功能 自动发送即将过期礼物 + go reply.AutoSend_silver_gift(common) + //获取热门榜 + F.Get(common).Get(`Note`) + + danmulog.L(`I: `, "连接到房间", common.Roomid) + + Cookie := make(map[string]string) + common.Cookie.Range(func(k, v any) bool { + Cookie[k.(string)] = v.(string) + return true + }) - // 直播状态 - if F.Get(common).Get(`Liveing`); common.Liveing { - danmulog.L(`I: `, "直播中") - } else { - danmulog.L(`I: `, "未直播") + // 检查与切换粉丝牌,只在cookie存在时启用 + F.Get(common).Get(`CheckSwitch_FansMedal`) + + // 对每个弹幕服务器尝试 + F.Get(common).Get(`WSURL`) + aliveT := time.Now().Add(3 * time.Hour) + for i, exitloop := 0, false; !exitloop && i < len(common.WSURL) && time.Now().Before(aliveT); { + v := common.WSURL[i] + //ws启动 + danmulog.L(`T: `, "连接 "+v) + u, _ := url.Parse(v) + ws_c, err := ws.New_client(&ws.Client{ + Url: v, + TO: 35 * 1000, + Proxy: common.Proxy, + Func_abort_close: func() { danmulog.L(`I: `, `服务器连接中断`) }, + Func_normal_close: func() { danmulog.L(`I: `, `服务器连接关闭`) }, + Header: map[string]string{ + `Cookie`: reqf.Map_2_Cookies_String(Cookie), + `Host`: u.Hostname(), + `User-Agent`: c.UA, + `Accept`: `*/*`, + `Accept-Language`: `zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2`, + `Origin`: `https://live.bilibili.com`, + `Pragma`: `no-cache`, + `Cache-Control`: `no-cache`, + }, + }) + if err != nil { + danmulog.L(`E: `, "连接错误", err) + i += 1 + continue + } + wsmsg, err := ws_c.Handle() + if err != nil { + danmulog.L(`E: `, "连接错误", err) + i += 1 + continue + } + if ws_c.Isclose() { + danmulog.L(`E: `, "连接错误", ws_c.Error()) + i += 1 + continue + } + + // auth + { + wsmsg.PushLock_tag(`send`, &ws.WsMsg{ + Msg: F.HelloGen(common.Roomid, common.Token), + }) + waitCheckAuth, cancel := context.WithTimeout(mainCtx, 5*time.Second) + doneAuth := wsmsg.Pull_tag_only(`rec`, func(wm *ws.WsMsg) (disable bool) { + if F.HelloChe(wm.Msg) { + cancel() } + return true + }) + <-waitCheckAuth.Done() + doneAuth() + if err := waitCheckAuth.Err(); errors.Is(err, context.DeadlineExceeded) { + danmulog.L(`E: `, "连接验证失败") + i += 1 + continue + } + } - // 处理ws消息 - var cancelDeal = wsmsg.Pull_tag(map[string]func(*ws.WsMsg) (disable bool){ - `rec`: func(wm *ws.WsMsg) (disable bool) { - go reply.Reply(common, wm.Msg) - return false - }, - `close`: func(_ *ws.WsMsg) (disable bool) { - return true - }, + danmulog.L(`I: `, "已连接到房间", common.Uname, `(`, common.Roomid, `)`) + reply.Gui_show(`进入直播间: `+common.Uname+` (`+strconv.Itoa(common.Roomid)+`)`, `0room`) + if common.Title != `` { + danmulog.L(`I: `, `房间标题: `+common.Title) + reply.Gui_show(`房间标题: `+common.Title, `0room`) + } + + // 直播状态 + if F.Get(common).Get(`Liveing`); common.Liveing { + danmulog.L(`I: `, "直播中") + } else { + danmulog.L(`I: `, "未直播") + } + + // 处理ws消息 + var cancelDeal = wsmsg.Pull_tag(map[string]func(*ws.WsMsg) (disable bool){ + `rec`: func(wm *ws.WsMsg) (disable bool) { + go reply.Reply(common, wm.Msg) + return false + }, + `close`: func(_ *ws.WsMsg) (disable bool) { + return true + }, + }) + + //30s获取一次人气 + go func() { + danmulog.L(`T: `, "获取人气") + heartbeatmsg, heartinterval := F.Heartbeat() + for !ws_c.Isclose() { + wsmsg.Push_tag(`send`, &ws.WsMsg{ + Msg: heartbeatmsg, }) + time.Sleep(time.Millisecond * time.Duration(heartinterval*1000)) + } + }() + + // 刷新舰长数 + F.Get(common).Get(`GuardNum`) + // 在线人数 + F.Get(common).Get(`getOnlineGoldRank`) + //验证cookie + if missKey := F.CookieCheck([]string{ + `bili_jct`, + `DedeUserID`, + `LIVE_BUVID`, + }); len(missKey) == 0 { + //附加功能 弹幕机 无cookie无法发送弹幕 + reply.Danmuji_auto() + } + { //附加功能 进房间发送弹幕 直播流保存 每日签到 + go F.Dosign() + go reply.Entry_danmu(common) + if _, e := recStartEnd.RecStartCheck.Run(mainCtx, common); e == nil { + go reply.StreamOStart(common, common.Roomid) + } else { + danmulog.Base("功能", "指定房间录制区间").L(`I: `, common.Roomid, e) + } + go F.RoomEntryAction(common.Roomid) + } - //30s获取一次人气 - go func() { - danmulog.L(`T: `, "获取人气") - heartbeatmsg, heartinterval := F.Heartbeat() - for !ws_c.Isclose() { - wsmsg.Push_tag(`send`, &ws.WsMsg{ - Msg: heartbeatmsg, - }) - time.Sleep(time.Millisecond * time.Duration(heartinterval*1000)) + //当前ws + { + // 处理各种指令 + var cancelfunc = common.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ + `interrupt`: func(_ any) (disable bool) { + exitloop = true + exitSign = true + ws_c.Close() + danmulog.L(`I: `, "停止,等待服务器断开连接") + reply.StreamOStopAll() //停止录制 + return true + }, + `exit_room`: func(_ any) bool { //退出当前房间 + exitloop = true + reply.StreamOStop(common.Roomid) + danmulog.L(`I: `, "退出房间", common.Roomid) + c.C.Roomid = 0 + ws_c.Close() + return true + }, + `change_room`: func(roomid any) bool { //换房时退出当前房间 + c.C.Roomid = roomid.(int) + exitloop = true + ws_c.Close() + if v, ok := common.K_v.LoadV(`仅保存当前直播间流`).(bool); ok && v { + reply.StreamOStopOther(c.C.Roomid) //停止其他房间录制 } - }() - - // 刷新舰长数 - F.Get(common).Get(`GuardNum`) - // 在线人数 - F.Get(common).Get(`getOnlineGoldRank`) - //验证cookie - if missKey := F.CookieCheck([]string{ - `bili_jct`, - `DedeUserID`, - `LIVE_BUVID`, - }); len(missKey) == 0 { - //附加功能 弹幕机 无cookie无法发送弹幕 - reply.Danmuji_auto() - } - { //附加功能 进房间发送弹幕 直播流保存 每日签到 - go F.Dosign() - go reply.Entry_danmu() - if _, e := recStartEnd.RecStartCheck.Run(mainCtx, common); e == nil { - go reply.StreamOStart(common, common.Roomid) - } else { - danmulog.Base("功能", "指定房间录制区间").L(`I: `, common.Roomid, e) + return true + }, + `flash_room`: func(_ any) bool { //重进房时退出当前房间 + go F.Get(common).Get(`WSURL`) + ws_c.Close() + return true + }, + `guard_update`: func(_ any) bool { //舰长更新 + go F.Get(common).Get(`GuardNum`) + return false + }, + `every100s`: func(_ any) bool { //每100s + if time.Now().After(aliveT) { + common.Danmu_Main_mq.Push_tag(`flash_room`, nil) + return false } - go F.RoomEntryAction(common.Roomid) - } - - //当前ws - { - // 处理各种指令 - var cancelfunc = common.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ - `interrupt`: func(_ any) (disable bool) { - exitloop = true - exitSign = true - ws_c.Close() - danmulog.L(`I: `, "停止,等待服务器断开连接") - reply.StreamOStopAll() //停止录制 - return true - }, - `exit_room`: func(_ any) bool { //退出当前房间 - exitloop = true - reply.StreamOStop(common.Roomid) - danmulog.L(`I: `, "退出房间", common.Roomid) - common.Roomid = 0 - ws_c.Close() - return true - }, - `change_room`: func(roomid any) bool { //换房时退出当前房间 - common.Roomid = roomid.(int) - exitloop = true - ws_c.Close() - if v, ok := common.K_v.LoadV(`仅保存当前直播间流`).(bool); ok && v { - reply.StreamOStopOther(common.Roomid) //停止其他房间录制 - } - return true - }, - `flash_room`: func(_ any) bool { //重进房时退出当前房间 - go F.Get(common).Get(`WSURL`) - ws_c.Close() - return true - }, - `guard_update`: func(_ any) bool { //舰长更新 - go F.Get(common).Get(`GuardNum`) - return false - }, - `every100s`: func(_ any) bool { //每100s - if time.Now().After(aliveT) { - common.Danmu_Main_mq.Push_tag(`flash_room`, nil) - return false - } - if v, ok := common.K_v.LoadV("下播后不记录人气观看人数").(bool); ok && v && !common.Liveing { - return false - } - // 在线人数 - go F.Get(common).Get(`getOnlineGoldRank`) - return false - }, - `new day`: func(_ any) bool { //日期更换 - go func() { - //每日签到 - F.Dosign() - //每日兑换硬币 - F.Get(common).Silver_2_coin() - //附加功能 每日发送弹幕 - reply.Entry_danmu() - //附加功能 保持牌子点亮 - reply.Keep_medal_light() - //附加功能 自动发送即将过期礼物 - reply.AutoSend_silver_gift() - }() - return false - }, - }) - - { - cancel, c := wsmsg.Pull_tag_chan(`exit`, 1, mainCtx) - <-c - cancel() + if v, ok := common.K_v.LoadV("下播后不记录人气观看人数").(bool); ok && v && !common.Liveing { + return false } + // 在线人数 + go F.Get(common).Get(`getOnlineGoldRank`) + return false + }, + `new day`: func(_ any) bool { //日期更换 + go func() { + //每日签到 + F.Dosign() + //每日兑换硬币 + F.Get(common).Silver_2_coin() + //附加功能 每日发送弹幕 + reply.Entry_danmu(common) + //附加功能 保持牌子点亮 + reply.Keep_medal_light(common) + //附加功能 自动发送即将过期礼物 + reply.AutoSend_silver_gift(common) + }() + return false + }, + }) - cancelfunc() - time.Sleep(time.Second) - } - cancelDeal() + { + cancel, c := wsmsg.Pull_tag_chan(`exit`, 1, mainCtx) + <-c + cancel() } - time.Sleep(time.Second) - } - { //附加功能 直播流停止 ws信息保存 - reply.SaveToJson.Close() - reply.StreamOStopAll() + cancelfunc() + time.Sleep(time.Second) } + cancelDeal() } + + return } diff --git a/cmd/cmd.go b/cmd/cmd.go index 7c2e557..e70b57b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -76,7 +76,8 @@ func Cmd() { if reply.StreamOStatus(room) { reply.StreamOStop(room) } else { - reply.StreamOStart(c.C.Copy(), room) + common, _ := c.CommonsLoadOrStore.LoadOrStoreP(c.Commons, room) + reply.StreamOStart(common, room) } continue } @@ -87,7 +88,8 @@ func Cmd() { if reply.StreamOStatus(c.C.Roomid) { reply.StreamOStop(c.C.Roomid) } else { - reply.StreamOStart(c.C.Copy(), c.C.Roomid) + common, _ := c.Commons.LoadV(c.C.Roomid).(*c.Common) + reply.StreamOStart(common, c.C.Roomid) } } continue @@ -184,22 +186,23 @@ func Cmd() { if strings.Contains(inputs, ` room`) && c.C.Roomid != 0 { fmt.Print("\n") fmt.Println("当前直播间(" + strconv.Itoa(c.C.Roomid) + ")信息") + common, _ := c.Commons.LoadV(c.C.Roomid).(*c.Common) { living := `未在直播` - if c.C.Liveing { + if common.Liveing { living = `直播中` } - fmt.Println(c.C.Uname, c.C.Title, living) + fmt.Println(common.Uname, common.Title, living) } - if c.C.Liveing { - fmt.Println(`已直播时长:`, (time.Time{}).Add(time.Since(c.C.Live_Start_Time)).Format(time.TimeOnly)) + if common.Liveing { + fmt.Println(`已直播时长:`, (time.Time{}).Add(time.Since(common.Live_Start_Time)).Format(time.TimeOnly)) } { - fmt.Println(`营收:`, fmt.Sprintf("ï¿¥%.2f", c.C.Rev)) + fmt.Println(`营收:`, fmt.Sprintf("ï¿¥%.2f", common.Rev)) } - fmt.Println(`舰长数:`, c.C.GuardNum) - fmt.Println(`分区排行:`, c.C.Note, `人气:`, c.C.Renqi, `观看人数:`, c.C.Watched, `在线人数:`, c.C.OnlineNum) - fmt.Println(`Web服务地址:`, c.C.Stream_url.String()) + fmt.Println(`舰长数:`, common.GuardNum) + fmt.Println(`分区排行:`, common.Note, `观看人数:`, common.Watched, `在线人数:`, common.OnlineNum) + fmt.Println(`Web服务地址:`, common.Stream_url.String()) var array = reply.StreamOCommon(-1) fmt.Println(`正在录制的房间:`) for i := 0; i < len(array); i++ { diff --git a/go.mod b/go.mod index 4bf7951..4e4ce88 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.2 require ( github.com/gotk3/gotk3 v0.6.4 github.com/mdp/qrterminal/v3 v3.2.0 - github.com/qydysky/part v0.28.20240617114802 + github.com/qydysky/part v0.28.20240708182044 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.16.0 diff --git a/go.sum b/go.sum index 6b40423..b4466fa 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/qydysky/biliApi v0.0.0-20240707084224-dd146610f462 h1:SblV/m+0mx6QgH2YYvdjE5QxhyfE/EOQglJ1PGd3x3E= github.com/qydysky/biliApi v0.0.0-20240707084224-dd146610f462/go.mod h1:om024vfxALQ5vxsbaGoMm8IS0esLYBnEOpJI8FsGoDg= -github.com/qydysky/part v0.28.20240617114802 h1:cmDWin7303nKFQ7HAL2+Ur87vkh8d67xVqudvpXp96E= -github.com/qydysky/part v0.28.20240617114802/go.mod h1:dgagZnPYRFZDbt7XJf7nADOJLoYwlebD9B8Z8g5aHhI= +github.com/qydysky/part v0.28.20240708182044 h1:Av1SaUu/cvwGZ3xnsq9KabFjjn2Wwl+xjbAJbq6a2Iw= +github.com/qydysky/part v0.28.20240708182044/go.mod h1:dgagZnPYRFZDbt7XJf7nADOJLoYwlebD9B8Z8g5aHhI= 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=