From a652dbde1637eac7c16cc482969ef61ea39b21b8 Mon Sep 17 00:00:00 2001 From: qydysky Date: Tue, 5 Apr 2022 14:16:46 +0800 Subject: [PATCH] =?utf8?q?=E7=B2=89=E4=B8=9D=E5=8B=8B=E7=AB=A0fix=20?= =?utf8?q?=E5=BF=83=E8=B7=B3=E5=93=8D=E5=BA=94=E5=8C=85fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- CV/Var.go | 127 +-- F/api.go | 48 +- Json/getMyMedals.go | 38 + Reply/F.go | 1466 ++++++++++++++++++-------------- Reply/Msg.go | 178 ++-- Reply/Reply.go | 563 ++++++------ Reply/gtk.go | 785 ++++++++++------- Reply/tts.go | 366 ++++---- Reply/ws_msg/WATCHED_CHANGE.go | 11 + demo/go.mod | 2 +- demo/go.sum | 2 + go.mod | 2 +- go.sum | 2 + 13 files changed, 2042 insertions(+), 1548 deletions(-) create mode 100644 Json/getMyMedals.go create mode 100644 Reply/ws_msg/WATCHED_CHANGE.go diff --git a/CV/Var.go b/CV/Var.go index 5c0990a..c17aa5c 100644 --- a/CV/Var.go +++ b/CV/Var.go @@ -1,49 +1,51 @@ package cv import ( + "encoding/json" + "io/ioutil" "time" - syncmap "github.com/qydysky/part/map" - mq "github.com/qydysky/part/msgq" - s "github.com/qydysky/part/buf" + log "github.com/qydysky/part/log" + mq "github.com/qydysky/part/msgq" + syncmap "github.com/qydysky/part/sync" ) - var ( - Uid = 0//client uid - - Live []string//直播流链接 - Live_qn int//当前直播流质量 - Live_want_qn int//期望直播流质量 - Roomid int - Cookie syncmap.Map - Title string//直播标题 - Uname string//主播名 - UpUid int//主播uid - Rev float64//营收 - Renqi int//人气 - GuardNum int//舰长数 - ParentAreaID int//父分区 - AreaID int//子分区 - Locked bool//直播间封禁 - Note string//分区排行 - Live_Start_Time time.Time//直播开始时间 - Liveing bool//是否在直播 - Wearing_FansMedal int//当前佩戴的粉丝牌 - Token string//弹幕钥 - WSURL []string//弹幕链接 - LIVE_BUVID bool//cookies含LIVE_BUVID + Uid = 0 //client uid + + Live []string //直播流链接 + Live_qn int //当前直播流质量 + Live_want_qn int //期望直播流质量 + Roomid int + Cookie syncmap.Map + Title string //直播标题 + Uname string //主播名 + UpUid int //主播uid + Rev float64 //营收 + Renqi int //人气 + GuardNum int //舰长数 + ParentAreaID int //父分区 + AreaID int //子分区 + Locked bool //直播间封禁 + Note string //分区排行 + Live_Start_Time time.Time //直播开始时间 + Liveing bool //是否在直播 + Wearing_FansMedal int //当前佩戴的粉丝牌 + Token string //弹幕钥 + WSURL []string //弹幕链接 + LIVE_BUVID bool //cookies含LIVE_BUVID ) var ( - Stream_url string//直播Web服务 + Stream_url string //直播Web服务 ) //消息队列 type Danmu_Main_mq_item struct { Class string - Data interface{} + Data interface{} } + //200长度防止push击穿 var Danmu_Main_mq = mq.New(200) @@ -51,38 +53,43 @@ var Danmu_Main_mq = mq.New(200) var K_v syncmap.Map func init() { - buf := s.New() - buf.Load("config/config_K_v.json") - for k,v := range buf.B { + bb, err := ioutil.ReadFile("config/config_K_v.json") + if err != nil { + return + } + var data map[string]interface{} + json.Unmarshal(bb, &data) + for k, v := range data { K_v.Store(k, v) } } //constKv var ( - Proxy string//全局代理 + Proxy string //全局代理 ) + func init() { - Proxy,_ = K_v.LoadV("http代理地址").(string) + Proxy, _ = K_v.LoadV("http代理地址").(string) } //日志 var Log = log.New(log.Config{ - File:`danmu.log`, - Stdout:true, - Prefix_string:map[string]struct{}{ - `T: `:log.On, - `I: `:log.On, - `N: `:log.On, - `W: `:log.On, - `E: `:log.On, + File: `danmu.log`, + Stdout: true, + Prefix_string: map[string]struct{}{ + `T: `: log.On, + `I: `: log.On, + `N: `: log.On, + `W: `: log.On, + `E: `: log.On, }, }) func init() { logmap := make(map[string]struct{}) - if array,ok := K_v.Load(`日志显示`);ok{ - for _,v := range array.([]interface{}){ + if array, ok := K_v.Load(`日志显示`); ok { + for _, v := range array.([]interface{}) { logmap[v.(string)] = log.On } } @@ -102,21 +109,21 @@ func init() { var ( AcceptQn = map[int]string{ - 10000:"原画", - 800:"4K", - 401:"蓝光(杜比)", - 400:"蓝光", - 250:"超清", - 150:"高清", - 80:"流畅", + 10000: "原画", + 800: "4K", + 401: "蓝光(杜比)", + 400: "蓝光", + 250: "超清", + 150: "高清", + 80: "流畅", } - Qn = map[int]string{// no change - 10000:"原画", - 800:"4K", - 401:"蓝光(杜比)", - 400:"蓝光", - 250:"超清", - 150:"高清", - 80:"流畅", + Qn = map[int]string{ // no change + 10000: "原画", + 800: "4K", + 401: "蓝光(杜比)", + 400: "蓝光", + 250: "超清", + 150: "高清", + 80: "流畅", } -) \ No newline at end of file +) diff --git a/F/api.go b/F/api.go index c91a4d2..f2d446b 100644 --- a/F/api.go +++ b/F/api.go @@ -1394,28 +1394,11 @@ func Get_cookie() (missKey []string) { //短信登录 func Get_cookie_by_msg() { - /* - - https://passport.bilibili.com/x/passport-login/web/sms/send - - - */ -} - -//牌子 -type TGet_list_in_room struct { - Medal_id int `json:"medal_id"` //牌子id - Medal_name string `json:"medal_name"` //牌子名 - Target_id int `json:"target_id"` //牌子up主uid - Target_name string `json:"target_name"` //牌子up主名 - Room_id int `json:"roomid"` //牌子直播间 - Last_wear_time int `json:"last_wear_time"` //佩戴有效截止时间(佩戴本身不会刷新,发弹幕,送小心心,送金瓜子礼物才会刷新) - Today_intimacy int `json:"today_intimacy"` //今日亲密(0:未发送弹幕 100:已发送弹幕) - Is_lighted int `json:"is_lighted"` //牌子是否熄灭(0:熄灭 1:亮) + /*https://passport.bilibili.com/x/passport-login/web/sms/send*/ } //获取牌子信息 -func Get_list_in_room() (array []TGet_list_in_room) { +func Get_list_in_room() (array []J.GetMyMedals_Items) { apilog := apilog.Base_add(`获取牌子`) //验证cookie @@ -1434,11 +1417,11 @@ func Get_list_in_room() (array []TGet_list_in_room) { }) { //获取牌子列表 - var medalList []TGet_list_in_room + var medalList []J.GetMyMedals_Items for pageNum := 1; true; pageNum += 1 { r := reqf.New() if e := r.Reqf(reqf.Rval{ - Url: `https://api.live.bilibili.com/fans_medal/v5/live_fans_medal/iApiMedal?page=` + strconv.Itoa(pageNum) + `&pageSize=10`, + Url: `https://api.live.bilibili.com/xlive/app-ucenter/v1/user/GetMyMedals?page=` + strconv.Itoa(pageNum) + `&pageSize=10`, Header: map[string]string{ `Cookie`: reqf.Map_2_Cookies_String(Cookie), }, @@ -1450,31 +1433,20 @@ func Get_list_in_room() (array []TGet_list_in_room) { return } - var res struct { - Code int `json:"code"` - Msg string `json:"msg"` - Message string `json:"message"` - Data struct { - FansMedalList []TGet_list_in_room `json:"fansMedalList"` - Pageinfo struct { - Totalpages int `json:"totalpages"` - CurPage int `json:"curPage"` - } `json:"pageinfo"` - } `json:"data"` - } + var res J.GetMyMedals if e := json.Unmarshal(r.Respon, &res); e != nil { apilog.L(`E: `, e) } if res.Code != 0 { - apilog.L(`E: `, `返回code`, res.Code, res.Msg) + apilog.L(`E: `, `返回code`, res.Code, res.Message) return } - medalList = append(medalList, res.Data.FansMedalList...) + medalList = append(medalList, res.Data.Items...) - if res.Data.Pageinfo.CurPage == res.Data.Pageinfo.Totalpages { + if res.Data.PageInfo.CurPage == res.Data.PageInfo.TotalPage { break } @@ -1595,10 +1567,10 @@ func CheckSwitch_FansMedal() (missKey []string) { { medal_list := Get_list_in_room() for _, v := range medal_list { - if v.Target_id != c.UpUid { + if v.TargetID != c.UpUid { continue } - medal_id = v.Medal_id + medal_id = v.MedalID } if medal_id == 0 { //无牌 apilog.L(`I: `, `无主播粉丝牌`) diff --git a/Json/getMyMedals.go b/Json/getMyMedals.go new file mode 100644 index 0000000..890a6bc --- /dev/null +++ b/Json/getMyMedals.go @@ -0,0 +1,38 @@ +package part + +type GetMyMedals struct { + Code int `json:"code"` + Message string `json:"message"` + TTL int `json:"ttl"` + Data GetMyMedals_Data `json:"data"` +} +type GetMyMedals_Items struct { + CanDeleted bool `json:"can_deleted"` + DayLimit int `json:"day_limit"` + GuardLevel int `json:"guard_level"` + GuardMedalTitle string `json:"guard_medal_title"` + Intimacy int `json:"intimacy"` + IsLighted int `json:"is_lighted"` + Level int `json:"level"` + MedalName string `json:"medal_name"` + MedalColorBorder int `json:"medal_color_border"` + MedalColorEnd int `json:"medal_color_end"` + MedalColorStart int `json:"medal_color_start"` + MedalID int `json:"medal_id"` + NextIntimacy int `json:"next_intimacy"` + TodayFeed int `json:"today_feed"` + Roomid int `json:"roomid"` + Status int `json:"status"` + TargetID int `json:"target_id"` + TargetName string `json:"target_name"` + Uname string `json:"uname"` +} +type GetMyMedals_PageInfo struct { + CurPage int `json:"cur_page"` + TotalPage int `json:"total_page"` +} +type GetMyMedals_Data struct { + Items []GetMyMedals_Items `json:"items"` + PageInfo GetMyMedals_PageInfo `json:"page_info"` + Count int `json:"count"` +} diff --git a/Reply/F.go b/Reply/F.go index d6698c3..c3da740 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -1,24 +1,27 @@ package reply import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" "fmt" - "os" "io" "io/fs" + "io/ioutil" + "math" + "net/http" + "net/url" + "os" + "os/exec" + "path" + "path/filepath" "strconv" "strings" "sync" - "math" "time" - "os/exec" - "path/filepath" - "path" - "net/http" - "context" - "net/url" - "errors" - "bytes" - "encoding/base64" + // "runtime" "golang.org/x/text/encoding/simplifiedchinese" @@ -30,15 +33,15 @@ import ( p "github.com/qydysky/part" funcCtrl "github.com/qydysky/part/funcCtrl" idpool "github.com/qydysky/part/idpool" + limit "github.com/qydysky/part/limit" msgq "github.com/qydysky/part/msgq" reqf "github.com/qydysky/part/reqf" - web "github.com/qydysky/part/web" - b "github.com/qydysky/part/buf" s "github.com/qydysky/part/signal" - limit "github.com/qydysky/part/limit" + psync "github.com/qydysky/part/sync" util "github.com/qydysky/part/util" + web "github.com/qydysky/part/web" - "github.com/christopher-dG/go-obs-websocket" + obsws "github.com/christopher-dG/go-obs-websocket" ) /* @@ -54,31 +57,38 @@ func IsOn(s string) bool { //字符重复度检查 //a在buf中出现的字符占a的百分数 -func cross(a string,buf []string) (float32) { +func cross(a string, buf []string) float32 { var s float32 var matched bool - for _,v1 := range a { - for _,v2 := range buf { - for _,v3 := range v2 { - if v3 == v1 {matched = true;break} + for _, v1 := range a { + for _, v2 := range buf { + for _, v3 := range v2 { + if v3 == v1 { + matched = true + break + } + } + if matched { + break } - if matched {break} } - if matched {s += 1} + if matched { + s += 1 + } matched = false } return s / float32(len([]rune(a))) } //在a中仅出现一次出现的字符占a的百分数 -func selfcross(a string) (float32) { +func selfcross(a string) float32 { buf := make(map[rune]bool) - for _,v := range a { - if _,ok := buf[v]; !ok { + for _, v := range a { + if _, ok := buf[v]; !ok { buf[v] = true } } - return 1 - float32(len(buf)) / float32(len([]rune(a))) + return 1 - float32(len(buf))/float32(len([]rune(a))) } //在a的每个字符串中 @@ -87,22 +97,27 @@ func selfcross(a string) (float32) { //*单字符串中的重复出现计为1次 func selfcross2(a []string) (float32, string) { buf := make(map[rune]float32) - for _,v := range a { + for _, v := range a { block := make(map[rune]bool) - for _,v1 := range v { - if _,ok := block[v1]; ok {continue} + for _, v1 := range v { + if _, ok := block[v1]; ok { + continue + } block[v1] = true buf[v1] += 1 } } var ( - max float32 + max float32 maxS string - all float32 + all float32 ) - for k,v := range buf { + for k, v := range buf { all += v - if v > max {max = v;maxS = string(k)} + if v > max { + max = v + maxS = string(k) + } } return max / all, maxS } @@ -110,67 +125,71 @@ func selfcross2(a []string) (float32, string) { //功能区 //ShowRev 显示h营收 var ( - ShowRev_old float64 + ShowRev_old float64 ShowRev_start bool ) -func ShowRevf(){ - if!IsOn("统计营收") {return} +func ShowRevf() { + if !IsOn("统计营收") { + return + } if ShowRev_start { - c.Log.Base(`功能`).L(`I: `, fmt.Sprintf("营收 ¥%.2f",c.Rev)) + c.Log.Base(`功能`).L(`I: `, fmt.Sprintf("营收 ¥%.2f", c.Rev)) return } ShowRev_start = true for { - c.Log.Base(`功能`).L(`I: `, fmt.Sprintf("营收 ¥%.2f",c.Rev)) - for c.Rev == ShowRev_old {p.Sys().Timeoutf(60)} + c.Log.Base(`功能`).L(`I: `, fmt.Sprintf("营收 ¥%.2f", c.Rev)) + for c.Rev == ShowRev_old { + p.Sys().Timeoutf(60) + } ShowRev_old = c.Rev } } //Ass 弹幕转字幕 type Ass struct { - file string//弹幕ass文件名 - startT time.Time//开始记录的基准时间 - header string//ass开头 - wrap func(io.Writer)(io.Writer)//编码 + file string //弹幕ass文件名 + startT time.Time //开始记录的基准时间 + header string //ass开头 + wrap func(io.Writer) io.Writer //编码 } 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 { -header:`[Script Info] +var ass = Ass{ + header: `[Script Info] Title: Default Ass file ScriptType: v4.00+ WrapStyle: 0 ScaledBorderAndShadow: yes -PlayResX: `+strconv.Itoa(Ass_height)+` -PlayResY: `+strconv.Itoa(Ass_width)+` +PlayResX: ` + strconv.Itoa(Ass_height) + ` +PlayResY: ` + strconv.Itoa(Ass_width) + ` [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding -Style: Default,,`+strconv.Itoa(Ass_font)+`,&H40FFFFFF,&H000017FF,&H80000000,&H40000000,0,0,0,0,100,100,0,0,1,4,4,`+strconv.Itoa(Ass_loc)+`,20,20,50,1 +Style: Default,,` + strconv.Itoa(Ass_font) + `,&H40FFFFFF,&H000017FF,&H80000000,&H40000000,0,0,0,0,100,100,0,0,1,4,4,` + strconv.Itoa(Ass_loc) + `,20,20,50,1 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text `, -wrap:simplifiedchinese.GB18030.NewEncoder().Writer, + wrap: simplifiedchinese.GB18030.NewEncoder().Writer, } -func init(){ +func init() { accept := map[string]bool{ - `GB18030`:true, - `utf-8`:true, + `GB18030`: true, + `utf-8`: true, } - if v,ok := c.K_v.LoadV("Ass编码").(string);ok{ - if v1,ok := accept[v];ok && v1 { - c.Log.Base(`Ass`).L(`T: `,"编码:", v) + if v, ok := c.K_v.LoadV("Ass编码").(string); ok { + if v1, ok := accept[v]; ok && v1 { + c.Log.Base(`Ass`).L(`T: `, "编码:", v) if v == `utf-8` { ass.wrap = nil } @@ -179,34 +198,42 @@ func init(){ } //设定字幕文件名,为""时停止输出 -func Ass_f(file string, st time.Time){ +func Ass_f(file string, st time.Time) { ass.file = file - if file == "" {return} + if file == "" { + return + } - if rel, err := filepath.Rel(savestream.base_path, ass.file);err == nil { - c.Log.Base(`Ass`).L(`I: `,"保存到", rel + ".ass") + if rel, err := filepath.Rel(savestream.base_path, ass.file); err == nil { + c.Log.Base(`Ass`).L(`I: `, "保存到", rel+".ass") } else { - c.Log.Base(`Ass`).L(`I: `, "保存到", ass.file + ".ass") + c.Log.Base(`Ass`).L(`I: `, "保存到", ass.file+".ass") c.Log.Base(`Ass`).L(`W: `, err) } p.File().FileWR(p.Filel{ - File:ass.file + ".ass", - Loc:0, - Context:[]interface{}{ass.header}, - WrapWriter:ass.wrap, + File: ass.file + ".ass", + Loc: 0, + Context: []interface{}{ass.header}, + WrapWriter: ass.wrap, }) ass.startT = st } //传入要显示的单条字幕 -func Assf(s string){ - if !IsOn("生成Ass弹幕") {return} - if ass.file == "" {return} +func Assf(s string) { + if !IsOn("生成Ass弹幕") { + return + } + if ass.file == "" { + return + } - if s == "" {return} + if s == "" { + return + } - st := time.Since(ass.startT) + time.Duration(p.Rand().MixRandom(0, 2000)) * time.Millisecond - et := st + time.Duration(Ass_T) * time.Second + st := time.Since(ass.startT) + time.Duration(p.Rand().MixRandom(0, 2000))*time.Millisecond + et := st + time.Duration(Ass_T)*time.Second var b string // b += "Comment: " + strconv.Itoa(loc) + " "+ Dtos(showedt) + "\n" @@ -214,10 +241,10 @@ func Assf(s string){ b += dtos(st) + `,` + dtos(et) b += `,Default,,0,0,0,,{\fad(200,500)\blur3}` + s + "\n" p.File().FileWR(p.Filel{ - File:ass.file + ".ass", - Loc:-1, - Context:[]interface{}{b}, - WrapWriter:ass.wrap, + File: ass.file + ".ass", + Loc: -1, + Context: []interface{}{b}, + WrapWriter: ass.wrap, }) } @@ -235,37 +262,38 @@ func dtos(t time.Duration) string { //直播流保存 type Savestream struct { - base_path string - path string + base_path string + path string hls_stream struct { - b []byte//发送给客户的m3u8字节 + b []byte //发送给客户的m3u8字节 t time.Time } - front []byte//flv头及首tag or hls的初始化m4s - stream *msgq.Msgq//发送给客户的flv流关键帧间隔片 or hls的fmp4片 + front []byte //flv头及首tag or hls的初始化m4s + stream *msgq.Msgq //发送给客户的flv流关键帧间隔片 or hls的fmp4片 - m4s_hls int//hls list 中的m4s数量 - hlsbuffersize int//hls list缓冲m4s数量 - hls_banlance_host bool//使用均衡hls服务器 + m4s_hls int //hls list 中的m4s数量 + hlsbuffersize int //hls list缓冲m4s数量 + hls_banlance_host bool //使用均衡hls服务器 - wait *s.Signal - cancel *s.Signal + wait *s.Signal + cancel *s.Signal skipFunc funcCtrl.SkipFunc } type hls_generate struct { - hls_first_fmp4_name string - hls_file_header []byte//发送给客户的m3u8不变头 - m4s_list []*m4s_link_item//m4s列表 缓冲 + hls_first_fmp4_name string + hls_file_header []byte //发送给客户的m3u8不变头 + m4s_list []*m4s_link_item //m4s列表 缓冲 } -type m4s_link_item struct {//使用指针以设置是否已下载 - Url string// m4s链接 - Base string//m4s文件名 - Offset_line int//m3u8中的行下标 - status int//该m4s下载状态 s_noload:未下载 s_loading正在下载 s_fin下载完成 s_fail下载失败 - isshow bool +type m4s_link_item struct { //使用指针以设置是否已下载 + Url string // m4s链接 + Base string //m4s文件名 + Offset_line int //m3u8中的行下标 + status int //该m4s下载状态 s_noload:未下载 s_loading正在下载 s_fin下载完成 s_fail下载失败 + isshow bool } + //m4s状态 const ( s_noload = iota @@ -274,15 +302,15 @@ const ( s_fail ) -var savestream = Savestream { - stream:msgq.New(10),//队列最多保留10个关键帧间隔片 - m4s_hls:8, +var savestream = Savestream{ + stream: msgq.New(10), //队列最多保留10个关键帧间隔片 + m4s_hls: 8, } -func init(){ +func init() { //使用带tag的消息队列在功能间传递消息 c.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ - `savestream`:func(data interface{})(bool){ + `savestream`: func(data interface{}) bool { if savestream.cancel.Islive() { Savestream_wait() } else { @@ -293,99 +321,107 @@ func init(){ }, }) //base_path - if path,ok := c.K_v.LoadV("直播流保存位置").(string);ok{ - if path,err := filepath.Abs(path);err == nil{ - savestream.base_path = path+"/" + if path, ok := c.K_v.LoadV("直播流保存位置").(string); ok { + if path, err := filepath.Abs(path); err == nil { + savestream.base_path = path + "/" } } - if v, ok := c.K_v.LoadV(`直播hls流缓冲`).(float64);ok && v > 0 { + if v, ok := c.K_v.LoadV(`直播hls流缓冲`).(float64); ok && v > 0 { savestream.hlsbuffersize = int(v) } - if v, ok := c.K_v.LoadV(`直播hls流均衡`).(bool);ok { + if v, ok := c.K_v.LoadV(`直播hls流均衡`).(bool); ok { savestream.hls_banlance_host = v } } //已go func形式调用,将会获取直播流 -func Savestreamf(){ +func Savestreamf() { l := c.Log.Base(`savestream`) //避免多次开播导致的多次触发 { if savestream.skipFunc.NeedSkip() { - l.L(`T: `,`已存在实例`) + l.L(`T: `, `已存在实例`) return } defer savestream.skipFunc.UnSet() } want_qn, ok := c.K_v.LoadV("直播流清晰度").(float64) - if !ok || want_qn < 0 {return} + if !ok || want_qn < 0 { + return + } c.Live_want_qn = int(want_qn) F.Get(`Live`) - if savestream.cancel.Islive() {return} + if savestream.cancel.Islive() { + return + } //random host load balance Host_list := []string{} if savestream.hls_banlance_host { - for _,v := range c.Live { - url_struct,e := url.Parse(v) - if e != nil {continue} + for _, v := range c.Live { + url_struct, e := url.Parse(v) + if e != nil { + continue + } Host_list = append(Host_list, url_struct.Hostname()) } } var ( - no_found_link = errors.New("no_found_link") - no_Modified = errors.New("no_Modified") + no_found_link = errors.New("no_found_link") + no_Modified = errors.New("no_Modified") last_hls_Modified time.Time - hls_get_link = func(m3u8_urls []string,last_download *m4s_link_item) (need_download []*m4s_link_item,m3u8_file_addition []byte,expires int,err error) { + hls_get_link = func(m3u8_urls []string, last_download *m4s_link_item) (need_download []*m4s_link_item, m3u8_file_addition []byte, expires int, err error) { var ( - r *reqf.Req + r *reqf.Req m3u8_url *url.URL ) - for index:=0;index 3000 { + if usedt := r.UsedTime.Seconds(); usedt > 3000 { l.L(`I: `, `hls列表下载慢`, usedt, `ms`) } if r.Response.StatusCode == http.StatusNotModified { @@ -394,8 +430,8 @@ func Savestreamf(){ return } //last_hls_Modified - if t,ok := r.Response.Header[`Last-Modified`];ok && len(t) > 0 { - if lm,e := time.Parse("Mon, 02 Jan 2006 15:04:05 CST", t[0]);e == nil { + if t, ok := r.Response.Header[`Last-Modified`]; ok && len(t) > 0 { + if lm, e := time.Parse("Mon, 02 Jan 2006 15:04:05 CST", t[0]); e == nil { last_hls_Modified = lm } else { l.L(`T: `, e) @@ -404,12 +440,12 @@ func Savestreamf(){ query := m3u8_url.Query() trid := query.Get("trid") - expires,_ = strconv.Atoi(query.Get("expires")) + expires, _ = strconv.Atoi(query.Get("expires")) buf := r.Respon //base-64 if len(buf) != 0 && !bytes.Contains(buf, []byte("#")) { - buf,err = base64.StdEncoding.DecodeString(string(buf)) + buf, err = base64.StdEncoding.DecodeString(string(buf)) if err != nil { l.L(`W: `, err, string(buf)) return @@ -418,29 +454,31 @@ func Savestreamf(){ var m4s_links []*m4s_link_item lines := bytes.Split(buf, []byte("\n")) - for i:=0;i 4 { if reqs_keyframe_index == len(reqs_used_id)-1 { - l.L(`T: `,"flv强行拼合") + l.L(`T: `, "flv强行拼合") - for i:=0;i 0 { - live_index = int(p.Rand().MixRandom(0,int64(len(c.Live)-1))) + live_index = int(p.Rand().MixRandom(0, int64(len(c.Live)-1))) } - link,exp,e := flv_get_link(c.Live[live_index]) + link, exp, e := flv_get_link(c.Live[live_index]) if e != nil { - l.L(`W: `,`流链接获取错误`,e) + l.L(`W: `, `流链接获取错误`, e) break } // 新建chan var ( - bc = make(chan []byte,1<<17) - req = reqf.New() + bc = make(chan []byte, 1<<17) + req = reqf.New() req_exit = s.Init() ) - l.L(`I: `,`新建请求`,req.Id()) + l.L(`I: `, `新建请求`, req.Id()) //新建请求 - go func(r *reqf.Req,rval reqf.Rval){ - go func(){ + go func(r *reqf.Req, rval reqf.Rval) { + go func() { select { - case <-exit_chan.WaitC():; - case <-req_exit.WaitC():; + case <-exit_chan.WaitC(): + case <-req_exit.WaitC(): } r.Close() }() defer req_exit.Done() e := r.Reqf(rval) if r.Response == nil { - l.L(`W: `,`请求退出`,r.Id(),e) + l.L(`W: `, `请求退出`, r.Id(), e) } else if r.Response.StatusCode != 200 { - l.L(`W: `,`请求退出`,r.Id(),e,r.Response.Status,string(r.Respon)) + l.L(`W: `, `请求退出`, r.Id(), e, r.Response.Status, string(r.Respon)) } else { - l.L(`W: `,`请求退出`,r.Id()) + l.L(`W: `, `请求退出`, r.Id()) } - }(req,reqf.Rval{ - Url:link, - Proxy:c.Proxy, - Header:map[string]string{ - `Cookie`:reqf.Map_2_Cookies_String(CookieM), + }(req, reqf.Rval{ + Url: link, + Proxy: c.Proxy, + Header: map[string]string{ + `Cookie`: reqf.Map_2_Cookies_String(CookieM), }, //SaveToPath:savestream.path + ".flv", - SaveToChan:bc, - Timeout:int(int64(exp) - p.Sys().GetSTime())*1000, - ReadTimeout:5*1000, - ConnectTimeout:10*1000, + SaveToChan: bc, + Timeout: int(int64(exp)-p.Sys().GetSTime()) * 1000, + ReadTimeout: 5 * 1000, + ConnectTimeout: 10 * 1000, }) //返回通道 var item = link_stream{ - close:req.Close, - id:id_pool.Get(), - } - l.L(`I: `,`新建连接`,item.id.Id) + close: req.Close, + id: id_pool.Get(), + } + l.L(`I: `, `新建连接`, item.id.Id) //解析 - go func(bc chan[]byte,item *link_stream,exit_chan *s.Signal){ + go func(bc chan []byte, item *link_stream, exit_chan *s.Signal) { var ( - buf []byte + buf []byte skip_buf_size int ) defer req_exit.Done() - defer l.L(`W: `,`连接退出`,item.id.Id) + defer l.L(`W: `, `连接退出`, item.id.Id) for exit_chan.Islive() && req_exit.Islive() { select { - case <-exit_chan.WaitC():return; - case <-req_exit.WaitC():return; - case b :=<- bc: + case <-exit_chan.WaitC(): + return + case <-req_exit.WaitC(): + return + case b := <-bc: if len(b) == 0 { // fmt.Println(`req退出`,item.id.Id) id_pool.Put(item.id) @@ -839,19 +889,21 @@ func Savestreamf(){ buf = append(buf, b...) - if len(buf) < skip_buf_size {break} + if len(buf) < skip_buf_size { + break + } - front,list,_ := Seach_stream_tag(buf) + front, list, _ := Seach_stream_tag(buf) if len(front) != 0 && len(item.front) == 0 { // fmt.Println(item.id.Id,`获取到header`,len(front)) - item.front = make([]byte,len(front)) + item.front = make([]byte, len(front)) copy(item.front, front) } if len(list) == 0 || len(item.front) == 0 { // fmt.Println(`再次查询bufsize`,skip_buf_size) - skip_buf_size = 2*len(buf) + skip_buf_size = 2 * len(buf) break } @@ -859,31 +911,31 @@ func Savestreamf(){ { last_keyframe := list[len(list)-1] - cut_offset := bytes.LastIndex(buf, last_keyframe)+len(last_keyframe) + cut_offset := bytes.LastIndex(buf, last_keyframe) + len(last_keyframe) // fmt.Printf("buf截断 当前%d=>%d 下一header %b\n",len(buf),len(buf)-cut_offset,buf[:11]) buf = buf[cut_offset:] } - skip_buf_size = len(buf)+len(list[0]) - reqs.Push_tag(`req`,*item) + skip_buf_size = len(buf) + len(list[0]) + reqs.Push_tag(`req`, *item) } } - }(bc,&item,exit_chan) + }(bc, &item, exit_chan) - expires := int64(exp) - p.Sys().GetSTime()-120 + expires := int64(exp) - p.Sys().GetSTime() - 120 // no expect qn if c.Live_want_qn < c.Live_qn { - expires = time.Now().Add(time.Minute*2).Unix() + expires = time.Now().Add(time.Minute * 2).Unix() } //等待过期/退出 { var exit_sign bool select { - case <- req_exit.Chan:;//本次连接错误,退出重试 - case <- exit_chan.Chan://要求退出 - exit_sign = true// - case <- time.After(time.Second*time.Duration(int(expires))):; + case <-req_exit.Chan: //本次连接错误,退出重试 + case <-exit_chan.Chan: //要求退出 + exit_sign = true // + case <-time.After(time.Second * time.Duration(int(expires))): } if exit_sign { //退出 @@ -892,94 +944,106 @@ func Savestreamf(){ } } - l.L(`I: `,"flv关闭,开始新连接") + l.L(`I: `, "flv关闭,开始新连接") //即将过期,刷新c.Live F.Get(`Liveing`) - if !c.Liveing {break} + if !c.Liveing { + break + } F.Get(`Live`) - if len(c.Live)==0 {break} + if len(c.Live) == 0 { + break + } } exit_chan.Done() - reqs.Push_tag(`close`,nil) + reqs.Push_tag(`close`, nil) out.Close() p.FileMove(savestream.path+".flv.dtmp", savestream.path+".flv") } else { savestream.path += "/" - if rel, err := filepath.Rel(savestream.base_path, savestream.path);err == nil { - l.L(`I: `,"保存到", rel+`/0.m3u8`) + if rel, err := filepath.Rel(savestream.base_path, savestream.path); err == nil { + l.L(`I: `, "保存到", rel+`/0.m3u8`) } else { - l.L(`I: `,"保存到", savestream.path) + l.L(`I: `, "保存到", savestream.path) l.L(`W: `, err) } Ass_f(savestream.path+"0", time.Now()) var ( - hls_msg = msgq.New(20) - hls_gen hls_generate + hls_msg = msgq.New(20) + hls_gen hls_generate DISCONTINUITY int - SEQUENCE int + SEQUENCE int ) //hls stream gen 用户m3u8生成 - go func(){ + go func() { per_second := time.Tick(time.Second) for { select { - case <- savestream.cancel.WaitC():return;//exit - case now :=<- per_second:hls_msg.Push_tag(`clock`, now); + case <-savestream.cancel.WaitC(): + return //exit + case now := <-per_second: + hls_msg.Push_tag(`clock`, now) } } }() //hls stream gen 用户m3u8生成 - hls_msg.Pull_tag(map[string]func(interface{})(bool){ - `header`:func(d interface{})(bool){ - if b,ok := d.([]byte);ok { + hls_msg.Pull_tag(map[string]func(interface{}) bool{ + `header`: func(d interface{}) bool { + if b, ok := d.([]byte); ok { hls_gen.hls_file_header = b } return false }, - `body`:func(d interface{})(bool){ - links,ok := d.([]*m4s_link_item) - if !ok {return false} + `body`: func(d interface{}) bool { + links, ok := d.([]*m4s_link_item) + if !ok { + return false + } //remove hls first m4s if len(links) > 0 && len((*links[0]).Base) > 0 && - (*links[0]).Base[0] == 104 {links = links[1:]} + (*links[0]).Base[0] == 104 { + links = links[1:] + } hls_gen.m4s_list = append(hls_gen.m4s_list, links...) - + return false }, - `clock`:func(now interface{})(bool){ + `clock`: func(now interface{}) bool { //buffer - if len(hls_gen.m4s_list) - savestream.hlsbuffersize < 0 { + if len(hls_gen.m4s_list)-savestream.hlsbuffersize < 0 { return false } //add block var ( - m4s_num int + m4s_num int has_DICONTINUITY bool - res []byte - threshold = savestream.m4s_hls + res []byte + threshold = savestream.m4s_hls ) - if threshold < 3 {threshold=3} + if threshold < 3 { + threshold = 3 + } { //m4s list m4s_list_b := []byte{} - for k,v := range hls_gen.m4s_list { + for k, v := range hls_gen.m4s_list { if v.status != s_fin { //#EXT-X-DISCONTINUITY-SEQUENCE //reset hls lists if !has_DICONTINUITY && m4s_num < threshold { m4s_list := append(util.SliceCopy(hls_gen.m4s_list[:k]).([]*m4s_link_item), &m4s_link_item{ - Base:"DICONTINUITY", - status:s_fin, - isshow:true, + Base: "DICONTINUITY", + status: s_fin, + isshow: true, }) hls_gen.m4s_list = append(m4s_list, hls_gen.m4s_list[k:]...) m4s_list_b = append(m4s_list_b, []byte("#EXT-X-DICONTINUITY\n")...) @@ -995,7 +1059,9 @@ func Savestreamf(){ continue } - if m4s_num >= savestream.m4s_hls {break} + if m4s_num >= savestream.m4s_hls { + break + } m4s_num += 1 // if m4s_num == 1 {SEQUENCE = strings.ReplaceAll(v.Base, ".m4s", "")} @@ -1013,7 +1079,7 @@ func Savestreamf(){ //add #EXT-X-MEDIA-SEQUENCE res = append(res, []byte("#EXT-X-MEDIA-SEQUENCE:"+strconv.Itoa(SEQUENCE)+"\n")...) //add #INFO - res = append(res, []byte(fmt.Sprintf("#INFO-BUFFER:%d/%d\n",m4s_num,len(hls_gen.m4s_list)))...) + res = append(res, []byte(fmt.Sprintf("#INFO-BUFFER:%d/%d\n", m4s_num, len(hls_gen.m4s_list)))...) //add m4s res = append(res, m4s_list_b...) //去除最后一个换行 @@ -1025,11 +1091,11 @@ func Savestreamf(){ var skip_del bool if m4s_num < threshold { var ( - index int//the first useable fmp4 index of section + index int //the first useable fmp4 index of section DICONTINUITY_num int - catch bool//catch useable fmp4? + catch bool //catch useable fmp4? ) - for i:=0;i=0;index-=1 { - if hls_gen.m4s_list[index].status == s_fin {continue} - skip+=1 - hls_gen.m4s_list = append(hls_gen.m4s_list[:index],hls_gen.m4s_list[index+1:]...) + for ; index >= 0; index -= 1 { + if hls_gen.m4s_list[index].status == s_fin { + continue + } + skip += 1 + hls_gen.m4s_list = append(hls_gen.m4s_list[:index], hls_gen.m4s_list[index+1:]...) } - l.L(`I: `,"卡顿,跳过",skip,"个tag") + l.L(`I: `, "卡顿,跳过", skip, "个tag") break } } @@ -1057,13 +1125,17 @@ func Savestreamf(){ } //设置到全局变量,方便流服务器获取 - if len(res) != 0 {savestream.hls_stream.b = res} - savestream.hls_stream.t,_ = now.(time.Time) + if len(res) != 0 { + savestream.hls_stream.b = res + } + savestream.hls_stream.t, _ = now.(time.Time) //del - for del_num:=1;!skip_del&&del_num > 0;hls_gen.m4s_list = hls_gen.m4s_list[1:] { + for del_num := 1; !skip_del && del_num > 0; hls_gen.m4s_list = hls_gen.m4s_list[1:] { del_num -= 1 - if !hls_gen.m4s_list[0].isshow {continue} + if !hls_gen.m4s_list[0].isshow { + continue + } //#EXT-X-DICONTINUITY if hls_gen.m4s_list[0].Base == "DICONTINUITY" { DISCONTINUITY += 1 @@ -1073,23 +1145,23 @@ func Savestreamf(){ if hls_gen.m4s_list[0].isshow { SEQUENCE += 1 - {//stream hls2mp4 + { //stream hls2mp4 var buf []byte //stream front if len(savestream.front) == 0 { - buf,_,e := get_m4s_cache(savestream.path+hls_gen.hls_first_fmp4_name) + buf, _, e := get_m4s_cache(savestream.path + hls_gen.hls_first_fmp4_name) if e == nil { savestream.front = buf } else { - l.L(`W: `,`推送mp4流错误,无法读取文件` ,e) + l.L(`W: `, `推送mp4流错误,无法读取文件`, e) } } //stream body - buf,_,e := get_m4s_cache(savestream.path+hls_gen.m4s_list[0].Base) + buf, _, e := get_m4s_cache(savestream.path + hls_gen.m4s_list[0].Base) if e == nil { savestream.stream.Push_tag(`stream`, buf) } else { - l.L(`W: `,`推送mp4流错误,无法读取文件` ,e) + l.L(`W: `, `推送mp4流错误,无法读取文件`, e) } } } @@ -1097,31 +1169,31 @@ func Savestreamf(){ return false }, - `close`:func(d interface{})(bool){ - savestream.hls_stream.b = []byte{}//退出置空 + `close`: func(d interface{}) bool { + savestream.hls_stream.b = []byte{} //退出置空 savestream.hls_stream.t = time.Now() return true }, }) var ( - last_download *m4s_link_item - miss_download = make(chan *m4s_link_item,100) + last_download *m4s_link_item + miss_download = make(chan *m4s_link_item, 100) download_limit = funcCtrl.BlockFuncN{ - Max:2, - }//limit + Max: 2, + } //limit ) - expires := time.Now().Add(time.Minute*2).Unix() + expires := time.Now().Add(time.Minute * 2).Unix() var ( - path_front string + path_front string path_behind string ) for { //退出,等待下载完成 if !savestream.cancel.Islive() { - l.L(`I: `,"退出,等待片段下载") + l.L(`I: `, "退出,等待片段下载") download_limit.None() download_limit.UnNone() @@ -1131,22 +1203,22 @@ func Savestreamf(){ links = append(links, <-miss_download) } - for k,v :=range links { - l.L(`I: `,"正在下载最后片段:",k+1,"/",len(links)) + for k, v := range links { + l.L(`I: `, "正在下载最后片段:", k+1, "/", len(links)) v.status = s_loading r := reqf.New() if e := r.Reqf(reqf.Rval{ - Url:v.Url, - SaveToPath:savestream.path+v.Base, - ConnectTimeout:5000, - ReadTimeout:1000, - Retry: 1, - Proxy:c.Proxy, + Url: v.Url, + SaveToPath: savestream.path + v.Base, + ConnectTimeout: 5000, + ReadTimeout: 1000, + Retry: 1, + Proxy: c.Proxy, }); e != nil && !errors.Is(e, io.EOF) { - l.L(`I: `,e) + l.L(`I: `, e) v.status = s_fail } else { - if usedt := r.UsedTime.Seconds();usedt > 700 { + if usedt := r.UsedTime.Seconds(); usedt > 700 { l.L(`I: `, `hls切片下载慢`, usedt, `ms`) } v.status = s_fin @@ -1156,16 +1228,16 @@ func Savestreamf(){ break } - links,file_add,exp,e := hls_get_link(c.Live,last_download) + links, file_add, exp, e := hls_get_link(c.Live, last_download) if e != nil { if e == no_Modified { - time.Sleep(time.Duration(2)*time.Second) + time.Sleep(time.Duration(2) * time.Second) continue } else if reqf.IsTimeout(e) || strings.Contains(e.Error(), "x509") { - l.L(`I: `,e) + l.L(`I: `, e) continue } else { - l.L(`W: `,e) + l.L(`W: `, e) break } } @@ -1174,13 +1246,15 @@ func Savestreamf(){ if last_download == nil { var res []byte { - for row,i:=bytes.SplitAfter(file_add, []byte("\n")),0;i 100 { - l.L(`W: `,`diff too large `,diff) + if diff := now - previou; diff > 100 { + l.L(`W: `, `diff too large `, diff) break } else { - l.L(`I: `,`猜测hls`,previou,`-`,now,`(`,diff,`)`) + l.L(`I: `, `猜测hls`, previou, `-`, now, `(`, diff, `)`) } - {//file_add - for i:=now-1;i>previou;i-=1 { - file_add = append([]byte(strconv.Itoa(i)+".m4s"),file_add...) + { //file_add + for i := now - 1; i > previou; i -= 1 { + file_add = append([]byte(strconv.Itoa(i)+".m4s"), file_add...) } } - {//links + { //links if path_front == "" || path_behind == "" { u, e := url.Parse(links[0].Url) if e != nil { - l.L(`E: `,`fault to enable guess`,e) + l.L(`E: `, `fault to enable guess`, e) return } - path_front = u.Scheme+"://"+path.Dir(u.Host+u.Path)+"/" - path_behind = "?"+u.RawQuery + path_front = u.Scheme + "://" + path.Dir(u.Host+u.Path) + "/" + path_behind = "?" + u.RawQuery } //出错期间没能获取到的 - for i:=now-1;i>previou;i-=1 { - base := strconv.Itoa(i)+".m4s" + for i := now - 1; i > previou; i -= 1 { + base := strconv.Itoa(i) + ".m4s" links = append([]*m4s_link_item{ &m4s_link_item{ - Url:path_front+base+path_behind, - Base:base, + Url: path_front + base + path_behind, + Base: base, }, }, links...) } @@ -1241,15 +1315,19 @@ func Savestreamf(){ } if len(links) > 10 { - l.L(`T: `,`等待下载切片:`,len(links)) + l.L(`T: `, `等待下载切片:`, len(links)) } else if len(links) > 100 { - l.L(`W: `,`重试,等待下载切片:`,len(links)) + l.L(`W: `, `重试,等待下载切片:`, len(links)) - if F.Get(`Liveing`);!c.Liveing {break} - if F.Get(`Live`);len(c.Live) == 0 {break} + if F.Get(`Liveing`); !c.Liveing { + break + } + if F.Get(`Live`); len(c.Live) == 0 { + break + } // set expect - expires = time.Now().Add(time.Minute*2).Unix() + expires = time.Now().Add(time.Minute * 2).Unix() continue } @@ -1258,68 +1336,72 @@ func Savestreamf(){ f := p.File() f.FileWR(p.Filel{ - File:savestream.path+"0.m3u8.dtmp", - Loc:-1, - Context:[]interface{}{file_add}, + File: savestream.path + "0.m3u8.dtmp", + Loc: -1, + Context: []interface{}{file_add}, }) - for i:=0;i 700 { + if usedt := r.UsedTime.Seconds(); usedt > 700 { l.L(`I: `, `hls切片下载慢`, usedt, `ms`) } link.status = s_fin //存入cache - if _,ok := m4s_cache.Load(path + link.Base);!ok{ - m4s_cache.Store(path + link.Base, r.Respon) - go func(){//移除 - time.Sleep(time.Second*time.Duration(savestream.hlsbuffersize+savestream.m4s_hls+2)) + if _, ok := m4s_cache.Load(path + link.Base); !ok { + m4s_cache.Store(path+link.Base, r.Respon) + go func() { //移除 + time.Sleep(time.Second * time.Duration(savestream.hlsbuffersize+savestream.m4s_hls+2)) m4s_cache.Delete(path + link.Base) }() } break } } - }(links[i],savestream.path) + }(links[i], savestream.path) //只记录最新 if links[i].Offset_line > 0 { @@ -1329,21 +1411,25 @@ func Savestreamf(){ //m3u8_url 将过期 if p.Sys().GetSTime()+60 > expires { - if F.Get(`Liveing`);!c.Liveing {break} - if F.Get(`Live`);len(c.Live) == 0 {break} + if F.Get(`Liveing`); !c.Liveing { + break + } + if F.Get(`Live`); len(c.Live) == 0 { + break + } // set expect - expires = time.Now().Add(time.Minute*2).Unix() + expires = time.Now().Add(time.Minute * 2).Unix() } else { time.Sleep(time.Second) } } - if p.Checkfile().IsExist(savestream.path+"0.m3u8.dtmp") { + if p.Checkfile().IsExist(savestream.path + "0.m3u8.dtmp") { f := p.File() f.FileWR(p.Filel{ - File:savestream.path+"0.m3u8.dtmp", - Loc:-1, - Context:[]interface{}{"#EXT-X-ENDLIST"}, + File: savestream.path + "0.m3u8.dtmp", + Loc: -1, + Context: []interface{}{"#EXT-X-ENDLIST"}, }) p.FileMove(savestream.path+"0.m3u8.dtmp", savestream.path+"0.m3u8") } @@ -1352,15 +1438,15 @@ func Savestreamf(){ } //set ro `` savestream.path = `` - savestream.front = []byte{}//flv头及首tag置空 + savestream.front = []byte{} //flv头及首tag置空 savestream.stream.Push_tag("close", nil) - Ass_f("", time.Now())//ass - l.L(`I: `,"结束") + Ass_f("", time.Now()) //ass + l.L(`I: `, "结束") if !savestream.cancel.Islive() { // l.L(`I: `,"退出") break - }//cancel + } //cancel /* Savestream需要外部组件 ffmpeg http://ffmpeg.org/download.html @@ -1380,62 +1466,74 @@ func Savestreamf(){ } //已func形式调用,将会停止保存直播流 -func Savestream_wait(){ - if !savestream.cancel.Islive() {return} +func Savestream_wait() { + if !savestream.cancel.Islive() { + return + } savestream.cancel.Done() - c.Log.Base(`savestream`).L(`I: `,"等待停止") + c.Log.Base(`savestream`).L(`I: `, "等待停止") savestream.wait.Wait() } type Obs struct { - c obsws.Client - Prog string//程序路径 + c obsws.Client + Prog string //程序路径 } -var obs = Obs { - c:obsws.Client{Host: "127.0.0.1", Port: 4444}, - Prog:"obs", +var obs = Obs{ + c: obsws.Client{Host: "127.0.0.1", Port: 4444}, + Prog: "obs", } -func Obsf(on bool){ - if !IsOn("调用obs") {return} +func Obsf(on bool) { + if !IsOn("调用obs") { + return + } l := c.Log.Base(`obs`) if on { - if p.Sys().CheckProgram("obs")[0] != 0 {l.L(`W: `,"obs已经启动");return} + if p.Sys().CheckProgram("obs")[0] != 0 { + l.L(`W: `, "obs已经启动") + return + } if p.Sys().CheckProgram("obs")[0] == 0 { if obs.Prog == "" { - l.L(`E: `,"未知的obs程序位置") + l.L(`E: `, "未知的obs程序位置") return } - l.L(`I: `,"启动obs") + l.L(`I: `, "启动obs") p.Exec().Start(exec.Command(obs.Prog)) p.Sys().Timeoutf(3) } // Connect a client. if err := obs.c.Connect(); err != nil { - l.L(`E: `,err) + l.L(`E: `, err) return } } else { - if p.Sys().CheckProgram("obs")[0] == 0 {l.L(`W: `,"obs未启动");return} + if p.Sys().CheckProgram("obs")[0] == 0 { + l.L(`W: `, "obs未启动") + return + } obs.c.Disconnect() } } -func Obs_R(on bool){ - if !IsOn("调用obs") {return} +func Obs_R(on bool) { + if !IsOn("调用obs") { + return + } l := c.Log.Base("obs_R") if p.Sys().CheckProgram("obs")[0] == 0 { - l.L(`W: `,"obs未启动") + l.L(`W: `, "obs未启动") return } else { if err := obs.c.Connect(); err != nil { - l.L(`E: `,err) + l.L(`E: `, err) return } } @@ -1443,30 +1541,30 @@ func Obs_R(on bool){ if on { req := obsws.NewStartRecordingRequest() if err := req.Send(obs.c); err != nil { - l.L(`E: `,err) + l.L(`E: `, err) return } resp, err := req.Receive() if err != nil { - l.L(`E: `,err) + l.L(`E: `, err) return } if resp.Status() == "ok" { - l.L(`I: `,"开始录制") + l.L(`I: `, "开始录制") } } else { req := obsws.NewStopRecordingRequest() if err := req.Send(obs.c); err != nil { - l.L(`E: `,err) + l.L(`E: `, err) return } resp, err := req.Receive() if err != nil { - l.L(`E: `,err) + l.L(`E: `, err) return } if resp.Status() == "ok" { - l.L(`I: `,"停止录制") + l.L(`I: `, "停止录制") } p.Sys().Timeoutf(3) } @@ -1474,21 +1572,22 @@ func Obs_R(on bool){ type Autoban struct { Banbuf []string - buf []string + buf []string } -var autoban = Autoban { -} +var autoban = Autoban{} func Autobanf(s string) bool { - if !IsOn("Autoban") {return false} + if !IsOn("Autoban") { + return false + } if len(autoban.Banbuf) == 0 { f := p.File().FileWR(p.Filel{ - File:"Autoban.txt", + File: "Autoban.txt", }) - for _,v := range strings.Split(f, "\n") { + for _, v := range strings.Split(f, "\n") { autoban.Banbuf = append(autoban.Banbuf, v) } } @@ -1497,66 +1596,82 @@ func Autobanf(s string) bool { autoban.buf = append(autoban.buf, s) return false } - defer func(){ + defer func() { autoban.buf = append(autoban.buf[1:], s) }() var res []float32 { pt := float32(len([]rune(s))) - if pt <= 5 {return false}//字数过少去除 + if pt <= 5 { + return false + } //字数过少去除 res = append(res, pt) } { - pt := selfcross(s); + pt := selfcross(s) // if pt > 0.5 {return false}//自身重复高去除 // res = append(res, pt) - pt1 := cross(s, autoban.buf); - if pt + pt1 > 0.3 {return false}//历史重复高去除 + pt1 := cross(s, autoban.buf) + if pt+pt1 > 0.3 { + return false + } //历史重复高去除 res = append(res, pt, pt1) } { - pt := cross(s, autoban.Banbuf); - if pt < 0.8 {return false}//ban字符重复低去除 + pt := cross(s, autoban.Banbuf) + if pt < 0.8 { + return false + } //ban字符重复低去除 res = append(res, pt) } l := c.Log.Base("autoban") - l.L(`W: `,res) + l.L(`W: `, res) return true } type Danmuji struct { - Buf map[string]string - Inuse_auto bool + Buf map[string]string + Inuse_auto bool reflect_limit *limit.Limit mute bool } var danmuji = Danmuji{ - Inuse_auto:IsOn("自动弹幕机"), - Buf:map[string]string{ - "弹幕机在么":"在", + Inuse_auto: IsOn("自动弹幕机"), + Buf: map[string]string{ + "弹幕机在么": "在", }, - reflect_limit:limit.New(1,4000,8000), + reflect_limit: limit.New(1, 4000, 8000), } -func init(){//初始化反射型弹幕机 - buf := b.New() - buf.Load("config/config_auto_reply.json") - for k,v := range buf.B { - if k == v {continue} +func init() { //初始化反射型弹幕机 + bb, err := ioutil.ReadFile("config/config_auto_reply.json") + if err != nil { + return + } + var buf map[string]interface{} + json.Unmarshal(bb, &buf) + for k, v := range buf { + if k == v { + continue + } danmuji.Buf[k] = v.(string) } } func Danmujif(s string) { - if !IsOn("反射弹幕机") {return} + if !IsOn("反射弹幕机") { + return + } - if danmuji.reflect_limit.TO() {return} + if danmuji.reflect_limit.TO() { + return + } - for k,v := range danmuji.Buf { + for k, v := range danmuji.Buf { if strings.Contains(s, k) { Msg_senddanmu(v) break @@ -1565,24 +1680,32 @@ func Danmujif(s string) { } func Danmuji_auto() { - if !IsOn("反射弹幕机") || !IsOn("自动弹幕机") || danmuji.mute {return} + if !IsOn("反射弹幕机") || !IsOn("自动弹幕机") || danmuji.mute { + return + } danmuji.mute = true var ( - list []string + list []string timeout int ) - for _,v := range c.K_v.LoadV(`自动弹幕机_内容`).([]interface{}){ + for _, v := range c.K_v.LoadV(`自动弹幕机_内容`).([]interface{}) { list = append(list, v.(string)) } timeout = int(c.K_v.LoadV(`自动弹幕机_发送间隔s`).(float64)) - if timeout < 5 {timeout = 5} + if timeout < 5 { + timeout = 5 + } - go func(){ - for i := 0; true; i++{ - if i >= len(list) {i = 0} - if msg := list[i];msg != ``{Msg_senddanmu(msg)} + go func() { + for i := 0; true; i++ { + if i >= len(list) { + i = 0 + } + if msg := list[i]; msg != `` { + Msg_senddanmu(msg) + } p.Sys().Timeoutf(timeout) } }() @@ -1590,66 +1713,70 @@ func Danmuji_auto() { type Autoskip struct { roomid int - buf map[string]Autoskip_item + buf map[string]Autoskip_item sync.Mutex - now uint + now uint ticker *time.Ticker } type Autoskip_item struct { Exprie uint - Num uint + Num uint } var autoskip = Autoskip{ - buf:make(map[string]Autoskip_item), - ticker:time.NewTicker(time.Duration(2)*time.Second), + buf: make(map[string]Autoskip_item), + ticker: time.NewTicker(time.Duration(2) * time.Second), } -func init(){ - go func(){ +func init() { + go func() { for { <-autoskip.ticker.C - if len(autoskip.buf) == 0 {continue} + if len(autoskip.buf) == 0 { + continue + } autoskip.now += 1 autoskip.Lock() if autoskip.roomid != c.Roomid { autoskip.buf = make(map[string]Autoskip_item) autoskip.roomid = c.Roomid - flog.Base_add(`弹幕合并`).L(`T: `,`房间更新:`,autoskip.roomid) + flog.Base_add(`弹幕合并`).L(`T: `, `房间更新:`, autoskip.roomid) autoskip.Unlock() continue } - for k,v := range autoskip.buf{ + for k, v := range autoskip.buf { if v.Exprie <= autoskip.now { - delete(autoskip.buf,k) - {//超时显示 + delete(autoskip.buf, k) + { //超时显示 if v.Num > 3 { - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:`0multi`, - m:map[string]string{ - `{num}`:strconv.Itoa(int(v.Num)), - `{msg}`:k, + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: `0multi`, + m: map[string]string{ + `{num}`: strconv.Itoa(int(v.Num)), + `{msg}`: k, }, }) Msg_showdanmu(Danmu_item{ - msg:strconv.Itoa(int(v.Num)) + " x " + k, - uid:`0multi`, - roomid:autoskip.roomid, + msg: strconv.Itoa(int(v.Num)) + " x " + k, + uid: `0multi`, + roomid: autoskip.roomid, }) } else if v.Num > 1 { Msg_showdanmu(Danmu_item{ - msg:strconv.Itoa(int(v.Num)) + " x " + k, - uid:`0default`, - roomid:autoskip.roomid, + msg: strconv.Itoa(int(v.Num)) + " x " + k, + uid: `0default`, + roomid: autoskip.roomid, }) } } } } - {//copy map + { //copy map tmp := make(map[string]Autoskip_item) - for k,v := range autoskip.buf {tmp[k] = v} + for k, v := range autoskip.buf { + tmp[k] = v + } autoskip.buf = tmp } autoskip.Unlock() @@ -1658,60 +1785,64 @@ func init(){ } func Autoskipf(s string) uint { - if !IsOn("弹幕合并") || s == ""{return 0} + if !IsOn("弹幕合并") || s == "" { + return 0 + } autoskip.Lock() defer autoskip.Unlock() if autoskip.roomid != c.Roomid { autoskip.buf = make(map[string]Autoskip_item) autoskip.roomid = c.Roomid - flog.Base_add(`弹幕合并`).L(`T: `,`房间更新:`,autoskip.roomid) + flog.Base_add(`弹幕合并`).L(`T: `, `房间更新:`, autoskip.roomid) return 0 } - {//验证是否已经存在 - if v,ok := autoskip.buf[s];ok && autoskip.now < v.Exprie{ + { //验证是否已经存在 + if v, ok := autoskip.buf[s]; ok && autoskip.now < v.Exprie { autoskip.buf[s] = Autoskip_item{ - Exprie:v.Exprie, - Num:v.Num+1, + Exprie: v.Exprie, + Num: v.Num + 1, } return v.Num } } - {//设置 + { //设置 autoskip.buf[s] = Autoskip_item{ - Exprie:autoskip.now + 8, - Num:1, + Exprie: autoskip.now + 8, + Num: 1, } } return 0 } type Lessdanmu struct { - roomid int - buf []string - limit *limit.Limit - max_num int + roomid int + buf []string + limit *limit.Limit + max_num int threshold float32 } var lessdanmu = Lessdanmu{ - threshold:0.7, + threshold: 0.7, } func init() { - if max_num,ok := c.K_v.LoadV(`每秒显示弹幕数`).(float64);ok && int(max_num) >= 1 { - flog.Base_add(`更少弹幕`).L(`T: `,`每秒弹幕数:`,int(max_num)) + if max_num, ok := c.K_v.LoadV(`每秒显示弹幕数`).(float64); ok && int(max_num) >= 1 { + flog.Base_add(`更少弹幕`).L(`T: `, `每秒弹幕数:`, int(max_num)) lessdanmu.max_num = int(max_num) - lessdanmu.limit = limit.New(int(max_num),1000,0)//timeout right now + lessdanmu.limit = limit.New(int(max_num), 1000, 0) //timeout right now } } func Lessdanmuf(s string) (show bool) { - if !IsOn("相似弹幕忽略") {return true} + if !IsOn("相似弹幕忽略") { + return true + } if lessdanmu.roomid != c.Roomid { lessdanmu.buf = nil lessdanmu.roomid = c.Roomid lessdanmu.threshold = 0.7 - flog.Base_add(`更少弹幕`).L(`T: `,`房间更新:`,lessdanmu.roomid) + flog.Base_add(`更少弹幕`).L(`T: `, `房间更新:`, lessdanmu.roomid) return true } if len(lessdanmu.buf) < 20 { @@ -1720,7 +1851,9 @@ func Lessdanmuf(s string) (show bool) { } o := cross(s, lessdanmu.buf) - if o == 1 {return false}//完全无用 + if o == 1 { + return false + } //完全无用 Jiezouf(lessdanmu.buf) lessdanmu.buf = append(lessdanmu.buf[1:], s) @@ -1791,24 +1924,30 @@ type Shortdanmu struct { lastdanmu []rune } -var shortdanmu = Shortdanmu{ -} +var shortdanmu = Shortdanmu{} func Shortdanmuf(s string) string { - if !IsOn("精简弹幕") {return s} - if len(shortdanmu.lastdanmu) == 0 {shortdanmu.lastdanmu = []rune(s);return s} + if !IsOn("精简弹幕") { + return s + } + if len(shortdanmu.lastdanmu) == 0 { + shortdanmu.lastdanmu = []rune(s) + return s + } var new string - for k,v := range []rune(s) { + for k, v := range []rune(s) { if k >= len(shortdanmu.lastdanmu) { new += string([]rune(s)[k:]) break } if v != shortdanmu.lastdanmu[k] { switch k { - case 0, 1, 2:new = s - default:new = "..." + string([]rune(s)[k-1:]) + case 0, 1, 2: + new = s + default: + new = "..." + string([]rune(s)[k-1:]) } break } @@ -1820,41 +1959,50 @@ func Shortdanmuf(s string) string { type Jiezou struct { alertdanmu string - skipS map[string]interface{} + skipS map[string]interface{} - avg float32 + avg float32 turn int sync.Mutex } var jiezou = Jiezou{ - alertdanmu:"", - skipS:map[string]interface{}{//常见语气词忽略 - "了":nil, - "的":nil, - "哈":nil, - "是":nil, - ",":nil, - "这":nil, + alertdanmu: "", + skipS: map[string]interface{}{ //常见语气词忽略 + "了": nil, + "的": nil, + "哈": nil, + "是": nil, + ",": nil, + "这": nil, }, } func Jiezouf(s []string) bool { - if !IsOn("Jiezou") {return false} - now,S := selfcross2(s) - jiezou.avg = (8 * jiezou.avg + 2 * now)/10 - if jiezou.turn < len(s) {jiezou.turn += 1;return false} + if !IsOn("Jiezou") { + return false + } + now, S := selfcross2(s) + jiezou.avg = (8*jiezou.avg + 2*now) / 10 + if jiezou.turn < len(s) { + jiezou.turn += 1 + return false + } - if _,ok := jiezou.skipS[S]; ok {return false} + if _, ok := jiezou.skipS[S]; ok { + return false + } jiezou.Lock() - if now > 1.3 * jiezou.avg {//触发 - c.Log.Base("jiezou").L(`W: `,"节奏注意", now, jiezou.avg, S) + if now > 1.3*jiezou.avg { //触发 + c.Log.Base("jiezou").L(`W: `, "节奏注意", now, jiezou.avg, S) jiezou.avg = now //沉默 jiezou.Unlock() //发送弹幕 - if jiezou.alertdanmu != "" {Msg_senddanmu(jiezou.alertdanmu)} + if jiezou.alertdanmu != "" { + Msg_senddanmu(jiezou.alertdanmu) + } return true } jiezou.Unlock() @@ -1862,162 +2010,168 @@ func Jiezouf(s []string) bool { } //保存所有消息到json -func init(){ +func init() { Save_to_json(0, []interface{}{`[`}) c.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ - `change_room`:func(data interface{})(bool){//房间改变 + `change_room`: func(data interface{}) bool { //房间改变 Save_to_json(0, []interface{}{`[`}) return false }, - `flash_room`:func(data interface{})(bool){//房间改变 + `flash_room`: func(data interface{}) bool { //房间改变 Save_to_json(0, []interface{}{`[`}) return false }, }) } -func Save_to_json(Loc int,Context []interface{}) { - if path,ok := c.K_v.LoadV(`save_to_json`).(string);ok && path != ``{ +func Save_to_json(Loc int, Context []interface{}) { + if path, ok := c.K_v.LoadV(`save_to_json`).(string); ok && path != `` { p.File().FileWR(p.Filel{ - File:path, - Loc:int64(Loc), - Context:Context, + File: path, + Loc: int64(Loc), + Context: Context, }) } } //进入房间发送弹幕 -func Entry_danmu(){ +func Entry_danmu() { flog := flog.Base_add(`进房弹幕`) //检查与切换粉丝牌,只在cookie存在时启用 F.Get(`CheckSwitch_FansMedal`) - if v,_ := c.K_v.LoadV(`进房弹幕_有粉丝牌时才发`).(bool);v && c.Wearing_FansMedal == 0{ - flog.L(`T: `,`无粉丝牌`) + if v, _ := c.K_v.LoadV(`进房弹幕_有粉丝牌时才发`).(bool); v && c.Wearing_FansMedal == 0 { + flog.L(`T: `, `无粉丝牌`) return } - if v,_ := c.K_v.LoadV(`进房弹幕_仅发首日弹幕`).(bool);v { + if v, _ := c.K_v.LoadV(`进房弹幕_仅发首日弹幕`).(bool); v { res := F.Get_weared_medal() if res.Today_intimacy > 0 { - flog.L(`T: `,`今日已发弹幕`) + flog.L(`T: `, `今日已发弹幕`) return } } - if array,ok := c.K_v.LoadV(`进房弹幕_内容`).([]interface{});ok && len(array) != 0{ - rand := p.Rand().MixRandom(0,int64(len(array)-1)) + if array, ok := c.K_v.LoadV(`进房弹幕_内容`).([]interface{}); ok && len(array) != 0 { + rand := p.Rand().MixRandom(0, int64(len(array)-1)) send.Danmu_s(array[rand].(string), c.Roomid) } } //保持所有牌子点亮 func Keep_medal_light() { - if v,_ := c.K_v.LoadV(`保持牌子亮着`).(bool);!v { + if v, _ := c.K_v.LoadV(`保持牌子亮着`).(bool); !v { return } flog := flog.Base_add(`保持亮牌`) - array,ok := c.K_v.LoadV(`进房弹幕_内容`).([]interface{}) - if !ok || len(array) == 0{ - flog.L(`I: `,`进房弹幕_内容 为 空,退出`) + array, ok := c.K_v.LoadV(`进房弹幕_内容`).([]interface{}) + if !ok || len(array) == 0 { + flog.L(`I: `, `进房弹幕_内容 为 空,退出`) return } - flog.L(`T: `,`开始`) + flog.L(`T: `, `开始`) var hasKeep bool - for _,v := range F.Get_list_in_room() { - if t := int64(v.Last_wear_time) - time.Now().Unix();t > 60*60*24*2 || t < 0{continue}//到期时间在2天以上或已过期 + for _, v := range F.Get_list_in_room() { + if v.IsLighted == 1 { + continue + } //点亮状态 hasKeep = true - info := F.Info(v.Target_id) + info := F.Info(v.TargetID) //两天内到期,发弹幕续期 - rand := p.Rand().MixRandom(0,int64(len(array)-1)) + rand := p.Rand().MixRandom(0, int64(len(array)-1)) send.Danmu_s(array[rand].(string), info.Data.LiveRoom.Roomid) time.Sleep(time.Second) } //重试,使用历史弹幕 - for _,v := range F.Get_list_in_room() { - if t := int64(v.Last_wear_time) - time.Now().Unix();t > 60*60*24*2 || t < 0{continue}//到期时间在2天以上或已过期 + for _, v := range F.Get_list_in_room() { + if v.IsLighted == 1 { + continue + } - info := F.Info(v.Target_id) + info := F.Info(v.TargetID) //两天内到期,发弹幕续期 var Str string - for _,v := range F.GetHistory(info.Data.LiveRoom.Roomid).Data.Room{ + for _, v := range F.GetHistory(info.Data.LiveRoom.Roomid).Data.Room { if v.Text != "" { Str = v.Text break } } if Str == "" { - rand := p.Rand().MixRandom(0,int64(len(array)-1)) + rand := p.Rand().MixRandom(0, int64(len(array)-1)) Str = array[rand].(string) } - send.Danmu_s(Str,info.Data.LiveRoom.Roomid) + send.Danmu_s(Str, info.Data.LiveRoom.Roomid) time.Sleep(time.Second) } if hasKeep { - flog.L(`I: `,`完成`) + flog.L(`I: `, `完成`) } else { - flog.L(`T: `,`完成`) + flog.L(`T: `, `完成`) } } //自动发送即将过期的银瓜子礼物 func AutoSend_silver_gift() { - day,_ := c.K_v.LoadV(`发送还有几天过期的礼物`).(float64) + day, _ := c.K_v.LoadV(`发送还有几天过期的礼物`).(float64) if day <= 0 { return } - flog := flog.Base_add(`自动送礼`).L(`T: `,`开始`) + flog := flog.Base_add(`自动送礼`).L(`T: `, `开始`) - if c.UpUid == 0 {F.Get(`UpUid`)} + if c.UpUid == 0 { + F.Get(`UpUid`) + } var hasSend bool - for _,v := range F.Gift_list() { - if time.Now().Add(time.Hour * time.Duration(24 * int(day))).Unix() > int64(v.Expire_at) { + for _, v := range F.Gift_list() { + if time.Now().Add(time.Hour*time.Duration(24*int(day))).Unix() > int64(v.Expire_at) { hasSend = true send.Send_gift(v.Gift_id, v.Bag_id, v.Gift_num) } } if hasSend { - flog.L(`I: `,`完成`) + flog.L(`I: `, `完成`) } else { - flog.L(`T: `,`完成`) + flog.L(`T: `, `完成`) } } -var m4s_cache sync.Map//使用内存cache避免频繁io +var m4s_cache sync.Map //使用内存cache避免频繁io -func get_m4s_cache(path string)(buf []byte, cached bool, err error){ - if b,ok := m4s_cache.Load(path);!ok{ - f,e := os.OpenFile(path,os.O_RDONLY,0644) +func get_m4s_cache(path string) (buf []byte, cached bool, err error) { + if b, ok := m4s_cache.Load(path); !ok { + f, e := os.OpenFile(path, os.O_RDONLY, 0644) if e != nil { err = e return } defer f.Close() - if b,e := io.ReadAll(f);e != nil { + if b, e := io.ReadAll(f); e != nil { err = e return } else { buf = b - m4s_cache.Store(path,buf) - go func(){//移除 - time.Sleep(time.Second*time.Duration(savestream.m4s_hls+2)) + m4s_cache.Store(path, buf) + go func() { //移除 + time.Sleep(time.Second * time.Duration(savestream.m4s_hls+2)) m4s_cache.Delete(path) }() } } else { cached = true - buf,_ = b.([]byte) + buf, _ = b.([]byte) } return } @@ -2025,7 +2179,7 @@ func get_m4s_cache(path string)(buf []byte, cached bool, err error){ //直播Web服务口 func init() { flog := flog.Base_add(`直播Web服务`) - if port_f,ok := c.K_v.LoadV(`直播Web服务口`).(float64);ok && port_f >= 0 { + if port_f, ok := c.K_v.LoadV(`直播Web服务口`).(float64); ok && port_f >= 0 { port := int(port_f) base_dir := savestream.base_path @@ -2041,7 +2195,7 @@ func init() { Addr: addr, }) var ( - root = func(w http.ResponseWriter,r *http.Request){ + root = func(w http.ResponseWriter, r *http.Request) { //header w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Allow-Headers", "*") @@ -2050,10 +2204,10 @@ func init() { w.Header().Set("Connection", "keep-alive") w.Header().Set("Content-Transfer-Encoding", "binary") start := time.Now() - + var path string = r.URL.Path[1:] - if !p.Checkfile().IsExist(base_dir+path) { + if !p.Checkfile().IsExist(base_dir + path) { w.WriteHeader(http.StatusNotFound) return } @@ -2061,7 +2215,7 @@ func init() { if savestream.path != "" && strings.Contains(path, filepath.Base(savestream.path)) { w.Header().Set("Server", "live") if filepath.Ext(path) == `.dtmp` { - if strings.Contains(path,".flv") { + if strings.Contains(path, ".flv") { if len(savestream.front) == 0 { w.Header().Set("Retry-After", "1") w.WriteHeader(http.StatusServiceUnavailable) @@ -2073,10 +2227,12 @@ func init() { w.WriteHeader(http.StatusOK) flusher, flushSupport := w.(http.Flusher) - if flushSupport {flusher.Flush()} + if flushSupport { + flusher.Flush() + } //写入flv头,首tag - if _,err := w.Write(savestream.front);err != nil { + if _, err := w.Write(savestream.front); err != nil { return } else if flushSupport { flusher.Flush() @@ -2085,10 +2241,10 @@ func init() { cancel := make(chan struct{}) //flv流关键帧间隔切片 - savestream.stream.Pull_tag(map[string]func(interface{})(bool){ - `stream`:func(data interface{})(bool){ - if b,ok := data.([]byte);ok{ - if _,err := w.Write(b);err != nil { + savestream.stream.Pull_tag(map[string]func(interface{}) bool{ + `stream`: func(data interface{}) bool { + if b, ok := data.([]byte); ok { + if _, err := w.Write(b); err != nil { close(cancel) return true } else if flushSupport { @@ -2097,14 +2253,14 @@ func init() { } return false }, - `close`:func(data interface{})(bool){ + `close`: func(data interface{}) bool { close(cancel) return true }, }) - <- cancel - } else if strings.Contains(path,".m3u8") { + <-cancel + } else if strings.Contains(path, ".m3u8") { if r.URL.Query().Get("type") == "mp4" { if len(savestream.front) == 0 { w.Header().Set("Retry-After", "1") @@ -2117,10 +2273,12 @@ func init() { w.WriteHeader(http.StatusOK) flusher, flushSupport := w.(http.Flusher) - if flushSupport {flusher.Flush()} + if flushSupport { + flusher.Flush() + } //写入hls头 - if _,err := w.Write(savestream.front);err != nil { + if _, err := w.Write(savestream.front); err != nil { return } else if flushSupport { flusher.Flush() @@ -2129,10 +2287,10 @@ func init() { cancel := make(chan struct{}) //hls切片 - savestream.stream.Pull_tag(map[string]func(interface{})(bool){ - `stream`:func(data interface{})(bool){ - if b,ok := data.([]byte);ok{ - if _,err := w.Write(b);err != nil { + savestream.stream.Pull_tag(map[string]func(interface{}) bool{ + `stream`: func(data interface{}) bool { + if b, ok := data.([]byte); ok { + if _, err := w.Write(b); err != nil { close(cancel) return true } else if flushSupport { @@ -2141,36 +2299,36 @@ func init() { } return false }, - `close`:func(data interface{})(bool){ + `close`: func(data interface{}) bool { close(cancel) return true }, }) - <- cancel + <-cancel } else { w.Header().Set("Cache-Control", "max-age=1") w.Header().Set("Content-Type", "application/vnd.apple.mpegurl") w.Header().Set("Last-Modified", savestream.hls_stream.t.Format(http.TimeFormat)) - + // //经常m4s下载速度赶不上,使用阻塞避免频繁获取列表带来的卡顿 // if time.Now().Sub(savestream.hls_stream.t).Seconds() > 1 { // time.Sleep(time.Duration(3)*time.Second) // } - + res := savestream.hls_stream.b - + if len(res) == 0 { w.Header().Set("Retry-After", "1") w.WriteHeader(http.StatusServiceUnavailable) return } - + //Server-Timing w.Header().Set("Server-Timing", fmt.Sprintf("dur=%d", time.Since(start).Microseconds())) - - if _,err := w.Write(res);err != nil { - flog.L(`E: `,err) + + if _, err := w.Write(res); err != nil { + flog.L(`E: `, err) return } } @@ -2179,9 +2337,9 @@ func init() { w.Header().Set("Server", "live") w.Header().Set("Cache-Control", "Cache-Control:public, max-age=3600") - path = base_dir+path + path = base_dir + path - buf,cached,e := get_m4s_cache(path) + buf, cached, e := get_m4s_cache(path) if e != nil { w.Header().Set("Retry-After", "1") @@ -2190,7 +2348,7 @@ func init() { } if len(buf) == 0 { - flog.L(`W: `,`buf size 0`) + flog.L(`W: `, `buf size 0`) w.Header().Set("Retry-After", "1") w.WriteHeader(http.StatusServiceUnavailable) return @@ -2199,37 +2357,39 @@ func init() { //Server-Timing w.Header().Add("Server-Timing", fmt.Sprintf("cache=%v;dur=%d", cached, time.Since(start).Microseconds())) w.WriteHeader(http.StatusOK) - if _,err := w.Write(buf);err != nil { - flog.L(`E: `,err) + if _, err := w.Write(buf); err != nil { + flog.L(`E: `, err) return } } } else { w.Header().Set("Server", "file") - if r.URL.Query().Get("type") == "mp4" {//hls拼合 - dir := base_dir+filepath.Dir(path)+"/" - if !p.Checkfile().IsExist(dir+`0.m3u8`) { + if r.URL.Query().Get("type") == "mp4" { //hls拼合 + dir := base_dir + filepath.Dir(path) + "/" + if !p.Checkfile().IsExist(dir + `0.m3u8`) { w.WriteHeader(http.StatusNotFound) return } var m4slist []string if e := filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error { - if err != nil {return err} + if err != nil { + return err + } if filepath.Ext(info.Name()) == ".m4s" { m4slist = append(m4slist, path) } return nil - });e != nil { - flog.L(`E: `,e); + }); e != nil { + flog.L(`E: `, e) w.WriteHeader(http.StatusServiceUnavailable) return } m4slist = append(m4slist[len(m4slist)-1:], m4slist[:len(m4slist)-1]...) - for _,v := range m4slist { - f,err := os.OpenFile(v,os.O_RDONLY,0644) + for _, v := range m4slist { + f, err := os.OpenFile(v, os.O_RDONLY, 0644) if err != nil { w.WriteHeader(http.StatusServiceUnavailable) - flog.L(`E: `,err) + flog.L(`E: `, err) return } io.Copy(w, f) @@ -2237,10 +2397,10 @@ func init() { } return } - http.FileServer(http.Dir(base_dir)).ServeHTTP(w,r) + http.FileServer(http.Dir(base_dir)).ServeHTTP(w, r) } } - now = func(w http.ResponseWriter,r *http.Request){ + now = func(w http.ResponseWriter, r *http.Request) { //header w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Allow-Headers", "*") @@ -2250,25 +2410,25 @@ func init() { //最新直播流 if savestream.path == `` { - flog.L(`T: `,`还没有下载直播流-直播流为空`) + flog.L(`T: `, `还没有下载直播流-直播流为空`) w.WriteHeader(http.StatusNotFound) return } path := filepath.Base(savestream.path) - if strings.Contains(c.Live[0],"flv") { + if strings.Contains(c.Live[0], "flv") { path += ".flv.dtmp" } else { path += "/0.m3u8.dtmp" } - if !p.Checkfile().IsExist(base_dir+path) { - flog.L(`T: `,`还没有下载直播流-文件未能找到`) + if !p.Checkfile().IsExist(base_dir + path) { + flog.L(`T: `, `还没有下载直播流-文件未能找到`) w.WriteHeader(http.StatusNotFound) } else { - u, e := url.Parse("../"+path) + u, e := url.Parse("../" + path) if e != nil { - flog.L(`E: `,e) + flog.L(`E: `, e) w.Header().Set("Retry-After", "1") w.WriteHeader(http.StatusServiceUnavailable) return @@ -2284,15 +2444,51 @@ func init() { return } ) - s.Handle(map[string]func(http.ResponseWriter,*http.Request){ - `/`:root, - `/now`:now, - `/exit`:func(w http.ResponseWriter,r *http.Request){ + s.Handle(map[string]func(http.ResponseWriter, *http.Request){ + `/`: root, + `/now`: now, + `/exit`: func(w http.ResponseWriter, r *http.Request) { s.Server.Shutdown(context.Background()) }, }) host := p.Sys().GetIntranetIp() - c.Stream_url = strings.Replace(`http://`+s.Server.Addr,`0.0.0.0`,host,-1) - flog.L(`I: `,`启动于`,c.Stream_url) + c.Stream_url = strings.Replace(`http://`+s.Server.Addr, `0.0.0.0`, host, -1) + flog.L(`I: `, `启动于`, c.Stream_url) } -} \ No newline at end of file +} + +//此次直播的交互人数 +var communicate Communicate + +type Communicate struct { + Buf *psync.Map +} + +func init() { + communicate.Buf = new(psync.Map) + c.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ + `change_room`: func(data interface{}) bool { //房间改变 + communicate.Reset() + return false + }, + `flash_room`: func(data interface{}) bool { //房间改变 + communicate.Reset() + return false + }, + }) +} + +func (t *Communicate) Reset() { + t.Buf.Range(func(key, _ interface{}) bool { + t.Buf.Delete(key) + return true + }) +} + +func (t *Communicate) Count() int { + return t.Buf.Len() +} + +func (t *Communicate) Store(k interface{}) { + t.Buf.Store(k, nil) +} diff --git a/Reply/Msg.go b/Reply/Msg.go index 2e90e69..f79897e 100644 --- a/Reply/Msg.go +++ b/Reply/Msg.go @@ -2,8 +2,9 @@ package reply import ( "encoding/json" + "io/ioutil" + c "github.com/qydysky/bili_danmu/CV" - s "github.com/qydysky/part/buf" ) /* @@ -13,88 +14,93 @@ import ( var msglog = c.Log.Base(`Msg`) //Msg类型数据处理方法map -var Msg_map = map[string]func(replyF, string) { - `VOICE_JOIN_ROOM_COUNT_INFO`:replyF.voice_join_room_count_info,//连麦等待 - `VOICE_JOIN_LIST`:nil, - `VOICE_JOIN_STATUS`:replyF.voice_join_status,//连麦人状态 - `STOP_LIVE_ROOM_LIST`:nil,//停止直播的直播间 - `PK_LOTTERY_START`:replyF.pk_lottery_start,//大乱斗pk - `PK_BATTLE_PRE_NEW`:nil,//pk准备 - `PK_BATTLE_START_NEW`:nil,//pk开始 - `PK_BATTLE_PROCESS_NEW`:replyF.pk_battle_process_new,//pk进行中 - `VTR_GIFT_LOTTERY`:replyF.vtr_gift_lottery,//特别礼物 - `ENTRY_EFFECT_MUST_RECEIVE`:nil,//高能榜前三进入 - `GIFT_BAG_DOT`:nil, - `LITTLE_MESSAGE_BOX`:replyF.little_message_box,//小消息 - `MESSAGEBOX_USER_MEDAL_CHANGE`:replyF.messagebox_user_medal_change,//粉丝牌切换 - `HOT_RANK_SETTLEMENT`:replyF.hot_rank_settlement,//热门榜获得 - `HOT_RANK_CHANGED`:replyF.hot_rank_changed,//热门榜变动 - `CARD_MSG`:nil,//提示关注 - `WIDGET_BANNER`:nil,//每日任务 - `ROOM_ADMINS`:nil,//房管列表 - `ONLINE_RANK_TOP3`:nil, - `ONLINE_RANK_COUNT`:nil, - `ONLINE_RANK_V2`:nil, - "TRADING_SCORE":nil,//每日任务 - "MATCH_ROOM_CONF":nil,//赛事房间配置 - "HOT_ROOM_NOTIFY":nil,//热点房间 - "MATCH_TEAM_GIFT_RANK":nil,//赛事人气比拼 - "ACTIVITY_MATCH_GIFT":nil,//赛事礼物 - "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":replyF.anchor_lot_start,//天选之人开始 - "ANCHOR_LOT_CHECKSTATUS":nil, - "ANCHOR_LOT_END":nil,//天选之人结束 - "ANCHOR_LOT_AWARD":replyF.anchor_lot_award,//天选之人获奖 - "COMBO_SEND":nil, - "INTERACT_WORD":replyF.interact_word,//进入信息,包含直播间关注提示 - "ACTIVITY_BANNER_UPDATE_V2":nil, - "NOTICE_MSG":nil, - "ROOM_BANNER":nil, - "ONLINERANK":nil, - "WELCOME":nil, - "HOUR_RANK_AWARDS":nil, - "ROOM_RANK":nil, - "ROOM_SHIELD":nil, - "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":nil,//replyF.welcome_guard,//大航海进入 ?已废弃? - "DANMU_MSG":replyF.danmu,//弹幕 - "DANMU_MSG:4:0:2:2:2:0":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_ENTRANCE":nil,//SC入口 - "SUPER_CHAT_MESSAGE_DELETE":nil,//SC删除 - "SUPER_CHAT_MESSAGE":nil,//replyF.super_chat_message,//SC - "SUPER_CHAT_MESSAGE_JPN":replyF.super_chat_message,//SC - "PANEL":nil,//replyF.panel,//排行榜 被HOT_RANK_CHANGED替代 - "ENTRY_EFFECT":replyF.entry_effect,//进入特效 - "ROOM_REAL_TIME_MESSAGE_UPDATE":nil,//replyF.roominfo,//粉丝数 +var Msg_map = map[string]func(replyF, string){ + `VOICE_JOIN_ROOM_COUNT_INFO`: replyF.voice_join_room_count_info, //连麦等待 + `VOICE_JOIN_LIST`: nil, + `VOICE_JOIN_STATUS`: replyF.voice_join_status, //连麦人状态 + `STOP_LIVE_ROOM_LIST`: nil, //停止直播的直播间 + `PK_LOTTERY_START`: replyF.pk_lottery_start, //大乱斗pk + `PK_BATTLE_PRE_NEW`: nil, //pk准备 + `PK_BATTLE_START_NEW`: nil, //pk开始 + `PK_BATTLE_PROCESS_NEW`: replyF.pk_battle_process_new, //pk进行中 + `VTR_GIFT_LOTTERY`: replyF.vtr_gift_lottery, //特别礼物 + `ENTRY_EFFECT_MUST_RECEIVE`: nil, //高能榜前三进入 + `GIFT_BAG_DOT`: nil, + `LITTLE_MESSAGE_BOX`: replyF.little_message_box, //小消息 + `MESSAGEBOX_USER_MEDAL_CHANGE`: replyF.messagebox_user_medal_change, //粉丝牌切换 + `HOT_RANK_SETTLEMENT`: replyF.hot_rank_settlement, //热门榜获得 + `HOT_RANK_CHANGED`: replyF.hot_rank_changed, //热门榜变动 + `CARD_MSG`: nil, //提示关注 + `WIDGET_BANNER`: nil, //每日任务 + `ROOM_ADMINS`: nil, //房管列表 + `ONLINE_RANK_TOP3`: nil, + `ONLINE_RANK_COUNT`: nil, + `ONLINE_RANK_V2`: nil, + "TRADING_SCORE": nil, //每日任务 + "MATCH_ROOM_CONF": nil, //赛事房间配置 + "HOT_ROOM_NOTIFY": nil, //热点房间 + "MATCH_TEAM_GIFT_RANK": nil, //赛事人气比拼 + "ACTIVITY_MATCH_GIFT": nil, //赛事礼物 + "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": replyF.anchor_lot_start, //天选之人开始 + "ANCHOR_LOT_CHECKSTATUS": nil, + "ANCHOR_LOT_END": nil, //天选之人结束 + "ANCHOR_LOT_AWARD": replyF.anchor_lot_award, //天选之人获奖 + "COMBO_SEND": nil, + "INTERACT_WORD": replyF.interact_word, //进入信息,包含直播间关注提示 + "ACTIVITY_BANNER_UPDATE_V2": nil, + "NOTICE_MSG": nil, + "ROOM_BANNER": nil, + "ONLINERANK": nil, + "WELCOME": nil, + "HOUR_RANK_AWARDS": nil, + "ROOM_RANK": nil, + "ROOM_SHIELD": nil, + "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": nil, //replyF.welcome_guard,//大航海进入 ?已废弃? + "DANMU_MSG": replyF.danmu, //弹幕 + "DANMU_MSG:4:0:2:2:2:0": 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_ENTRANCE": nil, //SC入口 + "SUPER_CHAT_MESSAGE_DELETE": nil, //SC删除 + "SUPER_CHAT_MESSAGE": nil, //replyF.super_chat_message,//SC + "SUPER_CHAT_MESSAGE_JPN": replyF.super_chat_message, //SC + "PANEL": nil, //replyF.panel,//排行榜 被HOT_RANK_CHANGED替代 + "ENTRY_EFFECT": replyF.entry_effect, //进入特效 + "ROOM_REAL_TIME_MESSAGE_UPDATE": nil, //replyF.roominfo,//粉丝数 + "WATCHED_CHANGE": replyF.watched_change, //Msg-观看人数 } //屏蔽不需要的消息 -func init(){ - {//加载不需要的消息 - buf := s.New() - buf.Load("config/config_disable_msg.json") - for k,v := range buf.B { - if able,ok := v.(bool);ok {//设置为true时,使用默认显示 +func init() { + { //加载不需要的消息 + bb, err := ioutil.ReadFile("config/config_disable_msg.json") + if err != nil { + return + } + var buf map[string]interface{} + json.Unmarshal(bb, &buf) + for k, v := range buf { + if able, ok := v.(bool); ok { //设置为true时,使用默认显示 if able { Msg_map[k] = replyF.defaultMsg } else { @@ -110,18 +116,20 @@ func init(){ func Msg(b []byte) { msglog := msglog.Base_add(`select func`) - var tmp struct{ + var tmp struct { Cmd string `json:"cmd"` } - if e := json.Unmarshal(b, &tmp);e != nil { - msglog.L(`E: `,e) + if e := json.Unmarshal(b, &tmp); e != nil { + msglog.L(`E: `, e) return } if F, ok := Msg_map[tmp.Cmd]; ok { - if F != nil {F(replyF{}, string(b))} + if F != nil { + F(replyF{}, string(b)) + } } else { (replyF{}).defaultMsg(string(b)) } - return + return } diff --git a/Reply/Reply.go b/Reply/Reply.go index f009291..3a2810c 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -1,20 +1,20 @@ package reply import ( - "fmt" - "time" "bytes" - "strings" - "strconv" "compress/zlib" "encoding/json" + "fmt" + "strconv" + "strings" + "time" - p "github.com/qydysky/part" - mq "github.com/qydysky/part/msgq" + c "github.com/qydysky/bili_danmu/CV" F "github.com/qydysky/bili_danmu/F" ws_msg "github.com/qydysky/bili_danmu/Reply/ws_msg" send "github.com/qydysky/bili_danmu/Send" - c "github.com/qydysky/bili_danmu/CV" + p "github.com/qydysky/part" + mq "github.com/qydysky/part/msgq" ) var reply_log = c.Log.Base(`Reply`) @@ -25,32 +25,50 @@ var reply_log = c.Log.Base(`Reply`) func Reply(b []byte) { reply_log := reply_log.Base_add(`返回分派`) - if len(b) <= c.WS_PACKAGE_HEADER_TOTAL_LENGTH {reply_log.L(`W: `,"包缺损");return} + if len(b) <= c.WS_PACKAGE_HEADER_TOTAL_LENGTH { + reply_log.L(`W: `, "包缺损") + return + } head := F.HeadChe(b[:c.WS_PACKAGE_HEADER_TOTAL_LENGTH]) - if int(head.PackL) > len(b) {reply_log.L(`E: `,"包缺损");return} + if int(head.PackL) > len(b) { + reply_log.L(`E: `, "首包缺损") + return + } if head.BodyV == c.WS_BODY_PROTOCOL_VERSION_DEFLATE { readc, err := zlib.NewReader(bytes.NewReader(b[16:])) - if err != nil {reply_log.L(`E: `,"解压错误");return} + if err != nil { + reply_log.L(`E: `, "解压错误") + return + } defer readc.Close() - + buf := bytes.NewBuffer(nil) - if _, err := buf.ReadFrom(readc);err != nil {reply_log.L(`E: `,"解压错误");return} + if _, err := buf.ReadFrom(readc); err != nil { + reply_log.L(`E: `, "解压错误") + return + } b = buf.Bytes() } for len(b) != 0 { head := F.HeadChe(b[:c.WS_PACKAGE_HEADER_TOTAL_LENGTH]) - if int(head.PackL) > len(b) {reply_log.L(`E: `,"包缺损");return} - + if int(head.PackL) > len(b) { + reply_log.L(`E: `, "包缺损") + return + } + contain := b[c.WS_PACKAGE_HEADER_TOTAL_LENGTH:head.PackL] switch head.OpeaT { case c.WS_OP_MESSAGE: Msg(contain) - Save_to_json(-1, []interface{}{contain,`,`}) - case c.WS_OP_HEARTBEAT_REPLY:Heart(contain) - default :reply_log.L(`W: `,"unknow reply", contain) + Save_to_json(-1, []interface{}{contain, `,`}) + case c.WS_OP_HEARTBEAT_REPLY: //心跳响应 + Heart(contain) + return //忽略剩余内容 + default: + reply_log.L(`W: `, "unknow reply", contain) } b = b[head.PackL:] @@ -59,123 +77,127 @@ func Reply(b []byte) { //所有的json对象处理子函数类 //包含Msg和HeartBeat两大类 -type replyF struct {} +type replyF struct{} //默认未识别Msg -func (replyF) defaultMsg(s string){ +func (replyF) defaultMsg(s string) { msglog.Base_add("Unknow").L(`E: `, s) } //大乱斗pk开始 -func (replyF) pk_lottery_start(s string){ +func (replyF) pk_lottery_start(s string) { msglog := msglog.Base_add("大乱斗") var j ws_msg.PK_LOTTERY_START - if e := json.Unmarshal([]byte(s), &j);e != nil{ + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) return } - Gui_show(j.Data.Title,`0room`) + Gui_show(j.Data.Title, `0room`) msglog.L(`I: `, j.Data.Title) } //连麦人状态 -func (replyF) voice_join_status(s string){ +func (replyF) voice_join_status(s string) { msglog := msglog.Base_add("连麦") var j ws_msg.VOICE_JOIN_STATUS - if e := json.Unmarshal([]byte(s), &j);e != nil{ + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) return } - if j.Data.UserName == `` {return} + if j.Data.UserName == `` { + return + } - Gui_show(`连麦中:`+j.Data.UserName,`0room`) + Gui_show(`连麦中:`+j.Data.UserName, `0room`) msglog.L(`I: `, `连麦中:`, j.Data.UserName) } //连麦等待 -func (replyF) voice_join_room_count_info(s string){ +func (replyF) voice_join_room_count_info(s string) { msglog := msglog.Base_add("连麦") var j ws_msg.VOICE_JOIN_ROOM_COUNT_INFO - if e := json.Unmarshal([]byte(s), &j);e != nil{ + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) return } - Gui_show(`连麦等待:`+strconv.Itoa(j.Data.ApplyCount),`0room`) + Gui_show(`连麦等待:`+strconv.Itoa(j.Data.ApplyCount), `0room`) msglog.L(`I: `, `连麦等待人数`, j.Data.ApplyCount) } //大乱斗pk状态 -func (replyF) pk_battle_process_new(s string){ +func (replyF) pk_battle_process_new(s string) { msglog := msglog.Base_add("大乱斗") var j ws_msg.PK_BATTLE_PROCESS_NEW - if e := json.Unmarshal([]byte(s), &j);e != nil{ + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) return } - if diff := j.Data.InitInfo.Votes-j.Data.MatchInfo.Votes;diff<0 { - Gui_show(`大乱斗落后`,strconv.Itoa(diff),`0room`) - msglog.L(`I: `, `大乱斗落后`,diff) + if diff := j.Data.InitInfo.Votes - j.Data.MatchInfo.Votes; diff < 0 { + Gui_show(`大乱斗落后`, strconv.Itoa(diff), `0room`) + msglog.L(`I: `, `大乱斗落后`, diff) } } //msg-特别礼物 -func (replyF) vtr_gift_lottery(s string){ +func (replyF) vtr_gift_lottery(s string) { msglog := msglog.Base_add("特别礼物") var j ws_msg.VTR_GIFT_LOTTERY - if e := json.Unmarshal([]byte(s), &j);e != nil{ + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) return } - {//语言tts - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{ - uid:`0room`, - m:map[string]string{ - `{msg}`:j.Data.InteractMsg, + { //语言tts + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ + uid: `0room`, + m: map[string]string{ + `{msg}`: j.Data.InteractMsg, }, }) } - Gui_show(j.Data.InteractMsg,`0room`) + Gui_show(j.Data.InteractMsg, `0room`) msglog.L(`I: `, j.Data.InteractMsg) } //msg-直播间进入信息,此处用来提示关注 -func (replyF) interact_word(s string){ - msg_type := p.Json().GetValFromS(s, "data.msg_type"); - if v,ok := msg_type.(float64);!ok || v < 2 {return}//关注时为2,进入时为1 - uname := p.Json().GetValFromS(s, "data.uname"); - if v,ok := uname.(string);ok { - {//语言tts - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{ - uid:`0follow`, - msg:fmt.Sprint(v + `关注了直播间`), +func (replyF) interact_word(s string) { + msg_type := p.Json().GetValFromS(s, "data.msg_type") + if v, ok := msg_type.(float64); !ok || v < 2 { + return + } //关注时为2,进入时为1 + uname := p.Json().GetValFromS(s, "data.uname") + if v, ok := uname.(string); ok { + { //语言tts + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ + uid: `0follow`, + msg: fmt.Sprint(v + `关注了直播间`), }) } - Gui_show(v + `关注了直播间`,`0follow`) - msglog.Base_add("房").Log_show_control(false).L(`I`, v + `关注了直播间`) + Gui_show(v+`关注了直播间`, `0follow`) + msglog.Base_add("房").Log_show_control(false).L(`I`, v+`关注了直播间`) } } //Msg-天选之人开始 -func (replyF) anchor_lot_start(s string){ - award_name := p.Json().GetValFromS(s, "data.award_name"); +func (replyF) anchor_lot_start(s string) { + award_name := p.Json().GetValFromS(s, "data.award_name") var sh = []interface{}{">天选"} if award_name != nil { sh = append(sh, award_name, "开始") } - {//额外 ass + { //额外 ass Assf(fmt.Sprintln("天选之人", award_name, "开始")) } fmt.Println(sh...) - Gui_show(Itos(sh),`0tianxuan`) + Gui_show(Itos(sh), `0tianxuan`) msglog.Base_add("房").Log_show_control(false).L(`I`, sh...) } //Msg-天选之人结束 -func (replyF) anchor_lot_award(s string){ - award_name := p.Json().GetValFromS(s, "data.award_name"); - award_users := p.Json().GetValFromS(s, "data.award_users"); +func (replyF) anchor_lot_award(s string) { + award_name := p.Json().GetValFromS(s, "data.award_name") + award_users := p.Json().GetValFromS(s, "data.award_users") var sh = []interface{}{">天选"} @@ -183,11 +205,11 @@ func (replyF) anchor_lot_award(s string){ sh = append(sh, award_name, "获奖[") } if award_users != nil { - for _,v := range award_users.([]interface{}) { - uname := p.Json().GetValFrom(v, "uname"); - uid := p.Json().GetValFrom(v, "uid"); + for _, v := range award_users.([]interface{}) { + uname := p.Json().GetValFrom(v, "uname") + uid := p.Json().GetValFrom(v, "uid") if uname != nil && uid != nil { - if v,ok := uid.(float64);ok {//uid可能为float型 + if v, ok := uid.(float64); ok { //uid可能为float型 sh = append(sh, uname, "(", strconv.Itoa(int(v)), ")") } else { sh = append(sh, uname, "(", uid, ")") @@ -196,21 +218,21 @@ func (replyF) anchor_lot_award(s string){ } } sh = append(sh, "]") - {//额外 ass + { //额外 ass Assf(fmt.Sprintln("天选之人", award_name, "结束")) } fmt.Println(sh...) - Gui_show(Itos(sh),`0tianxuan`) + Gui_show(Itos(sh), `0tianxuan`) msglog.Base_add("房").Log_show_control(false).L(`I`, sh...) } //msg-通常是大航海购买续费 -func (replyF) user_toast_msg(s string){ +func (replyF) user_toast_msg(s string) { msglog := msglog.Base_add("礼") var j ws_msg.USER_TOAST_MSG - if e := json.Unmarshal([]byte(s), &j);e != nil{ + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) return } @@ -255,35 +277,35 @@ func (replyF) user_toast_msg(s string){ sh = append(sh, role_name) } if price != 0 { - sh_log = append(sh, "¥", price / 1000)//不在界面显示价格 - c.Danmu_Main_mq.Push_tag(`c.Rev_add`,float64(price) / 1000) - } - {//语言tts - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:`0buyguide`, - m:map[string]string{ - `{username}`:username, - `{op_name}`:op_name, - `{role_name}`:role_name, - `{num}`:strconv.Itoa(num), - `{unit}`:unit, + sh_log = append(sh, "¥", price/1000) //不在界面显示价格 + c.Danmu_Main_mq.Push_tag(`c.Rev_add`, float64(price)/1000) + } + { //语言tts + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: `0buyguide`, + m: map[string]string{ + `{username}`: username, + `{op_name}`: op_name, + `{role_name}`: role_name, + `{num}`: strconv.Itoa(num), + `{unit}`: unit, }, }) } - {//额外 ass 私信 + { //额外 ass 私信 Assf(fmt.Sprintln(sh...)) - c.Danmu_Main_mq.Push_tag(`guard_update`,nil)//使用连续付费的新舰长无法区分,刷新舰长数 + c.Danmu_Main_mq.Push_tag(`guard_update`, nil) //使用连续付费的新舰长无法区分,刷新舰长数 if uid != 0 { - c.Danmu_Main_mq.Push_tag(`pm`,send.Pm_item{ - Uid:uid, - Msg:c.K_v.LoadV(`上舰私信`).(string), - })//上舰私信 + c.Danmu_Main_mq.Push_tag(`pm`, send.Pm_item{ + Uid: uid, + Msg: c.K_v.LoadV(`上舰私信`).(string), + }) //上舰私信 } if c.K_v.LoadV(`额外私信对象`).(float64) != 0 { - c.Danmu_Main_mq.Push_tag(`pm`,send.Pm_item{ - Uid:int(c.K_v.LoadV(`额外私信对象`).(float64)), - Msg:c.K_v.LoadV(`上舰私信(额外)`).(string), - })//上舰私信-对额外 + c.Danmu_Main_mq.Push_tag(`pm`, send.Pm_item{ + Uid: int(c.K_v.LoadV(`额外私信对象`).(float64)), + Msg: c.K_v.LoadV(`上舰私信(额外)`).(string), + }) //上舰私信-对额外 } } fmt.Println("\n====") @@ -294,17 +316,20 @@ func (replyF) user_toast_msg(s string){ Gui_show(Itos(sh), "0buyguide") // Gui_show("====\n") - msglog.Log_show_control(false).L(`I: `,sh_log...) + msglog.Log_show_control(false).L(`I: `, sh_log...) } //HeartBeat-心跳用来传递人气值 var ( - renqi_old int + renqi_old int continuity int ) -func (replyF) heartbeat(s int){ - c.Danmu_Main_mq.Push_tag(`c.Renqi`,s)//使用连续付费的新舰长无法区分,刷新舰长数 - if s == 1 {return}//人气为1,不输出 + +func (replyF) heartbeat(s int) { + c.Danmu_Main_mq.Push_tag(`c.Renqi`, s) //使用连续付费的新舰长无法区分,刷新舰长数 + if s == 1 { + return + } //人气为1,不输出 var ( tmp string ) @@ -312,45 +337,53 @@ func (replyF) heartbeat(s int){ if s > renqi_old { tmp += `+` } - tmp += fmt.Sprintf("%.1f%%",100*float64(s - renqi_old)/float64(renqi_old)) + tmp += fmt.Sprintf("%.1f%%", 100*float64(s-renqi_old)/float64(renqi_old)) if s > renqi_old { continuity += 1 if continuity > 2 { - tmp = tmp+` 连续上升`+strconv.Itoa(continuity) + tmp = tmp + ` 连续上升` + strconv.Itoa(continuity) } else if continuity < 0 { continuity = 1 } } else if s < renqi_old { continuity -= 1 if continuity < -2 { - tmp = tmp+` 连续下降`+strconv.Itoa(-continuity) + tmp = tmp + ` 连续下降` + strconv.Itoa(-continuity) } else if continuity > 0 { continuity = -1 } } - tmp = `(`+tmp+`)` + tmp = `(` + tmp + `)` } if renqi_old != s { fmt.Printf("\t人气:%d %s\n", s, tmp) } - reply_log.Base_add(`人气`).Log_show_control(false).L(`I: `,"当前人气", s) + reply_log.Base_add(`人气`).Log_show_control(false).L(`I: `, "当前人气", s) renqi_old = s } //Msg-房间特殊活动 -func (replyF) win_activity(s string){ - title := p.Json().GetValFromS(s, "data.title"); +func (replyF) win_activity(s string) { + title := p.Json().GetValFromS(s, "data.title") fmt.Println("活动", title, "已开启") - msglog.Base_add("房").Log_show_control(false).L(`I: `,"活动", title, "已开启") + msglog.Base_add("房").Log_show_control(false).L(`I: `, "活动", title, "已开启") +} + +//Msg-观看人数 +func (replyF) watched_change(s string) { + var data ws_msg.WATCHED_CHANGE + json.Unmarshal([]byte(s), &data) + fmt.Printf("\t观看人数:%d\n", data.Data.Num) + msglog.Base_add("房").Log_show_control(false).L(`I: `, "观看人数", data.Data.Num) } //Msg-特殊礼物,当前仅观察到节奏风暴 -func (replyF) special_gift(s string){ - - content := p.Json().GetValFromS(s, "data.39.content"); - action := p.Json().GetValFromS(s, "data.39.action"); +func (replyF) special_gift(s string) { + + content := p.Json().GetValFromS(s, "data.39.content") + action := p.Json().GetValFromS(s, "data.39.action") var sh []interface{} @@ -360,7 +393,7 @@ func (replyF) special_gift(s string){ if content != nil { sh = append(sh, "节奏风暴", content) } - {//额外 + { //额外 Assf(fmt.Sprintln(sh...)) } fmt.Println("\n====") @@ -376,10 +409,10 @@ 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"); - price := p.Json().GetValFromS(s, "data.price"); +func (replyF) guard_buy(s string) { + username := p.Json().GetValFromS(s, "data.username") + gift_name := p.Json().GetValFromS(s, "data.gift_name") + price := p.Json().GetValFromS(s, "data.price") var sh []interface{} var sh_log []interface{} @@ -391,9 +424,9 @@ func (replyF) guard_buy(s string){ sh = append(sh, "购买了", gift_name) } if price != nil { - sh_log = append(sh, "¥", int(price.(float64)) / 1000)//不在界面显示价格 + sh_log = append(sh, "¥", int(price.(float64))/1000) //不在界面显示价格 } - {//额外 ass + { //额外 ass Assf(fmt.Sprintln(sh...)) } fmt.Println("\n====") @@ -404,11 +437,11 @@ func (replyF) guard_buy(s string){ } //Msg-房间信息改变,标题等 -func (replyF) room_change(s string){ - title := p.Json().GetValFromS(s, "data.title"); - area_name := p.Json().GetValFromS(s, "data.area_name"); +func (replyF) room_change(s string) { + title := p.Json().GetValFromS(s, "data.title") + area_name := p.Json().GetValFromS(s, "data.area_name") - var sh = []interface{}{"房间改变"} + var sh = []interface{}{"房间改变"} if title != nil { sh = append(sh, title) @@ -423,7 +456,7 @@ func (replyF) room_change(s string){ } //Msg-大航海欢迎信息 或已废弃 -func (replyF) welcome_guard(s string){ +func (replyF) welcome_guard(s string) { // username := p.Json().GetValFromS(s, "data.username"); // guard_level := p.Json().GetValFromS(s, "data.guard_level"); // img := "0default" @@ -455,16 +488,18 @@ func (replyF) welcome_guard(s string){ } //Msg-礼物处理 -func (replyF) send_gift(s string){ +func (replyF) send_gift(s string) { msglog := msglog.Base_add("礼").Log_show_control(false) var j ws_msg.SEND_GIFT - if e := json.Unmarshal([]byte(s), &j);e != nil { + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) } //忽略银瓜子 - if j.Data.CoinType == "silver" {return} + if j.Data.CoinType == "silver" { + return + } num := j.Data.Num uname := j.Data.Uname @@ -483,34 +518,39 @@ func (replyF) send_gift(s string){ if total_coin != 0 { allprice = float64(total_coin) / 1000 - sh_log = append(sh, fmt.Sprintf("¥%.1f",allprice))//不在界面显示价格 - c.Danmu_Main_mq.Push_tag(`c.Rev_add`,allprice) + sh_log = append(sh, fmt.Sprintf("¥%.1f", allprice)) //不在界面显示价格 + c.Danmu_Main_mq.Push_tag(`c.Rev_add`, allprice) } - if len(sh) == 0 {return} + if len(sh) == 0 { + return + } //小于设定 { var tmp = 20.0 - if v,ok := c.K_v.Load(`弹幕_礼物金额显示阈值`);ok { + if v, ok := c.K_v.Load(`弹幕_礼物金额显示阈值`); ok { tmp = v.(float64) } - if allprice < tmp {msglog.L(`T: `, sh_log...);return} - msglog.L(`I: `, sh_log...); + if allprice < tmp { + msglog.L(`T: `, sh_log...) + return + } + msglog.L(`I: `, sh_log...) } - {//语言tts - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:`0gift`, - m:map[string]string{ - `{num}`:strconv.Itoa(num), - `{uname}`:uname, - `{action}`:action, - `{giftName}`:giftName, + { //语言tts + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: `0gift`, + m: map[string]string{ + `{num}`: strconv.Itoa(num), + `{uname}`: uname, + `{action}`: action, + `{giftName}`: giftName, }, }) } - {//额外 + { //额外 Assf(fmt.Sprintln(sh...)) } fmt.Println("\n====") @@ -524,7 +564,7 @@ func (replyF) send_gift(s string){ //Msg-房间封禁信息 func (replyF) room_block_msg(s string) { - if uname := p.Json().GetValFromS(s, "uname");uname == nil { + if uname := p.Json().GetValFromS(s, "uname"); uname == nil { msglog.L(`E: `, "uname", uname) return } else { @@ -538,11 +578,11 @@ func (replyF) room_block_msg(s string) { func (replyF) preparing(s string) { msglog := msglog.Base_add("房") - if roomid := p.Json().GetValFromS(s, "roomid");roomid == nil { + if roomid := p.Json().GetValFromS(s, "roomid"); roomid == nil { msglog.L(`E: `, "roomid", roomid) return } else { - {//附加功能 obs结束 `savestream`结束 + { //附加功能 obs结束 `savestream`结束 Obs_R(false) Obsf(false) Savestream_wait() @@ -563,18 +603,18 @@ func (replyF) preparing(s string) { func (replyF) live(s string) { msglog := msglog.Base_add("房") - if roomid := p.Json().GetValFromS(s, "roomid");roomid == nil { + if roomid := p.Json().GetValFromS(s, "roomid"); roomid == nil { msglog.L(`E: `, "roomid", roomid) return } else { - {//附加功能 obs录播 + { //附加功能 obs录播 Obsf(true) Obs_R(true) go Savestreamf() } { - c.Rev = 0.0 //营收 - c.Liveing = true //直播i标志 + c.Rev = 0.0 //营收 + c.Liveing = true //直播i标志 c.Live_Start_Time = time.Now() //开播h时间 } if p.Sys().Type(roomid) == "float64" { @@ -589,22 +629,30 @@ func (replyF) live(s string) { //Msg-超级留言处理 var sc_buf = make(map[string]struct{}) -func (replyF) super_chat_message(s string){ + +func (replyF) super_chat_message(s string) { msglog := msglog.Base_add("礼") var j ws_msg.SUPER_CHAT_MESSAGE_JPN - if e := json.Unmarshal([]byte(s), &j);e != nil { + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) } id := j.Data.ID if id != "" { - if _,ok := sc_buf[id];ok{return} + if _, ok := sc_buf[id]; ok { + return + } if len(sc_buf) >= 10 { - for k,_ := range sc_buf {delete(sc_buf, k);break} - {//copy map + for k, _ := range sc_buf { + delete(sc_buf, k) + break + } + { //copy map tmp := make(map[string]struct{}) - for k,v := range sc_buf {tmp[k] = v} + for k, v := range sc_buf { + tmp[k] = v + } sc_buf = tmp } } @@ -620,9 +668,9 @@ func (replyF) super_chat_message(s string){ sh = append(sh, uname) logg := sh if price != 0 { - sh = append(sh, "\n")//界面不显示价格 + sh = append(sh, "\n") //界面不显示价格 logg = append(logg, "¥", price) - c.Danmu_Main_mq.Push_tag(`c.Rev_add`,float64(price)) + c.Danmu_Main_mq.Push_tag(`c.Rev_add`, float64(price)) } fmt.Println("====") fmt.Println(sh...) @@ -633,14 +681,14 @@ func (replyF) super_chat_message(s string){ sh = append(sh, message) logg = append(logg, message) } - {//语言tts - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:`0superchat`, - m:map[string]string{ - `{uname}`:uname, - `{price}`:strconv.Itoa(price), - `{message}`:message, - `{message_jpn}`:message_jpn, + { //语言tts + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: `0superchat`, + m: map[string]string{ + `{uname}`: uname, + `{price}`: strconv.Itoa(price), + `{message}`: message, + `{message_jpn}`: message_jpn, }, }) } @@ -651,8 +699,8 @@ func (replyF) super_chat_message(s string){ logg = append(logg, message_jpn) } fmt.Print("====\n\n") - - {//额外 + + { //额外 Assf(fmt.Sprintln(sh...)) // Gui_show("====\n") Gui_show(Itos(sh), "0superchat") @@ -661,25 +709,27 @@ func (replyF) super_chat_message(s string){ } //Msg-分区排行 使用热门榜替代 -func (replyF) panel(s string){ +func (replyF) panel(s string) { msglog := msglog.Base_add("房").Log_show_control(false) - if note := p.Json().GetValFromS(s, "data.note");note == nil { + if note := p.Json().GetValFromS(s, "data.note"); note == nil { msglog.L(`E: `, "note", note) return } else { - if v,ok := note.(string);ok{c.Note = v} + if v, ok := note.(string); ok { + c.Note = v + } fmt.Println("排行", note) msglog.L(`I: `, "排行", note) } } //Msg-热门榜变动 -func (replyF) hot_rank_changed(s string){ +func (replyF) hot_rank_changed(s string) { msglog := msglog.Base_add("房").Log_show_control(false) var type_item ws_msg.HOT_RANK_CHANGED - if e := json.Unmarshal([]byte(s), &type_item);e != nil { + if e := json.Unmarshal([]byte(s), &type_item); e != nil { msglog.L(`E: `, e) } if type_item.Data.Area_name != `` { @@ -695,11 +745,11 @@ func (replyF) hot_rank_changed(s string){ } //Msg-热门榜获得 -func (replyF) hot_rank_settlement(s string){ +func (replyF) hot_rank_settlement(s string) { msglog := msglog.Base_add("房") var type_item ws_msg.HOT_RANK_SETTLEMENT - if e := json.Unmarshal([]byte(s), &type_item);e != nil { + if e := json.Unmarshal([]byte(s), &type_item); e != nil { msglog.L(`E: `, e) } var tmp = `获得:` @@ -710,38 +760,38 @@ func (replyF) hot_rank_settlement(s string){ tmp += strconv.Itoa(type_item.Data.Rank) } Gui_show(tmp, "0rank") - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:"0rank", - m:map[string]string{ - `{Area_name}`:type_item.Data.Area_name, - `{Rank}`:strconv.Itoa(type_item.Data.Rank), + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: "0rank", + m: map[string]string{ + `{Area_name}`: type_item.Data.Area_name, + `{Rank}`: strconv.Itoa(type_item.Data.Rank), }, }) msglog.L(`I: `, "热门榜", tmp) } //Msg-小消息 -func (replyF) little_message_box(s string){ +func (replyF) little_message_box(s string) { msglog := msglog.Base_add("系统") var type_item ws_msg.LITTLE_MESSAGE_BOX - if e := json.Unmarshal([]byte(s), &type_item);e != nil { + if e := json.Unmarshal([]byte(s), &type_item); e != nil { msglog.L(`E: `, e) } if type_item.Data.Msg != `` { msglog.L(`I: `, type_item.Data.Msg) - if strings.Contains(type_item.Data.Msg,`小心心`) && strings.Contains(type_item.Data.Msg,`上限`) { - F.F_x25Kn_cancel() + if strings.Contains(type_item.Data.Msg, `小心心`) && strings.Contains(type_item.Data.Msg, `上限`) { + F.F_x25Kn_cancel() } } } //Msg-粉丝牌切换 -func (replyF) messagebox_user_medal_change(s string){ +func (replyF) messagebox_user_medal_change(s string) { msglog := msglog.Base_add("房") var type_item ws_msg.MESSAGEBOX_USER_MEDAL_CHANGE - if e := json.Unmarshal([]byte(s), &type_item);e != nil { + if e := json.Unmarshal([]byte(s), &type_item); e != nil { msglog.L(`E: `, e) } if type_item.Data.Medal_name != `` { @@ -750,22 +800,22 @@ func (replyF) messagebox_user_medal_change(s string){ } //Msg-进入特效 -func (replyF) entry_effect(s string){ +func (replyF) entry_effect(s string) { - var res struct{ - Data struct{ + var res struct { + Data struct { Copy_writing string `json:"copy_writing"` } `json:"data"` } - if e := json.Unmarshal([]byte(s), &res);e != nil { + if e := json.Unmarshal([]byte(s), &res); e != nil { msglog.L(`E: `, e) } - + var username string op := strings.Index(res.Data.Copy_writing, ` <%`) ed := strings.Index(res.Data.Copy_writing, `%> `) if op != -1 && ed != -1 { - username = res.Data.Copy_writing[op+3:ed] + username = res.Data.Copy_writing[op+3 : ed] } //处理特殊字符 copy_writing := strings.ReplaceAll(res.Data.Copy_writing, `<%`, ``) @@ -784,13 +834,13 @@ func (replyF) entry_effect(s string){ img = "0level3" } - {//语言tts - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:img, - m:map[string]string{ - `{guard_name}`:guard_name, - `{username}`:username, - `{msg}`:copy_writing, + { //语言tts + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: img, + m: map[string]string{ + `{guard_name}`: guard_name, + `{username}`: username, + `{msg}`: copy_writing, }, }) } @@ -802,22 +852,24 @@ func (replyF) entry_effect(s string){ } //Msg-房间禁言 -func (replyF) roomsilent(s string){ +func (replyF) roomsilent(s string) { msglog := msglog.Base_add("房") - if level := p.Json().GetValFromS(s, "data.level");level == nil { + if level := p.Json().GetValFromS(s, "data.level"); level == nil { msglog.L(`E: `, "level", level) return } else { - if level.(float64) == 0 {msglog.L(`I: `, "主播关闭了禁言")} + if level.(float64) == 0 { + msglog.L(`I: `, "主播关闭了禁言") + } msglog.L(`I: `, "主播开启了等级禁言:", level) } } //Msg-粉丝信息,常刷屏,不显示 -func (replyF) roominfo(s string){ - fans := p.Json().GetValFromS(s, "data.fans"); - fans_club := p.Json().GetValFromS(s, "data.fans_club"); +func (replyF) roominfo(s string) { + fans := p.Json().GetValFromS(s, "data.fans") + fans_club := p.Json().GetValFromS(s, "data.fans_club") var sh []interface{} @@ -828,35 +880,38 @@ func (replyF) roominfo(s string){ sh = append(sh, "粉丝团人数:", fans_club) } - if len(sh) != 0 {msglog.Base_add("粉").L(`I: `, sh...)} + if len(sh) != 0 { + msglog.Base_add("粉").L(`I: `, sh...) + } } //Msg-弹幕处理 type Danmu_item struct { - msg string - auth interface{} - uid string - roomid int//to avoid danmu show when room has changed + msg string + auth interface{} + uid string + roomid int //to avoid danmu show when room has changed } + func (replyF) danmu(s string) { var j struct { Cmd string `json:"cmd"` Info []interface{} `json:"info"` } - if e := json.Unmarshal([]byte(s), &j);e != nil { + if e := json.Unmarshal([]byte(s), &j); e != nil { msglog.L(`E: `, e) } - + infob := j.Info item := Danmu_item{} { //解析 if len(infob) > 0 { - item.msg,_ = infob[1].(string) + item.msg, _ = infob[1].(string) } if len(infob) > 1 { - i,_ := infob[2].([]interface{}) + i, _ := infob[2].([]interface{}) if len(i) > 0 { item.uid = strconv.Itoa(int(i[0].(float64))) } @@ -869,7 +924,7 @@ func (replyF) danmu(s string) { msglog := msglog.Log_show_control(false) - {//附加功能 弹幕机 封禁 弹幕合并 + { //附加功能 弹幕机 封禁 弹幕合并 go Danmujif(item.msg) if Autobanf(item.msg) { Gui_show(Itos([]interface{}{"风险", item.auth, ":", item.msg})) @@ -889,7 +944,9 @@ func (replyF) danmu(s string) { if _msg := Shortdanmuf(item.msg); _msg == "" { msglog.L(`I: `, item.auth, ":", item.msg) return - } else {item.msg = _msg} + } else { + item.msg = _msg + } } Msg_showdanmu(item) } @@ -897,13 +954,13 @@ func (replyF) danmu(s string) { //弹幕发送 //传入字符串即可发送 //需要cookie -func Msg_senddanmu(msg string){ +func Msg_senddanmu(msg string) { if missKey := F.CookieCheck([]string{ `bili_jct`, `DedeUserID`, `LIVE_BUVID`, - });len(missKey) != 0 || c.Roomid == 0 { - msglog.L(`E: `,`c.Roomid == 0 || Cookie无Key:`,missKey) + }); len(missKey) != 0 || c.Roomid == 0 { + msglog.L(`E: `, `c.Roomid == 0 || Cookie无Key:`, missKey) return } send.Danmu_s(msg, c.Roomid) @@ -914,77 +971,89 @@ func Msg_senddanmu(msg string){ func Msg_showdanmu(item Danmu_item) { msg := item.msg msglog := msglog.Log_show_control(false) - + //room change - if item.roomid != 0 && item.roomid != c.Roomid {return} - + if item.roomid != 0 && item.roomid != c.Roomid { + return + } + //展示 { - Assf(msg)//ass + Assf(msg) //ass if item.auth != nil { - Gui_show(fmt.Sprint(item.auth) +`: `+ msg, item.uid) + Gui_show(fmt.Sprint(item.auth)+`: `+msg, item.uid) } else { Gui_show(msg, item.uid) } } - {//语言tts 私信 + { //语言tts 私信 if item.uid != "" { if item.auth != nil { - c.Danmu_Main_mq.Push_tag(`tts`,Danmu_mq_t{//传入消息队列 - uid:item.uid, - m:map[string]string{ - `{auth}`:fmt.Sprint(item.auth), - `{msg}`:msg, + c.Danmu_Main_mq.Push_tag(`tts`, Danmu_mq_t{ //传入消息队列 + uid: item.uid, + m: map[string]string{ + `{auth}`: fmt.Sprint(item.auth), + `{msg}`: msg, }, }) } - if i,e := strconv.Atoi(item.uid);e == nil { - c.Danmu_Main_mq.Push_tag(`pm`,send.Pm_item{ - Uid:i, - Msg:c.K_v.LoadV(`弹幕私信`).(string), - })//上舰私信 + if i, e := strconv.Atoi(item.uid); e == nil { + c.Danmu_Main_mq.Push_tag(`pm`, send.Pm_item{ + Uid: i, + Msg: c.K_v.LoadV(`弹幕私信`).(string), + }) //上舰私信 } if c.K_v.LoadV(`额外私信对象`).(float64) != 0 { - c.Danmu_Main_mq.Push_tag(`pm`,send.Pm_item{ - Uid:int(c.K_v.LoadV(`额外私信对象`).(float64)), - Msg:c.K_v.LoadV(`弹幕私信(额外)`).(string), - })//上舰私信-对额外 + c.Danmu_Main_mq.Push_tag(`pm`, send.Pm_item{ + Uid: int(c.K_v.LoadV(`额外私信对象`).(float64)), + Msg: c.K_v.LoadV(`弹幕私信(额外)`).(string), + }) //上舰私信-对额外 } } } fmt.Println(msg) - if item.auth != nil {msglog.L(`I: `, item.auth, ":", msg)} + if item.auth != nil { + msglog.L(`I: `, item.auth, ":", msg) + } } type Danmu_mq_t struct { uid string msg string - m map[string]string//tts参数替换列表 + m map[string]string //tts参数替换列表 } + var Danmu_mq = mq.New(10) -func Gui_show(m ...string){ +func Gui_show(m ...string) { //m[0]:msg m[1]:uid uid := "" - if len(m) > 1 {uid = m[1]} + if len(m) > 1 { + uid = m[1] + } - Danmu_mq.Push_tag(`danmu`,Danmu_mq_t{ - uid:uid, - msg:m[0], + Danmu_mq.Push_tag(`danmu`, Danmu_mq_t{ + uid: uid, + msg: m[0], }) } func Itos(i []interface{}) string { r := "" - for _,v := range i { + for _, v := range i { switch v.(type) { - case string:r += v.(string) - case int:r += strconv.Itoa(v.(int)) - case int64: r += strconv.Itoa(int(v.(int64))) - case float64: r+= strconv.Itoa(int(v.(float64))) - default:fmt.Println("unkonw type", v) + case string: + r += v.(string) + case int: + r += strconv.Itoa(v.(int)) + case int64: + r += strconv.Itoa(int(v.(int64))) + case float64: + r += strconv.Itoa(int(v.(float64))) + default: + fmt.Println("unkonw type", v) } r += " " } return r -} \ No newline at end of file +} diff --git a/Reply/gtk.go b/Reply/gtk.go index 60415a8..92b1582 100644 --- a/Reply/gtk.go +++ b/Reply/gtk.go @@ -1,84 +1,87 @@ -//+build gtk +//go:build gtk +// +build gtk package reply import ( "container/list" "errors" + "fmt" + "log" + "runtime" "strconv" - "time" "strings" - "log" - "fmt" "sync" - "runtime" + "time" - F "github.com/qydysky/bili_danmu/F" c "github.com/qydysky/bili_danmu/CV" + F "github.com/qydysky/bili_danmu/F" p "github.com/qydysky/part" - msgq "github.com/qydysky/part/msgq" s "github.com/qydysky/part/buf" + msgq "github.com/qydysky/part/msgq" reqf "github.com/qydysky/part/reqf" + "github.com/gotk3/gotk3/gdk" "github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/gtk" - "github.com/gotk3/gotk3/gdk" ) type danmu_item struct { - text *gtk.TextView - img *gtk.Image + text *gtk.TextView + img *gtk.Image handle glib.SignalHandle } type gtk_item_source struct { text string - img string + img string time time.Time } -var gtkGetList = make(chan string,100) -var danmu_item_chan = make(chan danmu_item,100) +var gtkGetList = make(chan string, 100) +var danmu_item_chan = make(chan danmu_item, 100) var keep_key = map[string]int{ - "face/0default":0, - "face/0room":0, - "face/0buyguide":9, - "face/0gift":8, - "face/0jiezou":8, - "face/0level1":5, - "face/0level2":3, - "face/0level3":1, - "face/0superchat":13, - "face/0tianxuan":5, + "face/0default": 0, + "face/0room": 0, + "face/0buyguide": 9, + "face/0gift": 8, + "face/0jiezou": 8, + "face/0level1": 5, + "face/0level2": 3, + "face/0level3": 1, + "face/0superchat": 13, + "face/0tianxuan": 5, } var ( - Gtk_on bool - Gtk_img_path string = "face" - Gtk_danmu_chan = make(chan Danmu_mq_t,100) + Gtk_on bool + Gtk_img_path string = "face" + Gtk_danmu_chan = make(chan Danmu_mq_t, 100) - imgbuf = struct{ + imgbuf = struct { b map[string](*gdk.Pixbuf) sync.Mutex }{ - b:make(map[string](*gdk.Pixbuf)), + b: make(map[string](*gdk.Pixbuf)), } - danmu_win_running bool//弹幕窗体是否正在运行 - contrl_win_running bool//控制窗体是否正在运行 - in_smooth_roll bool - grid0 *gtk.Grid - grid1 *gtk.Grid - keep_list = list.New() + danmu_win_running bool //弹幕窗体是否正在运行 + contrl_win_running bool //控制窗体是否正在运行 + in_smooth_roll bool + grid0 *gtk.Grid + grid1 *gtk.Grid + keep_list = list.New() ) -func init(){ - if!IsOn("Gtk弹幕窗") {return} +func init() { + if !IsOn("Gtk弹幕窗") { + return + } go Gtk_danmu() //使用带tag的消息队列在功能间传递消息 Danmu_mq.Pull_tag(msgq.FuncMap{ - `danmu`:func(data interface{})(bool){//弹幕 - select{ + `danmu`: func(data interface{}) bool { //弹幕 + select { case Gtk_danmu_chan <- data.(Danmu_mq_t): default: } @@ -86,12 +89,14 @@ func init(){ }, }) // - go func(){//copy map + go func() { //copy map for { - time.Sleep(time.Duration(60)*time.Second) + time.Sleep(time.Duration(60) * time.Second) { tmp := make(map[string](*gdk.Pixbuf)) - for k,v := range imgbuf.b {tmp[k] = v} + for k, v := range imgbuf.b { + tmp[k] = v + } imgbuf.Lock() imgbuf.b = tmp imgbuf.Unlock() @@ -101,34 +106,44 @@ func init(){ } func Gtk_danmu() { - if Gtk_on {return} + if Gtk_on { + return + } gtk.Init(nil) var ( - scrolledwindow0 *gtk.ScrolledWindow - viewport0 *gtk.Viewport - w2_textView0 *gtk.TextView - w2_textView1 *gtk.TextView - w2_textView2 *gtk.TextView - w2_textView3 *gtk.TextView - w2_textView4 *gtk.TextView - w2_Entry0 *gtk.Entry - w2_Entry0_editting = make(chan bool,10) + scrolledwindow0 *gtk.ScrolledWindow + viewport0 *gtk.Viewport + w2_textView0 *gtk.TextView + w2_textView1 *gtk.TextView + w2_textView2 *gtk.TextView + w2_textView3 *gtk.TextView + w2_textView4 *gtk.TextView + w2_Entry0 *gtk.Entry + w2_Entry0_editting = make(chan bool, 10) ) - application, err := gtk.ApplicationNew( - "com.github.qydysky.bili_danmu.reply"+p.Sys().GetTime(),//时间戳允许多开 - glib.APPLICATION_FLAGS_NONE) + "com.github.qydysky.bili_danmu.reply"+p.Sys().GetTime(), //时间戳允许多开 + glib.APPLICATION_FLAGS_NONE) - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } application.Connect("startup", func() { builder, err := gtk.BuilderNewFromFile("ui/1.glade") - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } builder2, err := gtk.BuilderNewFromFile("ui/2.glade") - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } { signals := map[string]interface{}{ @@ -138,177 +153,275 @@ func Gtk_danmu() { builder2.ConnectSignals(signals) } var ( - win *gtk.Window + win *gtk.Window win2 *gtk.Window ) { obj, err := builder.GetObject("main_window") - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } win, err = isWindow(obj) - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } danmu_win_running = true win.Connect("delete-event", func() { log.Println(`弹幕窗已关闭`) - danmu_win_running = false//关闭后置空 + danmu_win_running = false //关闭后置空 }) application.AddWindow(win) } { obj, err := builder2.GetObject("main_window") - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } win2, err = isWindow(obj) - if err != nil {log.Println(err);return} + if err != nil { + log.Println(err) + return + } contrl_win_running = true win2.Connect("delete-event", func() { log.Println(`弹幕信息窗已关闭`) - contrl_win_running = false//关闭后置空 + contrl_win_running = false //关闭后置空 }) application.AddWindow(win2) } - {//营收 + { //营收 obj, err := builder2.GetObject("t0") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.TextView); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.TextView); ok { w2_textView0 = tmp - }else{log.Println("cant find #t0 in .glade");return} + } else { + log.Println("cant find #t0 in .glade") + return + } } - {//直播时长 + { //直播时长 obj, err := builder2.GetObject("t1") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.TextView); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.TextView); ok { w2_textView1 = tmp - }else{log.Println("cant find #t1 in .glade");return} + } else { + log.Println("cant find #t1 in .glade") + return + } } - {//人气值 + { //人气值 obj, err := builder2.GetObject("t2") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.TextView); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.TextView); ok { w2_textView2 = tmp - }else{log.Println("cant find #t2 in .glade");return} + } else { + log.Println("cant find #t2 in .glade") + return + } } - {//舰长数 + { //舰长数 obj, err := builder2.GetObject("t3") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.TextView); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.TextView); ok { w2_textView3 = tmp - }else{log.Println("cant find #t3 in .glade");return} + } else { + log.Println("cant find #t3 in .glade") + return + } } - {//排名 + { //排名 obj, err := builder2.GetObject("t4") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.TextView); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.TextView); ok { w2_textView4 = tmp - }else{log.Println("cant find #t4 in .glade");return} + } else { + log.Println("cant find #t4 in .glade") + return + } } - {//发送弹幕 + { //发送弹幕 var danmu_send_form string - {//发送弹幕格式 + { //发送弹幕格式 obj, err := builder2.GetObject("send_danmu_form") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Entry); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Entry); ok { tmp.Connect("focus-out-event", func() { - if t,e := tmp.GetText();e == nil {//可设置为空 + if t, e := tmp.GetText(); e == nil { //可设置为空 danmu_send_form = t - log.Println("弹幕格式已设置为",danmu_send_form) + log.Println("弹幕格式已设置为", danmu_send_form) } }) - }else{log.Println("cant find #send_danmu in .glade");return} + } else { + log.Println("cant find #send_danmu in .glade") + return + } } obj, err := builder2.GetObject("send_danmu") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Entry); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Entry); ok { tmp.Connect("key-release-event", func(entry *gtk.Entry, event *gdk.Event) { eventKey := gdk.EventKeyNewFromEvent(event) if eventKey.KeyVal() == gdk.KEY_Return { - if t,e := entry.GetText();e == nil && t != ``{ + if t, e := entry.GetText(); e == nil && t != `` { danmu_want_send := t - if danmu_send_form != `` {danmu_want_send = strings.ReplaceAll(danmu_send_form, "{D}", t)} + if danmu_send_form != `` { + danmu_want_send = strings.ReplaceAll(danmu_send_form, "{D}", t) + } if len([]rune(danmu_want_send)) > 20 { log.Println(`弹幕长度大于20,不做格式处理`) danmu_want_send = t - } + } Msg_senddanmu(danmu_want_send) entry.SetText(``) } } }) - }else{log.Println("cant find #send_danmu in .glade");return} + } else { + log.Println("cant find #send_danmu in .glade") + return + } } - {//房间id + { //房间id obj, err := builder2.GetObject("want_room_id") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Entry); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Entry); ok { w2_Entry0 = tmp tmp.Connect("focus-out-event", func() { - glib.TimeoutAdd(uint(3000), func()bool{//3s后才解除,避免刚想切换又变回去 - for len(w2_Entry0_editting) != 0 {<-w2_Entry0_editting} + glib.TimeoutAdd(uint(3000), func() bool { //3s后才解除,避免刚想切换又变回去 + for len(w2_Entry0_editting) != 0 { + <-w2_Entry0_editting + } w2_Entry0_editting <- false return false }) }) - }else{log.Println("cant find #want_room_id in .glade");return} + } else { + log.Println("cant find #want_room_id in .glade") + return + } } - {//房间id click + { //房间id click obj, err := builder2.GetObject("want_click") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Button); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Button); ok { tmp.Connect("clicked", func() { - if t,e := w2_Entry0.GetText();e != nil { - show("读取错误",load_face("0room")) + if t, e := w2_Entry0.GetText(); e != nil { + show("读取错误", load_face("0room")) } else if t != `` { - if i,e := strconv.Atoi(t);e != nil { - show(`输入错误`,load_face("0room")) + if i, e := strconv.Atoi(t); e != nil { + show(`输入错误`, load_face("0room")) } else { - c.Roomid = i - c.Danmu_Main_mq.Push_tag(`change_room`,nil) + c.Roomid = i + c.Danmu_Main_mq.Push_tag(`change_room`, nil) } } else { - show(`房间号输入为空`,load_face("0room")) + show(`房间号输入为空`, load_face("0room")) } }) - }else{log.Println("cant find #want_click in .glade");return} + } else { + log.Println("cant find #want_click in .glade") + return + } } { obj, err := builder.GetObject("scrolledwindow0") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.ScrolledWindow); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.ScrolledWindow); ok { scrolledwindow0 = tmp - }else{log.Println("cant find #scrolledwindow0 in .glade");return} + } else { + log.Println("cant find #scrolledwindow0 in .glade") + return + } } { obj, err := builder.GetObject("viewport0") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Viewport); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Viewport); ok { viewport0 = tmp - }else{log.Println("cant find #viewport0 in .glade");return} + } else { + log.Println("cant find #viewport0 in .glade") + return + } } { obj, err := builder.GetObject("grid0") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Grid); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Grid); ok { grid0 = tmp - }else{log.Println("cant find #grid0 in .glade");return} + } else { + log.Println("cant find #grid0 in .glade") + return + } } { obj, err := builder.GetObject("grid1") - if err != nil {log.Println(err);return} - if tmp,ok := obj.(*gtk.Grid); ok { + if err != nil { + log.Println(err) + return + } + if tmp, ok := obj.(*gtk.Grid); ok { grid1 = tmp - }else{log.Println("cant find #grid1 in .glade");return} + } else { + log.Println("cant find #grid1 in .glade") + return + } } imgbuf.Lock() - imgbuf.b["face/0default"],_ = gdk.PixbufNewFromFileAtSize("face/0default", 40, 40); + imgbuf.b["face/0default"], _ = gdk.PixbufNewFromFileAtSize("face/0default", 40, 40) imgbuf.Unlock() { - if pro_style,e := gtk.CssProviderNew();e == nil{ - if e = pro_style.LoadFromPath(`ui/1.css`);e == nil{ - if scr := win.GetScreen();scr != nil { - gtk.AddProviderForScreen(scr,pro_style,gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + if pro_style, e := gtk.CssProviderNew(); e == nil { + if e = pro_style.LoadFromPath(`ui/1.css`); e == nil { + if scr := win.GetScreen(); scr != nil { + gtk.AddProviderForScreen(scr, pro_style, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) } - }else{log.Println(e)} - }else{log.Println(e)} + } else { + log.Println(e) + } + } else { + log.Println(e) + } } //先展示弹幕窗 @@ -320,57 +433,65 @@ func Gtk_danmu() { application.Connect("activate", func() { - go func(){ - glib.TimeoutAdd(uint(1000),func()(bool){ - if !danmu_win_running {return false} + go func() { + glib.TimeoutAdd(uint(1000), func() bool { + if !danmu_win_running { + return false + } el := keep_list.Front() for el != nil && time.Now().After(el.Value.(gtk_item_source).time) { if grid1.Container.GetChildren().Length() > 0 { grid1.RemoveRow(0) } - show(el.Value.(gtk_item_source).text,el.Value.(gtk_item_source).img, 0) + show(el.Value.(gtk_item_source).text, el.Value.(gtk_item_source).img, 0) keep_list.Remove(el) el = el.Next() } return true }) for danmu_win_running { - select{ - case item := <- Gtk_danmu_chan: - show(item.msg,load_face(item.uid)) + select { + case item := <-Gtk_danmu_chan: + show(item.msg, load_face(item.uid)) default: } - time.Sleep(time.Second/time.Duration(len(Gtk_danmu_chan)+1)) + time.Sleep(time.Second / time.Duration(len(Gtk_danmu_chan)+1)) } }() var old_cu float64 - {//平滑滚动效果 - glib.TimeoutAdd(uint(30),func()(bool){ - if !danmu_win_running {return false} - if !in_smooth_roll {return true} + { //平滑滚动效果 + glib.TimeoutAdd(uint(30), func() bool { + if !danmu_win_running { + return false + } + if !in_smooth_roll { + return true + } h := viewport0.GetViewWindow().WindowGetHeight() tmp := scrolledwindow0.GetVAdjustment() max := tmp.GetUpper() - float64(h) cu := tmp.GetValue() - + //用户在回看 - if old_cu != 0 &&//非初始 - max - cu > 100 &&//当前位置低于100 - old_cu != cu {//上一次滚动有移动 + if old_cu != 0 && //非初始 + max-cu > 100 && //当前位置低于100 + old_cu != cu { //上一次滚动有移动 return true } - - loc := int(grid0.Container.GetChildren().Length())/2 + + loc := int(grid0.Container.GetChildren().Length()) / 2 step := (max - cu) / 30 - if loc > 0 && step > 20 || max > 5 * float64(h){//太长或太快 + if loc > 0 && step > 20 || max > 5*float64(h) { //太长或太快 grid0.RemoveRow(0) } else if step > 0.5 { - if step > 5{step = 5} + if step > 5 { + step = 5 + } tmp.SetValue(cu + step) } else { in_smooth_roll = false tmp.SetValue(max) - if v,ok := c.K_v.LoadV(`gtk_保留弹幕数量`).(float64);ok { + if v, ok := c.K_v.LoadV(`gtk_保留弹幕数量`).(float64); ok { loc -= int(v) } else { loc -= 25 @@ -385,37 +506,51 @@ func Gtk_danmu() { }) } var renqi_old = 1 - glib.TimeoutAdd(uint(3000), func()(o bool){ + glib.TimeoutAdd(uint(3000), func() (o bool) { o = contrl_win_running //y("sssss",load_face("")) - {//加载特定信息驻留时长 + { //加载特定信息驻留时长 buf := s.New() buf.Load("config/config_gtk_keep_key.json") - for k,_ := range keep_key {delete(keep_key,k)} - for k,v := range buf.B { + for k, _ := range keep_key { + delete(keep_key, k) + } + for k, v := range buf.B { keep_key[k] = int(v.(float64)) } } - {//营收 + { //营收 if IsOn("统计营收") { - b,e := w2_textView0.GetBuffer() - if e != nil {log.Println(e);return} - b.SetText(fmt.Sprintf("¥%.2f",c.Rev)) + b, e := w2_textView0.GetBuffer() + if e != nil { + log.Println(e) + return + } + b.SetText(fmt.Sprintf("¥%.2f", c.Rev)) } } - {//舰长 - b,e := w2_textView3.GetBuffer() - if e != nil {log.Println(e);return} - b.SetText(fmt.Sprintf("%d",c.GuardNum)) + { //舰长 + b, e := w2_textView3.GetBuffer() + if e != nil { + log.Println(e) + return + } + b.SetText(fmt.Sprintf("%d", c.GuardNum)) } - {//分区排行 - b,e := w2_textView4.GetBuffer() - if e != nil {log.Println(e);return} + { //分区排行 + b, e := w2_textView4.GetBuffer() + if e != nil { + log.Println(e) + return + } b.SetText(c.Note) } - {//时长 - b,e := w2_textView1.GetBuffer() - if e != nil {log.Println(e);return} + { //时长 + b, e := w2_textView1.GetBuffer() + if e != nil { + log.Println(e) + return + } if c.Liveing { d := time.Since(c.Live_Start_Time).Round(time.Second) h := d / time.Hour @@ -428,24 +563,31 @@ func Gtk_danmu() { b.SetText("00:00:00") } } - {//人气 - b,e := w2_textView2.GetBuffer() - if e != nil {log.Println(e);return} + { //人气 + b, e := w2_textView2.GetBuffer() + if e != nil { + log.Println(e) + return + } if c.Liveing { if c.Renqi != renqi_old { var Renqi string = strconv.Itoa(c.Renqi) - L:=len([]rune(Renqi)) + L := len([]rune(Renqi)) var tmp string if renqi_old != 1 { - if c.Renqi > renqi_old {tmp += `+`} - tmp += fmt.Sprintf("%.1f",100*float64(c.Renqi - renqi_old)/float64(renqi_old)) + `% | ` + if c.Renqi > renqi_old { + tmp += `+` + } + tmp += fmt.Sprintf("%.1f", 100*float64(c.Renqi-renqi_old)/float64(renqi_old)) + `% | ` + } + if c.Renqi != 0 { + renqi_old = c.Renqi } - if c.Renqi != 0 {renqi_old = c.Renqi} - for k,v := range []rune(Renqi) { + for k, v := range []rune(Renqi) { tmp += string(v) - if (L - k)%3 == 1 && L - k != 1{ + if (L-k)%3 == 1 && L-k != 1 { tmp += `,` } } @@ -455,10 +597,12 @@ func Gtk_danmu() { b.SetText(`0`) } } - {//房间id - for len(w2_Entry0_editting) > 1 {<-w2_Entry0_editting} - select{ - case tmp:=<-w2_Entry0_editting: + { //房间id + for len(w2_Entry0_editting) > 1 { + <-w2_Entry0_editting + } + select { + case tmp := <-w2_Entry0_editting: if !tmp { w2_Entry0.SetText(strconv.Itoa(c.Roomid)) } @@ -466,18 +610,24 @@ func Gtk_danmu() { } } select { - case uid:=<-gtkGetList: - go func(){ - if p.Checkfile().IsExist(Gtk_img_path + `/` + uid) {return} + case uid := <-gtkGetList: + go func() { + if p.Checkfile().IsExist(Gtk_img_path + `/` + uid) { + return + } src := F.Get_face_src(uid) - if src == "" {return} + if src == "" { + return + } req := reqf.New() if e := req.Reqf(reqf.Rval{ - Url:src, - SaveToPath:Gtk_img_path + `/` + uid, - Timeout:3*1000, - Proxy:c.Proxy, - }); e != nil{log.Println(e);} + Url: src, + SaveToPath: Gtk_img_path + `/` + uid, + Timeout: 3 * 1000, + Proxy: c.Proxy, + }); e != nil { + log.Println(e) + } }() default: } @@ -486,9 +636,9 @@ func Gtk_danmu() { }) application.Connect("shutdown", func() { - log.Println("application shutdown") + log.Println("application shutdown") Gtk_on = false - c.Danmu_Main_mq.Push_tag(`gtk_close`,nil) + c.Danmu_Main_mq.Push_tag(`gtk_close`, nil) }) application.Run(nil) @@ -507,141 +657,158 @@ func onMainWindowDestroy() { func load_face(uid string) (loc string) { loc = Gtk_img_path + `/` + "0default" - if uid == "" {return} - if _,ok := keep_key[Gtk_img_path + `/` + uid];ok{ + if uid == "" { + return + } + if _, ok := keep_key[Gtk_img_path+`/`+uid]; ok { loc = Gtk_img_path + `/` + uid return } - if p.Checkfile().IsExist(Gtk_img_path + `/` + uid) && p.Rand().MixRandom(1,100) > 1 { + if p.Checkfile().IsExist(Gtk_img_path+`/`+uid) && p.Rand().MixRandom(1, 100) > 1 { loc = Gtk_img_path + `/` + uid return } - if v,ok := c.K_v.LoadV(`gtk_头像获取等待最大数量`).(float64);ok && len(gtkGetList) > int(v) {return} - select{ - case gtkGetList <- uid: - default: + if v, ok := c.K_v.LoadV(`gtk_头像获取等待最大数量`).(float64); ok && len(gtkGetList) > int(v) { + return + } + select { + case gtkGetList <- uid: + default: } return } -func show(s,img_src string,to_grid ...int){ - if s == `` || img_src == `` {return} - glib.TimeoutAdd(uint(1),func()(r bool){ - r = false +func show(s, img_src string, to_grid ...int) { + if s == `` || img_src == `` { + return + } + glib.TimeoutAdd(uint(1), func() (r bool) { + r = false - sec := 0 + sec := 0 - var item danmu_item - // runtime.SetFinalizer(&item,func(p *danmu_item){p = nil;fmt.Print(`i `)}) + var item danmu_item + // runtime.SetFinalizer(&item,func(p *danmu_item){p = nil;fmt.Print(`i `)}) - item.text,_ = gtk.TextViewNew() - // runtime.SetFinalizer(item.text,func(p *gtk.TextView){p = nil;fmt.Print(`t `)}) - { - item.text.SetMarginStart(5) - item.text.SetEditable(false) - item.text.SetHExpand(true) - item.text.SetWrapMode(gtk.WRAP_WORD_CHAR) - if tsec,ok := keep_key[img_src];ok && tsec != 0 { - sec = tsec - if sty,e := item.text.GetStyleContext();e == nil{ - sty.AddClass("highlight") + item.text, _ = gtk.TextViewNew() + // runtime.SetFinalizer(item.text,func(p *gtk.TextView){p = nil;fmt.Print(`t `)}) + { + item.text.SetMarginStart(5) + item.text.SetEditable(false) + item.text.SetHExpand(true) + item.text.SetWrapMode(gtk.WRAP_WORD_CHAR) + if tsec, ok := keep_key[img_src]; ok && tsec != 0 { + sec = tsec + if sty, e := item.text.GetStyleContext(); e == nil { + sty.AddClass("highlight") + } } + item.handle = item.text.Connect("size-allocate", func(text *gtk.TextView) { + if text == nil { + return + } + b, e := text.GetBuffer() + if e != nil { + log.Println(e) + return + } + b.SetText(s) + in_smooth_roll = true + }) } - item.handle = item.text.Connect("size-allocate", func(text *gtk.TextView){ - if text == nil {return} - b,e := text.GetBuffer() - if e != nil {log.Println(e);return} - b.SetText(s) - in_smooth_roll = true - }) - } - item.img,_ = gtk.ImageNew(); - // runtime.SetFinalizer(item.img,func(p *gtk.Image){p = nil;fmt.Print(`I `)}) - { - var ( - pixbuf *gdk.Pixbuf - e error - ) - if v,ok := imgbuf.b[img_src];ok{ - pixbuf,e = gdk.PixbufCopy(v) - } else { - pixbuf,e = gdk.PixbufNewFromFileAtSize(img_src, 40, 40); - if e == nil { - imgbuf.Lock() - if v,ok := c.K_v.LoadV(`gtk_内存头像数量`).(float64);ok && len(imgbuf.b) > int(v) + 10 { - for k,_ := range imgbuf.b { - delete(imgbuf.b,k) - if len(imgbuf.b) <= int(v) {break} + item.img, _ = gtk.ImageNew() + // runtime.SetFinalizer(item.img,func(p *gtk.Image){p = nil;fmt.Print(`I `)}) + { + var ( + pixbuf *gdk.Pixbuf + e error + ) + if v, ok := imgbuf.b[img_src]; ok { + pixbuf, e = gdk.PixbufCopy(v) + } else { + pixbuf, e = gdk.PixbufNewFromFileAtSize(img_src, 40, 40) + if e == nil { + imgbuf.Lock() + if v, ok := c.K_v.LoadV(`gtk_内存头像数量`).(float64); ok && len(imgbuf.b) > int(v)+10 { + for k, _ := range imgbuf.b { + delete(imgbuf.b, k) + if len(imgbuf.b) <= int(v) { + break + } + } } + imgbuf.b[img_src], e = gdk.PixbufCopy(pixbuf) + imgbuf.Unlock() } - imgbuf.b[img_src],e = gdk.PixbufCopy(pixbuf) - imgbuf.Unlock() + } + if e == nil { + item.img.SetFromPixbuf(pixbuf) } } - if e == nil {item.img.SetFromPixbuf(pixbuf)} - } - select { - case danmu_item_chan <- item: - default: - old :=<- danmu_item_chan - old.text.Destroy() - old.img.Destroy() - runtime.GC() - danmu_item_chan <- item - } - - - { - if len(to_grid) != 0 && to_grid[0] == 0 {//突出显示结束后,显示在普通弹幕区 - loc := int(grid0.Container.GetChildren().Length())/2; - grid0.InsertRow(loc); - grid0.Attach(item.img, 0, loc, 1, 1) - grid0.Attach(item.text, 1, loc, 1, 1) - grid0.ShowAll() - return + select { + case danmu_item_chan <- item: + default: + old := <-danmu_item_chan + old.text.Destroy() + old.img.Destroy() + runtime.GC() + danmu_item_chan <- item } - /* - front - | - back index:0 - */ - var InsertIndex int = keep_list.Len() - if sec > InsertIndex / 5 {//5不是指最大值,而是当list太大时,sec小的将直接跳过 - var cu_To = time.Now().Add(time.Second * time.Duration(sec)) - var hasInsert bool - for el := keep_list.Front(); el != nil; el = el.Next(){ - if cu_To.After(el.Value.(gtk_item_source).time) {InsertIndex -= 1;continue} - keep_list.InsertBefore(gtk_item_source{ - text:s, - img:img_src, - time:cu_To, - },el) - hasInsert = true - break - } - if !hasInsert { - keep_list.PushBack(gtk_item_source{ - text:s, - img:img_src, - time:cu_To, - }) + + { + if len(to_grid) != 0 && to_grid[0] == 0 { //突出显示结束后,显示在普通弹幕区 + loc := int(grid0.Container.GetChildren().Length()) / 2 + grid0.InsertRow(loc) + grid0.Attach(item.img, 0, loc, 1, 1) + grid0.Attach(item.text, 1, loc, 1, 1) + grid0.ShowAll() + return } - loc := int(grid1.Container.GetChildren().Length())/2; - grid1.InsertRow(loc - InsertIndex); - grid1.Attach(item.img, 0, loc - InsertIndex, 1, 1) - grid1.Attach(item.text, 1, loc - InsertIndex, 1, 1) - grid1.ShowAll() - } else { - loc := int(grid0.Container.GetChildren().Length())/2; - grid0.InsertRow(loc); - grid0.Attach(item.img, 0, loc, 1, 1) - grid0.Attach(item.text, 1, loc, 1, 1) - grid0.ShowAll() + /* + front + | + back index:0 + */ + var InsertIndex int = keep_list.Len() + if sec > InsertIndex/5 { //5不是指最大值,而是当list太大时,sec小的将直接跳过 + var cu_To = time.Now().Add(time.Second * time.Duration(sec)) + var hasInsert bool + for el := keep_list.Front(); el != nil; el = el.Next() { + if cu_To.After(el.Value.(gtk_item_source).time) { + InsertIndex -= 1 + continue + } + keep_list.InsertBefore(gtk_item_source{ + text: s, + img: img_src, + time: cu_To, + }, el) + hasInsert = true + break + } + if !hasInsert { + keep_list.PushBack(gtk_item_source{ + text: s, + img: img_src, + time: cu_To, + }) + } + loc := int(grid1.Container.GetChildren().Length()) / 2 + grid1.InsertRow(loc - InsertIndex) + grid1.Attach(item.img, 0, loc-InsertIndex, 1, 1) + grid1.Attach(item.text, 1, loc-InsertIndex, 1, 1) + grid1.ShowAll() + } else { + loc := int(grid0.Container.GetChildren().Length()) / 2 + grid0.InsertRow(loc) + grid0.Attach(item.img, 0, loc, 1, 1) + grid0.Attach(item.text, 1, loc, 1, 1) + grid0.ShowAll() + } + return } - return - } }) } @@ -668,8 +835,8 @@ item.handle,_ = item.text.Connect("size-allocate", func(){ }) */ /* - 同时太多ToWidget().Destroy()将会卡死gtk + 同时太多ToWidget().Destroy()将会卡死gtk */ /* gotk3存在内存泄漏,发生在C -*/ \ No newline at end of file +*/ diff --git a/Reply/tts.go b/Reply/tts.go index 4498bb2..18331d4 100644 --- a/Reply/tts.go +++ b/Reply/tts.go @@ -1,88 +1,93 @@ package reply import ( - "fmt" - "time" - "errors" + "crypto/hmac" "crypto/sha256" - "os/exec" - "net/url" - "strings" "encoding/base64" "encoding/json" - "crypto/hmac" + "errors" + "fmt" + "io/ioutil" + "net/url" + "os/exec" + "strings" + "time" + c "github.com/qydysky/bili_danmu/CV" p "github.com/qydysky/part" + funcCtrl "github.com/qydysky/part/funcCtrl" + limit "github.com/qydysky/part/limit" msgq "github.com/qydysky/part/msgq" - ws "github.com/qydysky/part/websocket" - s "github.com/qydysky/part/buf" reqf "github.com/qydysky/part/reqf" - limit "github.com/qydysky/part/limit" - funcCtrl "github.com/qydysky/part/funcCtrl" + ws "github.com/qydysky/part/websocket" ) var ( tts_setting_string = map[string]string{ - "0buyguide":"感谢{D}", - "0gift":"感谢{D}", - "0superchat":"感谢{D}", + "0buyguide": "感谢{D}", + "0gift": "感谢{D}", + "0superchat": "感谢{D}", } tts_setting_replace = map[string]string{ - "\n":" ", + "\n": " ", } ) -var tts_List = make(chan string,20) +var tts_List = make(chan string, 20) -var tts_limit = limit.New(1,5000,15000)//频率限制1次/5s,最大等待时间15s +var tts_limit = limit.New(1, 5000, 15000) //频率限制1次/5s,最大等待时间15s var tts_log = c.Log.Base_add(`TTS`) var ( - tts_ser = "baidu" - tts_ser_map = map[string]func(string)error{ - `baidu`:baidu, - `youdao`:youdao, - `xf`:xf, + tts_ser = "baidu" + tts_ser_map = map[string]func(string) error{ + `baidu`: baidu, + `youdao`: youdao, + `xf`: xf, } - tts_prog = "ffplay" + tts_prog = "ffplay" tts_prog_set = "-autoexit -nodisp" ) -func init(){ - {//tts配置 +func init() { + { //tts配置 - if v, ok := c.K_v.LoadV(`TTS_总开关`).(bool);ok && !v { + if v, ok := c.K_v.LoadV(`TTS_总开关`).(bool); ok && !v { return } - if v, ok := c.K_v.LoadV(`TTS_使用程序路径`).(string);ok && v != ``{ + if v, ok := c.K_v.LoadV(`TTS_使用程序路径`).(string); ok && v != `` { tts_prog = v } else { - tts_log.L(`E: `,`TTS_使用程序路径不是字符串或为空`) + tts_log.L(`E: `, `TTS_使用程序路径不是字符串或为空`) } - if v, ok := c.K_v.LoadV(`TTS_使用程序参数`).(string);ok && v != ``{ + if v, ok := c.K_v.LoadV(`TTS_使用程序参数`).(string); ok && v != `` { tts_prog_set = v } else { - tts_log.L(`E: `,`TTS_使用程序参数不是字符串`) + tts_log.L(`E: `, `TTS_使用程序参数不是字符串`) } - if v, ok := c.K_v.LoadV(`TTS_服务器`).(string);ok && v != "" { - if _,ok := tts_ser_map[v];ok{ + if v, ok := c.K_v.LoadV(`TTS_服务器`).(string); ok && v != "" { + if _, ok := tts_ser_map[v]; ok { tts_ser = v } else { - tts_log.L(`I: `,`未支持设定服务提供商,使用baidu`) + tts_log.L(`I: `, `未支持设定服务提供商,使用baidu`) tts_ser = `baidu` } } - - buf := s.New() - buf.Load("config/config_tts.json") - if onoff,ok := buf.Get(`onoff`);ok { - for k,v := range onoff.(map[string]interface{}) { + + bb, err := ioutil.ReadFile("config/config_tts.json") + if err != nil { + return + } + var buf map[string]interface{} + json.Unmarshal(bb, &buf) + if onoff, ok := buf[`onoff`]; ok { + for k, v := range onoff.(map[string]interface{}) { tts_setting_string[k] = v.(string) } } - if replace,ok := buf.Get(`replace`);ok { - for k,v := range replace.(map[string]interface{}) { + if replace, ok := buf[`replace`]; ok { + for k, v := range replace.(map[string]interface{}) { tts_setting_replace[k] = v.(string) } } @@ -90,38 +95,45 @@ func init(){ //启动程序 p.Exec().Start(exec.Command(tts_prog)) - go func(){ - for{ - s := <- tts_List + go func() { + for { + s := <-tts_List for len(tts_List) > 0 && len(s) < 100 { - s += " " + <- tts_List + s += " " + <-tts_List } TTS(s) } }() - + //消息队列接收tts类消息,并传送到TTS朗读 //使用带tag的消息队列在功能间传递消息 c.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ - `tts`:func(data interface{})(bool){//tts - d,_ := data.(Danmu_mq_t) - if s,ok := tts_setting_string[d.uid];ok && len(d.m) != 0 && s != "" { - - for k,v := range d.m { + `tts`: func(data interface{}) bool { //tts + d, _ := data.(Danmu_mq_t) + if s, ok := tts_setting_string[d.uid]; ok && len(d.m) != 0 && s != "" { + + for k, v := range d.m { s = strings.ReplaceAll(s, k, v) } - for k,v := range tts_setting_replace { + for k, v := range tts_setting_replace { s = strings.ReplaceAll(s, k, v) } var ( - skip bool + skip bool runel []rune ) - for _,v := range s { - if v == []rune("{")[0] {skip = true} - if v == []rune("}")[0] {skip = false;continue} - if skip {continue} - runel = append(runel,v) + for _, v := range s { + if v == []rune("{")[0] { + skip = true + } + if v == []rune("}")[0] { + skip = false + continue + } + if skip { + continue + } + runel = append(runel, v) } tts_log.L(`I: `, d.uid, string(runel)) @@ -129,11 +141,12 @@ func init(){ } return false }, - `change_room`:func(data interface{})(bool){ + `change_room`: func(data interface{}) bool { for { select { - case <- tts_List:; - default:return false; + case <-tts_List: + default: + return false } } return false @@ -141,29 +154,30 @@ func init(){ }) } - func TTS(msg string) { - if tts_limit.TO() {return} + if tts_limit.TO() { + return + } var err error - if f,ok := tts_ser_map[tts_ser];ok{ + if f, ok := tts_ser_map[tts_ser]; ok { err = f(msg) } else { err = baidu(msg) } - + if err != nil { tts_log.L(`E: `, err) return } - + return } -func play(){ +func play() { var prog = []string{} prog = append(prog, p.Sys().Cdir()+"/tts.mp3") - prog = append(prog, strings.Split(tts_prog_set," ")...) + prog = append(prog, strings.Split(tts_prog_set, " ")...) p.Exec().Run(false, tts_prog, prog...) } @@ -171,13 +185,13 @@ func play(){ func baidu(msg string) error { req := reqf.New() if err := req.Reqf(reqf.Rval{ - Url:`https://fanyi.baidu.com/gettts?lan=zh&text=`+ url.PathEscape(msg) +`&spd=5&source=web`, - SaveToPath:p.Sys().Cdir()+`/tts.mp3`, - Timeout:3*1000, - Retry:1, - SleepTime:5000, - Proxy:c.Proxy, - });err != nil { + Url: `https://fanyi.baidu.com/gettts?lan=zh&text=` + url.PathEscape(msg) + `&spd=5&source=web`, + SaveToPath: p.Sys().Cdir() + `/tts.mp3`, + Timeout: 3 * 1000, + Retry: 1, + SleepTime: 5000, + Proxy: c.Proxy, + }); err != nil { return err } play() @@ -185,14 +199,15 @@ func baidu(msg string) error { } var ( - youdaoId string + youdaoId string youdaoappKey string ) -func init(){ - if v, ok := c.K_v.LoadV(`TTS_服务器_youdaoId`).(string);ok && v != ``{ + +func init() { + if v, ok := c.K_v.LoadV(`TTS_服务器_youdaoId`).(string); ok && v != `` { youdaoId = v } - if v, ok := c.K_v.LoadV(`TTS_服务器_youdaoKey`).(string);ok && v != ``{ + if v, ok := c.K_v.LoadV(`TTS_服务器_youdaoKey`).(string); ok && v != `` { youdaoappKey = v } if tts_ser == `youdao` && (youdaoId == `` || youdaoappKey == ``) { @@ -208,29 +223,31 @@ func youdao(msg string) error { //https://ai.youdao.com/gw.s#/ var ( api = map[string]string{ - `q`:msg, - `langType`:"zh-CHS", - `youdaoappKey`:youdaoId, - `salt`:p.Stringf().Rand(1, 8), + `q`: msg, + `langType`: "zh-CHS", + `youdaoappKey`: youdaoId, + `salt`: p.Stringf().Rand(1, 8), } postS string ) - api[`sign`] = strings.ToUpper(p.Md5().Md5String(api[`youdaoappKey`]+api[`q`]+api[`salt`]+youdaoappKey)) - for k,v := range api { - if postS != "" {postS += "&"} - postS += k+`=`+v + api[`sign`] = strings.ToUpper(p.Md5().Md5String(api[`youdaoappKey`] + api[`q`] + api[`salt`] + youdaoappKey)) + for k, v := range api { + if postS != "" { + postS += "&" + } + postS += k + `=` + v } req := reqf.New() if err := req.Reqf(reqf.Rval{ - Url:`https://openapi.youdao.com/ttsapi`, - PostStr:url.PathEscape(postS), - SaveToPath:p.Sys().Cdir()+`/tts.mp3`, - Timeout:3*1000, - Retry:1, - SleepTime:5000, - Proxy:c.Proxy, - });err != nil { + Url: `https://openapi.youdao.com/ttsapi`, + PostStr: url.PathEscape(postS), + SaveToPath: p.Sys().Cdir() + `/tts.mp3`, + Timeout: 3 * 1000, + Retry: 1, + SleepTime: 5000, + Proxy: c.Proxy, + }); err != nil { return err } if req.Response.Header.Get(`Content-type`) == `application/json` { @@ -241,41 +258,44 @@ func youdao(msg string) error { } var ( - xfId string - xfKey string + xfId string + xfKey string xfSecret string - xfVoice = "random" - xfVmap = map[string]bool{ - `xiaoyan`:true, - `aisjiuxu`:true, - `aisxping`:true, - `aisjinger`:true, - `aisbabyxu`:true, + xfVoice = "random" + xfVmap = map[string]bool{ + `xiaoyan`: true, + `aisjiuxu`: true, + `aisxping`: true, + `aisjinger`: true, + `aisbabyxu`: true, } - xfwsClient *ws.Client - xf_req func() + xfwsClient *ws.Client + xf_req func() xf_req_block funcCtrl.BlockFunc ) -func init(){ - if v, ok := c.K_v.LoadV(`TTS_服务器_xfId`).(string);ok && v != ``{ + +func init() { + if v, ok := c.K_v.LoadV(`TTS_服务器_xfId`).(string); ok && v != `` { xfId = v } - if v, ok := c.K_v.LoadV(`TTS_服务器_xfKey`).(string);ok && v != ``{ + if v, ok := c.K_v.LoadV(`TTS_服务器_xfKey`).(string); ok && v != `` { xfKey = v } - if v, ok := c.K_v.LoadV(`TTS_服务器_xfSecret`).(string);ok && v != ``{ + if v, ok := c.K_v.LoadV(`TTS_服务器_xfSecret`).(string); ok && v != `` { xfSecret = v } - if v, ok := c.K_v.LoadV(`TTS_服务器_xfVoice`).(string);ok && v != ``{ - if _,ok := xfVmap[v];ok || v == `random` { + if v, ok := c.K_v.LoadV(`TTS_服务器_xfVoice`).(string); ok && v != `` { + if _, ok := xfVmap[v]; ok || v == `random` { xfVoice = v } else { - tts_log.L(`I: `,`未支持设定发音,使用随机`) + tts_log.L(`I: `, `未支持设定发音,使用随机`) } } // 设置了非讯飞tts - if tts_ser != `xf` {return} + if tts_ser != `xf` { + return + } if xfId == `` || xfKey == `` || xfSecret == `` { tts_log.L(`W: `, `未提供讯飞Id、Key、Secret,使用baidu`) @@ -284,37 +304,37 @@ func init(){ } //@hosturl : like wss://tts-api.xfyun.cn/v2/tts - //@apikey : apiKey - //@apiSecret : apiSecret - assembleAuthUrl := func (hosturl string, apiKey, apiSecret string) (string,error) { - ul, err := url.Parse(hosturl) - if err != nil { - return "",err - } - //签名时间 - date := time.Now().UTC().Format(time.RFC1123) - //参与签名的字段 host ,date, request-line - signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"} - //拼接签名字符串 - sgin := strings.Join(signString, "\n") - //签名 + //@apikey : apiKey + //@apiSecret : apiSecret + assembleAuthUrl := func(hosturl string, apiKey, apiSecret string) (string, error) { + ul, err := url.Parse(hosturl) + if err != nil { + return "", err + } + //签名时间 + date := time.Now().UTC().Format(time.RFC1123) + //参与签名的字段 host ,date, request-line + signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"} + //拼接签名字符串 + sgin := strings.Join(signString, "\n") + //签名 mac := hmac.New(sha256.New, []byte(apiSecret)) mac.Write([]byte(sgin)) - sha := base64.StdEncoding.EncodeToString(mac.Sum(nil)) - //构建请求参数 此时不需要urlencoding - authUrl := fmt.Sprintf("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha) - //将请求参数使用base64编码 - authorization:= base64.StdEncoding.EncodeToString([]byte(authUrl)) - v := url.Values{} - v.Add("host", ul.Host) - v.Add("date", date) - v.Add("authorization", authorization) - //将编码后的字符串url encode后添加到url后面 - callurl := hosturl + "?" + v.Encode() - return callurl,nil - } - - wsUrl,err := assembleAuthUrl("wss://tts-api.xfyun.cn/v2/tts", xfKey, xfSecret) + sha := base64.StdEncoding.EncodeToString(mac.Sum(nil)) + //构建请求参数 此时不需要urlencoding + authUrl := fmt.Sprintf("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha) + //将请求参数使用base64编码 + authorization := base64.StdEncoding.EncodeToString([]byte(authUrl)) + v := url.Values{} + v.Add("host", ul.Host) + v.Add("date", date) + v.Add("authorization", authorization) + //将编码后的字符串url encode后添加到url后面 + callurl := hosturl + "?" + v.Encode() + return callurl, nil + } + + wsUrl, err := assembleAuthUrl("wss://tts-api.xfyun.cn/v2/tts", xfKey, xfSecret) if err != nil { tts_log.L(`E: `, `错误,使用百度`, err) @@ -322,30 +342,32 @@ func init(){ return } - xf_req = func(){ - xf_req_block.Block()//cant call in same time + xf_req = func() { + xf_req_block.Block() //cant call in same time defer xf_req_block.UnBlock() xfwsClient = ws.New_client(ws.Client{ - Url:wsUrl, - Proxy:c.Proxy, + Url: wsUrl, + Proxy: c.Proxy, Header: map[string]string{ - `User-Agent`: `Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0`, - `Accept`: `*/*`, + `User-Agent`: `Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0`, + `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`, - `Pragma`: `no-cache`, - `Cache-Control`: `no-cache`, + `Pragma`: `no-cache`, + `Cache-Control`: `no-cache`, }, }).Handle() if xfwsClient.Isclose() { - tts_log.L(`E: `,"连接错误,使用百度", xfwsClient.Error()) + tts_log.L(`E: `, "连接错误,使用百度", xfwsClient.Error()) tts_ser = `baidu` } else { - go func(){ + go func() { var buf []byte for !xfwsClient.Isclose() { - data := <- xfwsClient.RecvChan - if len(data) == 0 {break} + data := <-xfwsClient.RecvChan + if len(data) == 0 { + break + } var partS struct { Code int `json:"code"` @@ -357,18 +379,18 @@ func init(){ Status int `json:"status"` } `json:"data"` } - if e := json.Unmarshal(data, &partS);e != nil { - tts_log.L(`E: `,"错误", e, data) + if e := json.Unmarshal(data, &partS); e != nil { + tts_log.L(`E: `, "错误", e, data) xfwsClient.Close() return } else { if partS.Code != 0 { - tts_log.L(`W: `,fmt.Sprintf("code:%d msg:%s", partS.Code, partS.Message)) + tts_log.L(`W: `, fmt.Sprintf("code:%d msg:%s", partS.Code, partS.Message)) break } if partS.Data.Audio != "" { - if part,e := base64.StdEncoding.DecodeString(partS.Data.Audio);e != nil { - tts_log.L(`E: `,"错误", e) + if part, e := base64.StdEncoding.DecodeString(partS.Data.Audio); e != nil { + tts_log.L(`E: `, "错误", e) break } else { buf = append(buf, part...) @@ -381,27 +403,27 @@ func init(){ } if len(buf) != 0 { p.File().FileWR(p.Filel{ - File:p.Sys().Cdir()+`/tts.mp3`, - Context:[]interface{}{buf}, + File: p.Sys().Cdir() + `/tts.mp3`, + Context: []interface{}{buf}, }) play() } xfwsClient.Close() }() } - + } xf_req() } func xf(msg string) error { if xfId == `` || xfKey == `` || xfSecret == `` { - tts_log.L(`T: `,"参数不足,使用百度") + tts_log.L(`T: `, "参数不足,使用百度") return baidu(msg) } voice := xfVoice if voice == `random` { - for k,_ := range xfVmap { + for k, _ := range xfVmap { voice = k break } @@ -412,10 +434,10 @@ func xf(msg string) error { AppID string `json:"app_id"` } `json:"common"` Business struct { - Aue string `json:"aue"` - Vcn string `json:"vcn"` - Tte string `json:"tte"` - Sfl int `json:"sfl"` + Aue string `json:"aue"` + Vcn string `json:"vcn"` + Tte string `json:"tte"` + Sfl int `json:"sfl"` } `json:"business"` Data struct { Status int `json:"status"` @@ -423,7 +445,7 @@ func xf(msg string) error { } `json:"data"` } - {//msg + { //msg var postS = rec{} postS.Common.AppID = xfId postS.Business.Aue = "lame" @@ -432,8 +454,8 @@ func xf(msg string) error { postS.Business.Vcn = voice postS.Data.Status = 2 postS.Data.Text = base64.StdEncoding.EncodeToString([]byte(msg)) - - if b,e := json.Marshal(postS);e != nil { + + if b, e := json.Marshal(postS); e != nil { return e } else { if xfwsClient.Isclose() { @@ -443,4 +465,4 @@ func xf(msg string) error { } } return nil -} \ No newline at end of file +} diff --git a/Reply/ws_msg/WATCHED_CHANGE.go b/Reply/ws_msg/WATCHED_CHANGE.go new file mode 100644 index 0000000..5d5efe8 --- /dev/null +++ b/Reply/ws_msg/WATCHED_CHANGE.go @@ -0,0 +1,11 @@ +package part + +type WATCHED_CHANGE struct { + Cmd string `json:"cmd"` + Data WATCHED_CHANGE_Data `json:"data"` +} +type WATCHED_CHANGE_Data struct { + Num int `json:"num"` + TextSmall string `json:"text_small"` + TextLarge string `json:"text_large"` +} diff --git a/demo/go.mod b/demo/go.mod index 2c16db5..5eed855 100644 --- a/demo/go.mod +++ b/demo/go.mod @@ -14,7 +14,7 @@ require ( github.com/miekg/dns v1.1.42 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/qydysky/bili_danmu v0.5.9 - github.com/qydysky/part v0.5.26 // indirect + github.com/qydysky/part v0.6.0 // indirect github.com/shirou/gopsutil v3.21.5+incompatible // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect diff --git a/demo/go.sum b/demo/go.sum index 3e22fe3..439d9d4 100644 --- a/demo/go.sum +++ b/demo/go.sum @@ -316,6 +316,8 @@ github.com/qydysky/part v0.5.25 h1:jDxxwml175gc9pcyuMGdlAX1ErLk5CWKv58Jr1UWdy4= github.com/qydysky/part v0.5.25/go.mod h1:43opuciW71sZvOR67kye50jgMDSDrn/t6+LefNdlXPg= github.com/qydysky/part v0.5.26 h1:h2rwwgE4o0CgkPKNHxWVFqFIzDcv+tj4vp3o97YmjLs= github.com/qydysky/part v0.5.26/go.mod h1:43opuciW71sZvOR67kye50jgMDSDrn/t6+LefNdlXPg= +github.com/qydysky/part v0.6.0 h1:4Akc4E7+rAbbcGwtpKTGZrnKn/hayIWj/AgTR8FhFjA= +github.com/qydysky/part v0.6.0/go.mod h1:43opuciW71sZvOR67kye50jgMDSDrn/t6+LefNdlXPg= github.com/qydysky/part/msgq v0.0.0-20201213031129-ca3253dc72ad h1:Jtzf509lQrkUMGTV0Sc6IDCAiR1VrBcHrIban7hpye4= github.com/qydysky/part/msgq v0.0.0-20201213031129-ca3253dc72ad/go.mod h1:w32TkJNVtTJd4LOS09cq+4uYG6itcN2vsqw+slp44Rg= github.com/qydysky/part/msgq v0.0.0-20201213120821-f36e49c32bba h1:1ew9dRpc0Rux0WkWeT/4AE15ynYWmL2D7onJEJIFOB8= diff --git a/go.mod b/go.mod index f218f48..fc9234e 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/mdp/qrterminal/v3 v3.0.0 github.com/miekg/dns v1.1.42 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/qydysky/part v0.5.26 + github.com/qydysky/part v0.6.0 github.com/shirou/gopsutil v3.21.5+incompatible // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 diff --git a/go.sum b/go.sum index 8a9602c..74e1bf1 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/qydysky/part v0.5.25 h1:jDxxwml175gc9pcyuMGdlAX1ErLk5CWKv58Jr1UWdy4= github.com/qydysky/part v0.5.25/go.mod h1:43opuciW71sZvOR67kye50jgMDSDrn/t6+LefNdlXPg= github.com/qydysky/part v0.5.26 h1:h2rwwgE4o0CgkPKNHxWVFqFIzDcv+tj4vp3o97YmjLs= github.com/qydysky/part v0.5.26/go.mod h1:43opuciW71sZvOR67kye50jgMDSDrn/t6+LefNdlXPg= +github.com/qydysky/part v0.6.0 h1:4Akc4E7+rAbbcGwtpKTGZrnKn/hayIWj/AgTR8FhFjA= +github.com/qydysky/part v0.6.0/go.mod h1:43opuciW71sZvOR67kye50jgMDSDrn/t6+LefNdlXPg= github.com/qydysky/part/msgq v0.0.0-20201213120821-f36e49c32bba h1:1ew9dRpc0Rux0WkWeT/4AE15ynYWmL2D7onJEJIFOB8= github.com/qydysky/part/msgq v0.0.0-20201213120821-f36e49c32bba/go.mod h1:w32TkJNVtTJd4LOS09cq+4uYG6itcN2vsqw+slp44Rg= github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -- 2.39.2