From: qydysky Date: Sat, 26 Sep 2020 09:56:48 +0000 (+0800) Subject: 25 X-Git-Tag: v0.0.0~13 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=081f395f5ea4f499fe5875cbe012a78b760a0d8b;p=bili_danmu%2F.git 25 --- diff --git a/README.md b/README.md index 02e94e5..0c9447f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,17 @@ golang go version go1.15.2 linux/amd64 显示 case 后有函数调用的为支持,为nil的为待完善,注释掉的调用为未启用 -var Msg_map = map[string]func(replayF, string) { +//Msg类型数据处理方法map +var Msg_map = map[string]func(replyF, string) { + "PK_BATTLE_PRE":nil,//人气pk + "PK_BATTLE_START":nil,//人气pk + "PK_BATTLE_PROCESS":nil,//人气pk + "PK_BATTLE_END":nil,//人气pk + "PK_BATTLE_RANK_CHANGE":nil,//人气pk + "PK_BATTLE_SETTLE_USER":nil,//人气pk + "PK_BATTLE_SETTLE_V2":nil,//人气pk + "PK_BATTLE_SETTLE":nil,//人气pk + "SYS_MSG":nil,//系统消息 "ROOM_SKIN_MSG":nil, "GUARD_ACHIEVEMENT_ROOM":nil, "ANCHOR_LOT_START":nil,//天选之人开始 @@ -23,23 +33,24 @@ var Msg_map = map[string]func(replayF, string) { "HOUR_RANK_AWARDS":nil, "ROOM_RANK":nil, "ROOM_SHIELD":nil, - "USER_TOAST_MSG":nil, - "WIN_ACTIVITY":nil, - "GUARD_BUY":replayF.guard_buy,//大航海购买 - "WELCOME_GUARD":replayF.welcome_guard,//大航海进入 - "DANMU_MSG":replayF.danmu,//弹幕 - "ROOM_CHANGE":replayF.room_change,//房间信息分区改变 - "ROOM_SILENT_OFF":replayF.roomsilent,//禁言结束 - "ROOM_SILENT_ON":replayF.roomsilent,//禁言开始 - "SEND_GIFT":replayF.send_gift,//礼物 - "ROOM_BLOCK_MSG":replayF.room_block_msg,//封禁 - "PREPARING":replayF.preparing,//下播 - "LIVE":replayF.live,//开播 - "SUPER_CHAT_MESSAGE":nil,//replayF.super_chat_message,//打赏 - "SUPER_CHAT_MESSAGE_JPN":replayF.super_chat_message,//打赏 - "PANEL":replayF.panel,//排行榜 - "ENTRY_EFFECT":nil,//replayF.entry_effect,//进入特效 - "ROOM_REAL_TIME_MESSAGE_UPDATE":nil,//replayF.roominfo,//粉丝数 + "USER_TOAST_MSG":replyF.user_toast_msg,//大航海购买信息 + "WIN_ACTIVITY":replyF.win_activity,//活动 + "SPECIAL_GIFT":replyF.special_gift,//节奏风暴 + "GUARD_BUY":nil,//replyF.guard_buy,//大航海购买 + "WELCOME_GUARD":replyF.welcome_guard,//大航海进入 + "DANMU_MSG":replyF.danmu,//弹幕 + "ROOM_CHANGE":replyF.room_change,//房间信息分区改变 + "ROOM_SILENT_OFF":replyF.roomsilent,//禁言结束 + "ROOM_SILENT_ON":replyF.roomsilent,//禁言开始 + "SEND_GIFT":replyF.send_gift,//礼物 + "ROOM_BLOCK_MSG":replyF.room_block_msg,//封禁 + "PREPARING":replyF.preparing,//下播 + "LIVE":replyF.live,//开播 + "SUPER_CHAT_MESSAGE":nil,//replyF.super_chat_message,//SC + "SUPER_CHAT_MESSAGE_JPN":replyF.super_chat_message,//SC + "PANEL":replyF.panel,//排行榜 + "ENTRY_EFFECT":nil,//replyF.entry_effect,//进入特效 + "ROOM_REAL_TIME_MESSAGE_UPDATE":nil,//replyF.roominfo,//粉丝数 } ``` 以下内容可能过时,点击查看[当前支持功能](https://github.com/qydysky/bili_danmu/blob/master/Reply/F.go#L16) @@ -59,6 +70,7 @@ var AllF = map[string]bool{ obs https://obsproject.com/download obs-websocket https://github.com/Palakis/obs-websocket/releases */ + "Ass":true,//Ass弹幕生成,由于时间对应关系,仅开启流保存时生效 "Autoban":true,//自动封禁(仅提示,未完成) "Jiezou":true,//带节奏预警,提示弹幕礼仪 "Danmuji":true,//反射型弹幕机,回应弹幕 @@ -79,6 +91,7 @@ go run main.go go run main.go -r=此处填房间ID ``` 以下内容可能过时,以实际运行为准 +- 命令窗口(以下为截取) ``` $ go run main.go 输入房间号: 213 @@ -86,18 +99,49 @@ INFO: 2020/09/16 16:48:11 [bili_danmu.go 测试] [连接到房间 213] INFO: 2020/09/16 16:48:11 [bili_danmu.go 测试] [连接 wss://tx-sh-live-comet-01.chat.bilibili.com/sub] INFO: 2020/09/16 16:48:11 [bili_danmu.go 测试] [已连接到房间 213] INFO: 2020/09/16 16:48:11 [bili_danmu.go 测试] [开始心跳] +``` +``` +//大航海进入 >>> 欢迎 舰长 茶摊儿在森林喝碗山海 进入直播间 +``` +``` +//普通弹幕 老鸡捉小鹰 你快扒拉他 你这好像是补刀 吓人 +``` +``` +//礼物 ==== 孤单猫与淋雨猪 投喂 1314 x 辣条 ( 131400 x 金瓜子 ) ==== +``` +``` +//同字符串合并 7 x 原神公测B服冲冲冲 +``` +``` +//同字符忽略 原神公测B站冲冲冲 ...B服冲冲冲 - -ctrl+c退出,日志会同时追加记录到文件danmu.log中(文件记录完整信息,不会减少附加功能作用的弹幕) ``` +``` +//SC +==== +SC: 吹舞火 ¥ 30 +我旁边的一万是幻觉吗? +私の隣の一万は幻ですか? +==== +``` +ctrl+c退出,会同时追加记录到文件danmu.log中(文件记录完整信息,不会减少附加功能作用的弹幕) +- 流保存以及弹幕ass +``` +结束后会保存为 +房间号_时间.mkv +房间号_时间.ass +``` +结束后的文件播放效果(显于左上) +![](_Screenshot/Screenshot_20200926_173834.png) + 更多内容详见注释,如有疑问请发issues,欢迎pr diff --git a/Reply/F.go b/Reply/F.go index cf8a989..c58e5c5 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -16,6 +16,10 @@ import ( p "github.com/qydysky/part" ) +/* + F额外功能区 +*/ + //功能开关 var AllF = map[string]bool{ "Saveflv":true,//保存直播流(仅高清) @@ -29,7 +33,7 @@ var AllF = map[string]bool{ obs https://obsproject.com/download obs-websocket https://github.com/Palakis/obs-websocket/releases */ - "Ass":true,//Ass弹幕生成 + "Ass":true,//Ass弹幕生成,由于时间对应关系,仅开启流保存时生效 "Autoban":true,//自动封禁(仅提示,未完成) "Jiezou":true,//带节奏预警,提示弹幕礼仪 "Danmuji":true,//反射型弹幕机,回应弹幕 @@ -40,13 +44,16 @@ var AllF = map[string]bool{ "Shortdanmu":true,//上下文相同文字缩减 } +//功能开关选取函数 func IsOn(s string) bool { if v, ok := AllF[s]; ok && v { return true } return false } -//公共 + +//字符重复度检查 +//a在buf中出现的字符占a的百分数 func cross(a string,buf []string) (float32) { var s float32 var matched bool @@ -62,6 +69,8 @@ func cross(a string,buf []string) (float32) { } return s / float32(len([]rune(a))) } + +//在a中仅出现一次出现的字符占a的百分数 func selfcross(a string) (float32) { buf := make(map[rune]bool) for _,v := range a { @@ -71,6 +80,11 @@ func selfcross(a string) (float32) { } return 1 - float32(len(buf)) / float32(len([]rune(a))) } + +//在a的每个字符串中 +//出现的字符次数最多的 +//占出现的字符总数的百分数 +//*单字符串中的重复出现计为1次 func selfcross2(a []string) (float32, string) { buf := make(map[rune]float32) for _,v := range a { @@ -92,23 +106,24 @@ func selfcross2(a []string) (float32, string) { } return max / all, maxS } + //功能区 + +//Ass 弹幕转字幕 type Ass struct { Inuse bool - file string - startT time.Time - header string - rtb [7]time.Duration//通道是否被字节占用 - ri int + file string//弹幕ass文件名 + startT time.Time//开始记录的基准时间 + header string//ass开头 } var ( - Ass_height = 720 - Ass_width = 1280 - Ass_font = 50 - Ass_T = 7 - Ass_loc = 7//小键盘对应的位置 + Ass_height = 720//字幕高度 + Ass_width = 1280//字幕宽度 + Ass_font = 50//字幕字体大小 + Ass_T = 7//单条字幕显示时间 + Ass_loc = 7//字幕位置 小键盘对应的位置 ) var ass = Ass { @@ -129,10 +144,13 @@ Style: Default,,`+strconv.Itoa(Ass_font)+`,&H40FFFFFF,&H000017FF,&H80000000,&H70 Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text `, } + +//设定字幕文件名,为""时停止输出 func Ass_f(file string){ ass.file = file } +//传入要显示的单条字幕 func Assf(s string){ if !ass.Inuse {return} if ass.file == "" {return} @@ -157,7 +175,7 @@ func Assf(s string){ var b string // b += "Comment: " + strconv.Itoa(loc) + " "+ Dtos(showedt) + "\n" b += `Dialogue: 0,` - b += Dtos(st) + `,` + Dtos(et) + b += dtos(st) + `,` + dtos(et) b += `,Default,,0,0,0,,{\fad(200,500)\blur3}` + s + "\n" p.File().FileWR(p.Filel{ @@ -168,7 +186,8 @@ func Assf(s string){ }) } -func Dtos(t time.Duration) string { +//时间转化为0:00:00.00规格字符串 +func dtos(t time.Duration) string { M := int(math.Floor(t.Minutes())) % 60 S := int(math.Floor(t.Seconds())) % 60 Ns := t.Nanoseconds() / int64(time.Millisecond) % 1000 / 10 @@ -176,6 +195,7 @@ func Dtos(t time.Duration) string { return fmt.Sprintf("%d:%02d:%02d.%02d", int(math.Floor(t.Hours())), M, S, Ns) } +//直播流保存 type Saveflv struct { Inuse bool path string @@ -187,6 +207,7 @@ var saveflv = Saveflv { Inuse:IsOn("Saveflv"), } +//已go func形式调用,将会获取直播流 func Saveflvf(){ if !saveflv.Inuse {return} l := p.Logf().New().Open("danmu.log").Base(-1, "saveflv") @@ -239,6 +260,7 @@ func Saveflvf(){ } } +//已func形式调用,将会停止保存直播流 func Saveflv_wait(){ if !saveflv.Inuse {return} saveflv.cancel.Done() diff --git a/Reply/Heartbeat.go b/Reply/Heartbeat.go index 6adf67a..f28019a 100644 --- a/Reply/Heartbeat.go +++ b/Reply/Heartbeat.go @@ -7,11 +7,18 @@ import ( F "github.com/qydysky/bili_danmu/F" ) +/* + HeartBeat数据分派 +*/ + var heartlog = p.Logf().New().Base(-1, "Heart.go").Open("danmu.log").Fileonly(true) + +//HeartBeat类型处理方法map var Heart_map = map[string]func(replyF, string) { "heartbeat":replyF.heartbeat,//人气 } +//HeartBeat类型,将人气4位byte转为字符串,并送到上述map指定的方法 func Heart(b []byte){ s := strconv.Itoa(int(F.Btoi32(b, 0))) if F,ok := Heart_map["heartbeat"]; ok { diff --git a/Reply/Msg.go b/Reply/Msg.go index 87f94e4..bf4d56c 100644 --- a/Reply/Msg.go +++ b/Reply/Msg.go @@ -3,11 +3,14 @@ package reply import ( p "github.com/qydysky/part" ) + /* - 数据为WS_OP_MESSAGE类型的 + 数据为WS_OP_MESSAGE类型的数据分派 */ var msglog = p.Logf().New().Base(-1, "Msg.go").Open("danmu.log").Level(1) + +//Msg类型数据处理方法map var Msg_map = map[string]func(replyF, string) { "PK_BATTLE_PRE":nil,//人气pk "PK_BATTLE_START":nil,//人气pk @@ -54,6 +57,8 @@ var Msg_map = map[string]func(replyF, string) { "ROOM_REAL_TIME_MESSAGE_UPDATE":nil,//replyF.roominfo,//粉丝数 } +//Msg类型数据处理方法挑选 +//识别cmd字段类型,查找上述map中设置的方法,并将json转为字符串型传入 func Msg(b []byte) { s := string(b) if cmd := p.Json().GetValFromS(s, "cmd");cmd == nil { diff --git a/Reply/Reply.go b/Reply/Reply.go index 6a1ee58..95ae73c 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -14,6 +14,8 @@ import ( var replylog = p.Logf().New().Open("danmu.log").Base(-1, "Reply.go") //返回数据分派 +//传入接受到的ws数据 +//判断进行解压,并对每个json对象进行分派 func Reply(b []byte) { replylog.Base(-1, "返回分派") defer replylog.Base(0) @@ -46,12 +48,16 @@ func Reply(b []byte) { } } +//所有的json对象处理子函数类 +//包含Msg和HeartBeat两大类 type replyF struct {} +//默认未识别Msg func (replyF) defaultMsg(s string){ msglog.Base(1, "Unknow").E(s) } +//msg-通常是大航海购买续费 func (replyF) user_toast_msg(s string){ username := p.Json().GetValFromS(s, "data.username"); op_type := p.Json().GetValFromS(s, "data.op_type"); @@ -97,13 +103,16 @@ func (replyF) user_toast_msg(s string){ msglog.Fileonly(true) defer msglog.Fileonly(false) - msglog.Base(1, "礼").I(sh...)} + msglog.Base(1, "礼").I(sh...) +} +//HeartBeat-心跳用来传递人气值 func (replyF) heartbeat(s string){ if s == "1" {return}//人气为1,不输出 heartlog.I("当前人气", s) } +//Msg-房间特殊活动 func (replyF) win_activity(s string){ msglog.Fileonly(true) defer msglog.Fileonly(false) @@ -114,6 +123,7 @@ func (replyF) win_activity(s string){ msglog.Base(1, "房").I("活动", title, "已开启") } +//Msg-特殊礼物,当前仅观察到节奏风暴 func (replyF) special_gift(s string){ msglog.Fileonly(true) defer msglog.Fileonly(false) @@ -139,6 +149,7 @@ func (replyF) special_gift(s string){ } +//Msg-大航海购买,由于信息少,用user_toast_msg进行替代 func (replyF) guard_buy(s string){ username := p.Json().GetValFromS(s, "data.username"); gift_name := p.Json().GetValFromS(s, "data.gift_name"); @@ -167,6 +178,7 @@ func (replyF) guard_buy(s string){ msglog.Base(1, "礼").I(sh...) } +//Msg-房间信息改变,标题等 func (replyF) room_change(s string){ title := p.Json().GetValFromS(s, "data.title"); area_name := p.Json().GetValFromS(s, "data.area_name"); @@ -182,6 +194,7 @@ func (replyF) room_change(s string){ msglog.Base(1, "房").I(sh...) } +//Msg-大航海欢迎信息 func (replyF) welcome_guard(s string){ username := p.Json().GetValFromS(s, "data.username"); @@ -207,6 +220,7 @@ func (replyF) welcome_guard(s string){ msglog.Base(1, "房").Fileonly(true).I(sh...).Fileonly(false) } +//Msg-礼物处理,对于小于30人民币的礼物不显示 func (replyF) send_gift(s string){ // coin_type := p.Json().GetValFromS(s, "data.coin_type"); num := p.Json().GetValFromS(s, "data.num"); @@ -251,6 +265,7 @@ func (replyF) send_gift(s string){ msglog.Base(1, "礼").I(sh...) } +//Msg-房间封禁信息 func (replyF) room_block_msg(s string) { msglog.Fileonly(true) defer msglog.Fileonly(false) @@ -264,6 +279,7 @@ func (replyF) room_block_msg(s string) { } } +//Msg-房间准备信息,通常出现在下播而不出现在开播 func (replyF) preparing(s string) { msglog.Base(1, "房") @@ -283,6 +299,7 @@ func (replyF) preparing(s string) { } } +//Msg-房间开播信息 func (replyF) live(s string) { msglog.Base(1, "房") @@ -303,6 +320,7 @@ func (replyF) live(s string) { } } +//Msg-超级留言处理 func (replyF) super_chat_message(s string){ uname := p.Json().GetValFromS(s, "data.user_info.uname"); price := p.Json().GetValFromS(s, "data.price"); @@ -317,23 +335,25 @@ func (replyF) super_chat_message(s string){ if price != nil { sh = append(sh, "¥", price) } + fmt.Println("\n====") + fmt.Println(sh...) if message != nil { + fmt.Println(message) sh = append(sh, message) } if message_jpn != nil && message != message_jpn { + fmt.Println(message_jpn) sh = append(sh, message_jpn) } - msglog.Fileonly(true) - defer msglog.Fileonly(false) + fmt.Print("====\n\n") + {//额外 Assf(fmt.Sprintln(sh...)) } - fmt.Println("\n====") - fmt.Println(sh...) - fmt.Print("====\n\n") - msglog.Base(1, "礼").I(sh...) + msglog.Base(1, "礼").Fileonly(true).I(sh...).Fileonly(false) } +//Msg-分区排行 func (replyF) panel(s string){ msglog.Fileonly(true).Base(1, "房") defer msglog.Fileonly(false) @@ -347,6 +367,7 @@ func (replyF) panel(s string){ } } +//Msg-进入特效,大多为大航海进入,信息少,使用welcome_guard替代 func (replyF) entry_effect(s string){ msglog.Fileonly(true).Base(-1, "房") defer msglog.Base(0).Fileonly(false) @@ -361,6 +382,7 @@ func (replyF) entry_effect(s string){ } +//Msg-房间禁言 func (replyF) roomsilent(s string){ msglog.Base(1, "房") @@ -373,6 +395,7 @@ func (replyF) roomsilent(s string){ } } +//Msg-粉丝信息,常刷屏,不显示 func (replyF) roominfo(s string){ fans := p.Json().GetValFromS(s, "data.fans"); fans_club := p.Json().GetValFromS(s, "data.fans_club"); @@ -389,6 +412,7 @@ func (replyF) roominfo(s string){ if len(sh) != 0 {msglog.Base(1, "粉").I(sh...)} } +//Msg-弹幕处理 func (replyF) danmu(s string) { if info := p.Json().GetValFromS(s, "info");info == nil { msglog.E("info", info) @@ -417,11 +441,16 @@ func (replyF) danmu(s string) { } } +//弹幕发送 +//传入字符串即可发送 +//需要cookie func Msg_senddanmu(msg string){ if c.Cookie == "" || c.Roomid == 0 {return} S.Danmu_s(msg, c.Cookie, c.Roomid) } +//弹幕显示 +//由于额外功能有些需要显示,为了统一管理,使用此方法进行处理 func Msg_showdanmu(auth interface{}, msg string) { {//附加功能 更少弹幕 if Lessdanmuf(msg, 20) > 0.7 {//与前20条弹幕重复的字数占比度>0.7的屏蔽 diff --git a/_Screenshot/Screenshot_20200926_173834.png b/_Screenshot/Screenshot_20200926_173834.png new file mode 100644 index 0000000..67f289b Binary files /dev/null and b/_Screenshot/Screenshot_20200926_173834.png differ