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)
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
}
}
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
+)
//短信登录
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
})
{ //获取牌子列表
- 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),
},
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
}
{
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: `, `无主播粉丝牌`)
--- /dev/null
+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"`
+}
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"
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"
)
/*
//字符重复度检查
//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的每个字符串中
//*单字符串中的重复出现计为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
}
//功能区
//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
}
}
//设定字幕文件名,为""时停止输出
-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"
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,
})
}
//直播流保存
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
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 {
},
})
//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<len(m3u8_urls);index+=1 {
+ for index := 0; index < len(m3u8_urls); index += 1 {
r = reqf.New()
- if tmp,e := url.Parse(m3u8_urls[index]);e != nil {
+ if tmp, e := url.Parse(m3u8_urls[index]); e != nil {
err = e
return
} else {
m3u8_url = tmp
}
-
+
rval := reqf.Rval{
- Url:m3u8_url.String(),
- ConnectTimeout:2000,
- ReadTimeout:1000,
- Timeout:2000,
- Proxy:c.Proxy,
- Header:map[string]string{
- `Host`: m3u8_url.Host,
- `User-Agent`: `Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0`,
- `Accept`: `*/*`,
+ Url: m3u8_url.String(),
+ ConnectTimeout: 2000,
+ ReadTimeout: 1000,
+ Timeout: 2000,
+ Proxy: c.Proxy,
+ Header: map[string]string{
+ `Host`: m3u8_url.Host,
+ `User-Agent`: `Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.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`,
`Accept-Encoding`: `gzip, deflate, br`,
- `Origin`: `https://live.bilibili.com`,
- `Connection`: `keep-alive`,
- `Pragma`: `no-cache`,
- `Cache-Control`: `no-cache`,
- `Referer`:"https://live.bilibili.com/",
+ `Origin`: `https://live.bilibili.com`,
+ `Connection`: `keep-alive`,
+ `Pragma`: `no-cache`,
+ `Cache-Control`: `no-cache`,
+ `Referer`: "https://live.bilibili.com/",
},
}
if !last_hls_Modified.IsZero() {
rval.Header[`If-Modified-Since`] = last_hls_Modified.Add(time.Second).Format("Mon, 02 Jan 2006 15:04:05 CST")
}
- if e := r.Reqf(rval);e != nil {
- if index+1<len(m3u8_urls) {continue}
+ if e := r.Reqf(rval); e != nil {
+ if index+1 < len(m3u8_urls) {
+ continue
+ }
err = e
return
}
break
}
- if usedt := r.UsedTime.Seconds();usedt > 3000 {
+ if usedt := r.UsedTime.Seconds(); usedt > 3000 {
l.L(`I: `, `hls列表下载慢`, usedt, `ms`)
}
if r.Response.StatusCode == http.StatusNotModified {
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)
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
var m4s_links []*m4s_link_item
lines := bytes.Split(buf, []byte("\n"))
- for i:=0;i<len(lines);i+=1 {
+ for i := 0; i < len(lines); i += 1 {
line := lines[i]
m4s_link := ""
if bytes.Contains(line, []byte("EXT-X-MAP")) {
- o := bytes.Index(line,[]byte(`EXT-X-MAP:URI="`)) + 15
- e := bytes.Index(line[o:],[]byte(`"`)) + o
+ o := bytes.Index(line, []byte(`EXT-X-MAP:URI="`)) + 15
+ e := bytes.Index(line[o:], []byte(`"`)) + o
m4s_link = string(line[o:e])
} else if bytes.Contains(line, []byte(".m4s")) {
m4s_link = string(line)
}
- if m4s_link == "" {continue}
+ if m4s_link == "" {
+ continue
+ }
- u, e := url.Parse("./"+m4s_link+"?trid="+trid)
+ u, e := url.Parse("./" + m4s_link + "?trid=" + trid)
if e != nil {
err = e
return
}
m4s_links = append(m4s_links, &m4s_link_item{
- Url:m3u8_url.ResolveReference(u).String(),
- Base:m4s_link,
- Offset_line:i,
+ Url: m3u8_url.ResolveReference(u).String(),
+ Base: m4s_link,
+ Offset_line: i,
})
}
if len(m4s_links) == 0 {
}
var found bool
- for i:=0;i<len(m4s_links);i+=1 {
+ for i := 0; i < len(m4s_links); i += 1 {
if found {
- offset := m4s_links[i].Offset_line-1
- for i:=offset;i<len(lines);i+=1 {
+ offset := m4s_links[i].Offset_line - 1
+ for i := offset; i < len(lines); i += 1 {
m3u8_file_addition = append(m3u8_file_addition, lines[i]...)
- m3u8_file_addition = append(m3u8_file_addition,[]byte("\n")...)
+ m3u8_file_addition = append(m3u8_file_addition, []byte("\n")...)
}
m3u8_file_addition = m3u8_file_addition[:len(m3u8_file_addition)-1]
found = (*last_download).Base == m4s_links[i].Base
}
if !found {
- offset := m4s_links[1].Offset_line-1
- for i:=offset;i<len(lines);i+=1 {
+ offset := m4s_links[1].Offset_line - 1
+ for i := offset; i < len(lines); i += 1 {
m3u8_file_addition = append(m3u8_file_addition, lines[i]...)
- m3u8_file_addition = append(m3u8_file_addition,[]byte("\n")...)
+ m3u8_file_addition = append(m3u8_file_addition, []byte("\n")...)
}
m3u8_file_addition = m3u8_file_addition[:len(m3u8_file_addition)-1]
return
}
- flv_get_link = func(link string) (need_download string,expires int,err error) {
+ flv_get_link = func(link string) (need_download string, expires int, err error) {
need_download = link
- url_struct,e := url.Parse(link)
+ url_struct, e := url.Parse(link)
if e != nil {
err = e
return
}
query := url_struct.Query()
- expires,_ = strconv.Atoi(query.Get("expires"))
+ expires, _ = strconv.Atoi(query.Get("expires"))
return
}
for {
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
+ }
savestream.path = savestream.base_path
savestream.cancel = s.Init()
CookieM := make(map[string]string)
- c.Cookie.Range(func(k,v interface{})(bool){
+ c.Cookie.Range(func(k, v interface{}) bool {
CookieM[k.(string)] = v.(string)
return true
})
- {//重试
+ { //重试
r := reqf.New()
- go func(){
+ go func() {
savestream.cancel.Wait()
r.Close()
}()
- l.L(`I: `,"尝试连接live")
+ l.L(`I: `, "尝试连接live")
if e := r.Reqf(reqf.Rval{
- Url:c.Live[0],
- Retry:10,
- SleepTime:1000,
- Proxy:c.Proxy,
- Header:map[string]string{
- `Cookie`:reqf.Map_2_Cookies_String(CookieM),
+ Url: c.Live[0],
+ Retry: 10,
+ SleepTime: 1000,
+ Proxy: c.Proxy,
+ Header: map[string]string{
+ `Cookie`: reqf.Map_2_Cookies_String(CookieM),
},
- Timeout:5*1000,
- JustResponseCode:true,
- }); e != nil{l.L(`W: `,e)}
+ Timeout: 5 * 1000,
+ JustResponseCode: true,
+ }); e != nil {
+ l.L(`W: `, e)
+ }
if r.Response == nil {
- l.L(`W: `,`live响应错误`)
+ l.L(`W: `, `live响应错误`)
savestream.wait.Done()
savestream.cancel.Done()
- time.Sleep(time.Second*5)
+ time.Sleep(time.Second * 5)
continue
} else if r.Response.StatusCode != 200 {
- l.L(`W: `,`live响应错误`,r.Response.Status,string(r.Respon))
+ l.L(`W: `, `live响应错误`, r.Response.Status, string(r.Respon))
savestream.wait.Done()
savestream.cancel.Done()
- time.Sleep(time.Second*5)
+ time.Sleep(time.Second * 5)
continue
}
}
- if strings.Contains(c.Live[0],"flv") {
- if rel, err := filepath.Rel(savestream.base_path, savestream.path);err == nil {
- l.L(`I: `,"保存到", rel + ".flv")
+ if strings.Contains(c.Live[0], "flv") {
+ if rel, err := filepath.Rel(savestream.base_path, savestream.path); err == nil {
+ l.L(`I: `, "保存到", rel+".flv")
} else {
- l.L(`I: `,"保存到", savestream.path + ".flv")
+ l.L(`I: `, "保存到", savestream.path+".flv")
l.L(`W: `, err)
}
Ass_f(savestream.path, time.Now())
// no expect qn
exit_chan := s.Init()
- go func(){
+ go func() {
savestream.cancel.Wait()
exit_chan.Done()
}()
type link_stream struct {
- id *idpool.Id
- front []byte
+ id *idpool.Id
+ front []byte
keyframe [][]byte
// sync_buf []byte
close func()
//chans
var (
- reqs = msgq.New(10)
+ reqs = msgq.New(10)
id_pool = idpool.New()
)
//文件
out, err := os.Create(savestream.path + ".flv" + ".dtmp")
if err != nil {
- l.L(`E: `,err)
+ l.L(`E: `, err)
return
}
//数据整合
{
type id_close struct {
- id uintptr
+ id uintptr
close func()
}
var (
- reqs_used_id []id_close
+ reqs_used_id []id_close
reqs_remove_id []id_close
reqs_keyframe [][][]byte
- reqs_func_block funcCtrl.BlockFunc
+ reqs_func_block funcCtrl.BlockFunc
last_keyframe_timestamp int
)
- reqs.Pull_tag(map[string]func(interface{})(bool){
- `req`:func(data interface{})(bool){
- req,ok := data.(link_stream)
+ reqs.Pull_tag(map[string]func(interface{}) bool{
+ `req`: func(data interface{}) bool {
+ req, ok := data.(link_stream)
- if !ok {return false}
+ if !ok {
+ return false
+ }
if len(req.keyframe) == 0 {
// fmt.Println(`没有keyframe,退出`)
}
// fmt.Println(`处理req_id`,req.id.Id,`keyframe_len`,len(req.keyframe))
- if offset,_ := out.Seek(0,1);offset == 0 {
+ if offset, _ := out.Seek(0, 1); offset == 0 {
// fmt.Println(`添加头`,len(req.front))
//stream
savestream.front = req.front
reqs_func_block.Block()
defer reqs_func_block.UnBlock()
- for i:=0;i<len(reqs_remove_id);i+=1 {
+ for i := 0; i < len(reqs_remove_id); i += 1 {
if reqs_remove_id[i].id == req.id.Id {
req.close()
return false
var reqs_keyframe_index int = len(reqs_used_id)
{
var isnew bool = true
- for i:=0;i<len(reqs_used_id);i+=1 {
+ for i := 0; i < len(reqs_used_id); i += 1 {
if reqs_used_id[i].id == req.id.Id {
reqs_keyframe_index = i
isnew = false
if isnew {
// fmt.Println(`新req`,req.id.Id,reqs_keyframe_index)
reqs_used_id = append(reqs_used_id, id_close{
- id:req.id.Id,
- close:req.close,
+ id: req.id.Id,
+ close: req.close,
})
}
}
if len(reqs_used_id) == 1 {
// l.L(`T: `,"单req写入",len(req.keyframe))
- last_keyframe_timestamp,_ = Keyframe_timebase(req.keyframe,last_keyframe_timestamp)
+ last_keyframe_timestamp, _ = Keyframe_timebase(req.keyframe, last_keyframe_timestamp)
- for i:=0;i<len(req.keyframe);i+=1 {
+ for i := 0; i < len(req.keyframe); i += 1 {
//stream
- savestream.stream.Push_tag("stream",req.keyframe[i])
+ savestream.stream.Push_tag("stream", req.keyframe[i])
out.Write(req.keyframe[i])
}
return false
// fmt.Println(`merge,添加reqs_keyframe数据`,reqs_keyframe_index,len(reqs_keyframe[reqs_keyframe_index]))
- for _,v := range reqs_keyframe {
+ for _, v := range reqs_keyframe {
if len(v) == 0 {
// fmt.Println(`merge,req无数据`,k)
return false
}
}
- if success_last_keyframe_timestamp,b,merged := Merge_stream(reqs_keyframe,last_keyframe_timestamp);merged == 0 {
+ if success_last_keyframe_timestamp, b, merged := Merge_stream(reqs_keyframe, last_keyframe_timestamp); merged == 0 {
// fmt.Println(`merge失败,reqs_keyframe[1]`,reqs_keyframe[1][0][:11],reqs_keyframe[1][len(reqs_keyframe[1])-1][:11])
size := 0
- for i:=1;i<len(reqs_keyframe);i+=1 {size += len(reqs_keyframe[i])}
+ for i := 1; i < len(reqs_keyframe); i += 1 {
+ size += len(reqs_keyframe[i])
+ }
if reqs_keyframe_index == 0 {
// l.L(`T: `,"flv拼合失败,reqs_keyframe[0]写入")
// fmt.Println(`merge失败,reqs_keyframe[0]写入`,len(req.keyframe))
- last_keyframe_timestamp,_ = Keyframe_timebase(req.keyframe,last_keyframe_timestamp)
+ last_keyframe_timestamp, _ = Keyframe_timebase(req.keyframe, last_keyframe_timestamp)
- for i:=0;i<len(req.keyframe);i+=1 {
+ for i := 0; i < len(req.keyframe); i += 1 {
//stream
- savestream.stream.Push_tag("stream",req.keyframe[i])
+ savestream.stream.Push_tag("stream", req.keyframe[i])
out.Write(req.keyframe[i])
}
// reqs_keyframe[0] = [][]byte{reqs_keyframe[0][len(reqs_keyframe[0])-1]}
} else if size > 4 {
if reqs_keyframe_index == len(reqs_used_id)-1 {
- l.L(`T: `,"flv强行拼合")
+ l.L(`T: `, "flv强行拼合")
- for i:=0;i<reqs_keyframe_index;i+=1 {
+ for i := 0; i < reqs_keyframe_index; i += 1 {
reqs_remove_id = append(reqs_remove_id, reqs_used_id[i])
reqs_used_id[i].close()
}
reqs_used_id = reqs_used_id[reqs_keyframe_index:]
- last_keyframe_timestamp,_ = Keyframe_timebase(req.keyframe,last_keyframe_timestamp)
+ last_keyframe_timestamp, _ = Keyframe_timebase(req.keyframe, last_keyframe_timestamp)
- for i:=0;i<len(req.keyframe);i+=1 {
+ for i := 0; i < len(req.keyframe); i += 1 {
//stream
- savestream.stream.Push_tag("stream",req.keyframe[i])
+ savestream.stream.Push_tag("stream", req.keyframe[i])
out.Write(req.keyframe[i])
}
}
} else {
// fmt.Println(`merge成功`,len(b))
- l.L(`T: `,"flv拼合成功")
+ l.L(`T: `, "flv拼合成功")
last_keyframe_timestamp = success_last_keyframe_timestamp
- for i:=0;i<merged;i+=1 {
+ for i := 0; i < merged; i += 1 {
reqs_remove_id = append(reqs_remove_id, reqs_used_id[i])
reqs_used_id[i].close()
}
reqs_used_id = reqs_used_id[merged:]
//stream
- savestream.stream.Push_tag("stream",b)
+ savestream.stream.Push_tag("stream", b)
out.Write(b)
}
return false
},
// 11区 1
- `close`:func(data interface{})(bool){
+ `close`: func(data interface{}) bool {
// defer l.L(`I: `,"处理退出")
- for i:=0;i<len(reqs_used_id);i+=1 {
+ for i := 0; i < len(reqs_used_id); i += 1 {
reqs_used_id[i].close()
}
reqs_used_id = []id_close{}
live_index := 0
if len(c.Live) > 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)
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
}
{
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 {
//退出
}
}
- 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")...)
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", "")}
//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...)
//去除最后一个换行
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<len(hls_gen.m4s_list);i+=1 {
+ for i := 0; i < len(hls_gen.m4s_list); i += 1 {
if hls_gen.m4s_list[i].status != s_fin {
catch = false
continue
//find a nice index, remove all bad fmp4s
skip := 0
skip_del = true
- 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:]...)
+ 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
}
}
}
//设置到全局变量,方便流服务器获取
- 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
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)
}
}
}
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()
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
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
}
}
if last_download == nil {
var res []byte
{
- for row,i:=bytes.SplitAfter(file_add, []byte("\n")),0;i<len(row);i+=1 {
+ for row, i := bytes.SplitAfter(file_add, []byte("\n")), 0; i < len(row); i += 1 {
if bytes.Contains(row[i], []byte("#EXT")) {
//set stream front
- if op := bytes.Index(row[i], []byte(`#EXT-X-MAP:URI="`));op != -1 {
- hls_gen.hls_first_fmp4_name = string(row[i][op+16:len(row[i])-2])
+ if op := bytes.Index(row[i], []byte(`#EXT-X-MAP:URI="`)); op != -1 {
+ hls_gen.hls_first_fmp4_name = string(row[i][op+16 : len(row[i])-2])
+ }
+ if bytes.Contains(row[i], []byte("#EXT-X-MEDIA-SEQUENCE")) || bytes.Contains(row[i], []byte("#EXTINF")) {
+ continue
}
- if bytes.Contains(row[i], []byte("#EXT-X-MEDIA-SEQUENCE")) || bytes.Contains(row[i], []byte("#EXTINF")){continue}
res = append(res, row[i]...)
}
}
}
if len(links) == 0 {
- time.Sleep(time.Duration(2)*time.Second)
+ time.Sleep(time.Duration(2) * time.Second)
continue
}
//use guess
if last_download != nil {
- previou,_ := strconv.Atoi((*last_download).Base[:len((*last_download).Base)-4])
- now,_ := strconv.Atoi(links[0].Base[:len(links[0].Base)-4])
+ previou, _ := strconv.Atoi((*last_download).Base[:len((*last_download).Base)-4])
+ now, _ := strconv.Atoi(links[0].Base[:len(links[0].Base)-4])
if previou < now-1 {
- if diff := now - previou;diff > 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...)
}
}
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
}
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<len(links);i+=1 {
+ for i := 0; i < len(links); i += 1 {
//fmp4切片下载
- go func(link *m4s_link_item,path string){
+ go func(link *m4s_link_item, path string) {
//use url struct
var link_url *url.URL
{
- if tmp,e := url.Parse(link.Url);e != nil {
+ if tmp, e := url.Parse(link.Url); e != nil {
l.L(`E: `, e)
return
- } else {link_url = tmp}
+ } else {
+ link_url = tmp
+ }
}
download_limit.Block()
defer download_limit.UnBlock()
link.status = s_loading
- for index:=0;index<len(Host_list);index+=1 {
+ for index := 0; index < len(Host_list); index += 1 {
link_url.Host = Host_list[index]
r := reqf.New()
if e := r.Reqf(reqf.Rval{
- Url: link_url.String(),
- SaveToPath: path + link.Base,
+ Url: link_url.String(),
+ SaveToPath: path + link.Base,
ConnectTimeout: 2000,
- ReadTimeout: 1000,
- Timeout: 2000,
- Proxy: c.Proxy,
- });e != nil{
+ ReadTimeout: 1000,
+ Timeout: 2000,
+ Proxy: c.Proxy,
+ }); e != nil {
//try other host
- if index+1<len(Host_list) {continue}
+ if index+1 < len(Host_list) {
+ continue
+ }
if reqf.IsTimeout(e) || strings.Contains(e.Error(), "x509") {
l.L(`T: `, link.Base, `将重试!`)
//避免影响后续猜测
link.Offset_line = 0
- go func(link *m4s_link_item){miss_download <- link}(link)
+ go func(link *m4s_link_item) { miss_download <- link }(link)
} else {
l.L(`W: `, e)
link.status = s_fail
}
} else {
- if usedt := r.UsedTime.Seconds();usedt > 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 {
//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")
}
}
//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
}
//已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
}
}
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)
}
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)
}
}
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
}
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)
}
}()
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()
}
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 {
}
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)
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
}
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()
}
//保存所有消息到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
}
//直播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
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", "*")
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
}
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)
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()
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 {
}
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")
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()
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 {
}
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
}
}
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")
}
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
//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)
}
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", "*")
//最新直播流
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
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)
+}
import (
"encoding/json"
+ "io/ioutil"
+
c "github.com/qydysky/bili_danmu/CV"
- s "github.com/qydysky/part/buf"
)
/*
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 {
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
}
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`)
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:]
//所有的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{}{">天选"}
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, ")")
}
}
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
}
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====")
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
)
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{}
if content != nil {
sh = append(sh, "节奏风暴", content)
}
- {//额外
+ { //额外
Assf(fmt.Sprintln(sh...))
}
fmt.Println("\n====")
}
//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{}
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====")
}
//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)
}
//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"
}
//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
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====")
//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 {
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()
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" {
//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
}
}
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...)
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,
},
})
}
logg = append(logg, message_jpn)
}
fmt.Print("====\n\n")
-
- {//额外
+
+ { //额外
Assf(fmt.Sprintln(sh...))
// Gui_show("====\n")
Gui_show(Itos(sh), "0superchat")
}
//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 != `` {
}
//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 = `获得:`
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 != `` {
}
//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, `<%`, ``)
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,
},
})
}
}
//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{}
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)))
}
msglog := msglog.Log_show_control(false)
- {//附加功能 弹幕机 封禁 弹幕合并
+ { //附加功能 弹幕机 封禁 弹幕合并
go Danmujif(item.msg)
if Autobanf(item.msg) {
Gui_show(Itos([]interface{}{"风险", item.auth, ":", item.msg}))
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)
}
//弹幕发送
//传入字符串即可发送
//需要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)
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
+}
-//+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:
}
},
})
//
- 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()
}
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{}{
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)
+ }
}
//先展示弹幕窗
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
})
}
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
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 += `,`
}
}
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))
}
}
}
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:
}
})
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)
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
- }
})
}
})
*/
/*
- 同时太多ToWidget().Destroy()将会卡死gtk
+ 同时太多ToWidget().Destroy()将会卡死gtk
*/
/*
gotk3存在内存泄漏,发生在C
-*/
\ No newline at end of file
+*/
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)
}
}
//启动程序
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))
}
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
})
}
-
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...)
}
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()
}
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 == ``) {
//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` {
}
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`)
}
//@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)
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"`
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...)
}
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
}
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"`
} `json:"data"`
}
- {//msg
+ { //msg
var postS = rec{}
postS.Common.AppID = xfId
postS.Business.Aue = "lame"
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() {
}
}
return nil
-}
\ No newline at end of file
+}
--- /dev/null
+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"`
+}
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
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=
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
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=