}
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:"-"` //配置文件
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:"-"`
func (t *Common) Copy() *Common {
var c = Common{
+ InIdle: t.InIdle,
PID: t.PID,
Version: t.Version,
Uid: t.Uid,
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
})
// 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 {
}
}
+// 房间实际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`)
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"
}
// 实例切断
-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 {
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
}
}
+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)
}
}
}
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{ //传入消息队列
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,不输出
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))
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 {
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...)
}
func (t *M4SStream) MarshalJSON() ([]byte, error) {
- t.common.Rev = c.C.Rev
- t.common.Watched = c.C.Watched
return json.MarshalIndent(t.common, "", " ")
}
// 每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=&` +
`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,
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)
}
}
}
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"
// 指定弹幕重启录制
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 {
//使用带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
},
}
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 {
}
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
}
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
}
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
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++ {
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
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=