Live []string//直播流链接
Live_qn int//当前直播流质量
+ Live_want_qn int//期望直播流质量
Roomid int
Cookie syncmap.Map
CookieOk bool//
// ) // 不再需要
//允许的清晰度
-var AcceptQn = map[int]string{
- 10000:"原画",
- 800:"4K",
- 401:"蓝光(杜比)",
- 400:"蓝光",
- 250:"超清",
- 150:"高清",
- 80:"流畅",
-}
\ No newline at end of file
+
+var (
+ AcceptQn = map[int]string{
+ 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
//是否在直播
c.Liveing = j.Roominitres.Data.LiveStatus == 1
+ //未在直播,不获取直播流
+ if !c.Liveing {
+ c.Live_qn = 0
+ c.AcceptQn = c.Qn
+ c.Live = []string{}
+ return
+ }
+
//当前直播流
{
type Stream_name struct {
{
var tmp = make(map[int]string)
for _,v := range v.AcceptQn {
- if s,ok := c.AcceptQn[v];ok{
+ if s,ok := c.Qn[v];ok{
tmp[v] = s
}
}
//是否在直播
c.Liveing = j.Data.LiveStatus == 1
+ //未在直播,不获取直播流
+ if !c.Liveing {
+ c.Live_qn = 0
+ c.AcceptQn = c.Qn
+ c.Live = []string{}
+ return
+ }
+
//当前直播流
{
type Stream_name struct {
} else {
apilog.L(`T: `, `默认hls`)
}
-
- for _,v := range j.Data.PlayurlInfo.Playurl.Stream {
- if v.ProtocolName != want_type.Protocol_name {continue}
-
- for _,v := range v.Format {
- if v.FormatName != want_type.Format_name {continue}
-
- for _,v := range v.Codec {
- if v.CodecName != want_type.Codec_name {continue}
-
- //当前直播流质量
- c.Live_qn = v.CurrentQn
- //允许的清晰度
- {
- var tmp = make(map[int]string)
- for _,v := range v.AcceptQn {
- if s,ok := c.AcceptQn[v];ok{
- tmp[v] = s
+ no_found_type := true
+ for {
+ for _,v := range j.Data.PlayurlInfo.Playurl.Stream {
+ if v.ProtocolName != want_type.Protocol_name {continue}
+
+ for _,v := range v.Format {
+ if v.FormatName != want_type.Format_name {continue}
+
+ for _,v := range v.Codec {
+ if v.CodecName != want_type.Codec_name {continue}
+
+ //当前直播流质量
+ c.Live_qn = v.CurrentQn
+ //允许的清晰度
+ {
+ var tmp = make(map[int]string)
+ for _,v := range v.AcceptQn {
+ if s,ok := c.Qn[v];ok{
+ tmp[v] = s
+ }
}
+ c.AcceptQn = tmp
+ }
+ //直播流链接
+ c.Live = []string{}
+ for _,v1 := range v.URLInfo {
+ c.Live = append(c.Live, v1.Host+v.BaseURL+v1.Extra)
}
- c.AcceptQn = tmp
- }
- //直播流链接
- c.Live = []string{}
- for _,v1 := range v.URLInfo {
- c.Live = append(c.Live, v1.Host+v.BaseURL+v1.Extra)
}
}
}
+ if no_found_type {
+ if want_type.Protocol_name == "http_stream" {
+ apilog.L(`I: `, `不支持flv,使用hls`)
+ want_type = name_map[`hls`]
+ } else {
+ apilog.L(`I: `, `不支持hls,使用flv`)
+ want_type = name_map[`flv`]
+ }
+ no_found_type = false
+ } else {
+ break
+ }
}
}
}
{
AcceptQn := []int{}
for k,_ := range c.AcceptQn {
- if k <= c.Live_qn {AcceptQn = append(AcceptQn, k)}
+ if k <= c.Live_want_qn {AcceptQn = append(AcceptQn, k)}
}
MaxQn := 0
for i:=0; len(AcceptQn)>i; i+=1{
//是否在直播
c.Liveing = j.Data.LiveStatus == 1
+ //未在直播,不获取直播流
+ if !c.Liveing {
+ c.Live_qn = 0
+ c.AcceptQn = c.Qn
+ c.Live = []string{}
+ return
+ }
+
//当前直播流
{
type Stream_name struct {
apilog.L(`T: `, `默认hls`)
}
- for _,v := range j.Data.PlayurlInfo.Playurl.Stream {
- if v.ProtocolName != want_type.Protocol_name {continue}
-
- for _,v := range v.Format {
- if v.FormatName != want_type.Format_name {continue}
-
- for _,v := range v.Codec {
- if v.CodecName != want_type.Codec_name {continue}
+ no_found_type := true
+ for {
+ for _,v := range j.Data.PlayurlInfo.Playurl.Stream {
+ if v.ProtocolName != want_type.Protocol_name {continue}
+
+ for _,v := range v.Format {
+ if v.FormatName != want_type.Format_name {continue}
+
+ for _,v := range v.Codec {
+ if v.CodecName != want_type.Codec_name {continue}
+
+ no_found_type = false
- //当前直播流质量
- c.Live_qn = v.CurrentQn
- //允许的清晰度
- {
- var tmp = make(map[int]string)
- for _,v := range v.AcceptQn {
- if s,ok := c.AcceptQn[v];ok{
- tmp[v] = s
+ //当前直播流质量
+ c.Live_qn = v.CurrentQn
+ //允许的清晰度
+ {
+ var tmp = make(map[int]string)
+ for _,v := range v.AcceptQn {
+ if s,ok := c.Qn[v];ok{
+ tmp[v] = s
+ }
}
+ c.AcceptQn = tmp
+ }
+ //直播流链接
+ c.Live = []string{}
+ for _,v1 := range v.URLInfo {
+ c.Live = append(c.Live, v1.Host+v.BaseURL+v1.Extra)
}
- c.AcceptQn = tmp
- }
- //直播流链接
- c.Live = []string{}
- for _,v1 := range v.URLInfo {
- c.Live = append(c.Live, v1.Host+v.BaseURL+v1.Extra)
}
}
}
+ if no_found_type {
+ if want_type.Protocol_name == "http_stream" {
+ apilog.L(`I: `, `不支持flv,使用hls`)
+ want_type = name_map[`hls`]
+ } else {
+ apilog.L(`I: `, `不支持hls,使用flv`)
+ want_type = name_map[`flv`]
+ }
+ no_found_type = false
+ } else {
+ break
+ }
}
- if s,ok := c.AcceptQn[c.Live_qn];!ok{
+ if s,ok := c.Qn[c.Live_qn];!ok{
apilog.L(`W: `, `未知清晰度`, c.Live_qn)
} else {
apilog.L(`I: `, s)
"net/url"
"errors"
"bytes"
-
+
c "github.com/qydysky/bili_danmu/CV"
F "github.com/qydysky/bili_danmu/F"
send "github.com/qydysky/bili_danmu/Send"
defer savestream.skipFunc.UnSet()
}
- qn, ok := c.K_v.LoadV("直播流清晰度").(float64)
- if !ok || qn < 0 {return}
+ want_qn, ok := c.K_v.LoadV("直播流清晰度").(float64)
+ if !ok || want_qn < 0 {return}
+ c.Live_want_qn = int(want_qn)
- {
- AcceptQn := []int{}
- for k,_ := range c.AcceptQn {
- if k <= int(qn) {AcceptQn = append(AcceptQn, k)}
- }
- MaxQn := 0
- for i:=0; len(AcceptQn)>i; i+=1{
- if AcceptQn[i] > MaxQn {
- MaxQn = AcceptQn[i]
- }
- }
- if MaxQn == 0 {
- l.L(`W: `,"使用默认清晰度")
- }
- c.Live_qn = MaxQn
- }
+ F.Get(`Live`)
if savestream.cancel.Islive() {return}
var (
no_found_link = errors.New("no_found_link")
- hls_get_link = func(m3u8_url,last_download string) (need_download []string,m3u8_file_addition []byte,expires int,err error) {
+ hls_get_link = func(m3u8_url,last_download string) (need_download [][]string,m3u8_file_addition []byte,expires int,err error) {
url_struct,e := url.Parse(m3u8_url)
if e != nil {
err = e
r := reqf.Req()
if e := r.Reqf(reqf.Rval{
Url:m3u8_url,
- Retry:2,
+ Retry:4,
SleepTime:1,
- Timeout:4,
+ Timeout:3,
Header:map[string]string{
`Host`: url_struct.Host,
`User-Agent`: `Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0`,
if last_download == "" {
m3u8_file_addition = r.Respon
for i:=0;i<len(m4s_links);i+=1 {
- need_download = append(need_download, m4s_links[i].Url)
+ need_download = append(need_download, []string{m4s_links[i].Url, m4s_links[i].Base})
}
return
}
for ii:=i;ii<len(m4s_links);ii+=1 {
- need_download = append(need_download, m4s_links[ii].Url)
+ need_download = append(need_download, []string{m4s_links[ii].Url, m4s_links[ii].Base})
}
break
}
- found = last_download == m4s_links[i].Url
+ found = last_download == m4s_links[i].Base
}
if !found {
offset := m4s_links[1].Offset_line-1
m3u8_file_addition = m3u8_file_addition[:len(m3u8_file_addition)-1]
for i:=1;i<len(m4s_links);i+=1 {
- need_download = append(need_download, m4s_links[i].Url)
+ need_download = append(need_download, []string{m4s_links[i].Url, m4s_links[i].Base})
}
}
l.L(`I: `,"保存到", savestream.path + ".flv")
Ass_f(savestream.path, time.Now())
+ // no expect qn
+ exit_chan := make(chan struct{})
+ if c.Live_want_qn < c.Live_qn {
+ go func(){
+ for c.Live_want_qn < c.Live_qn {
+ select{
+ case <- time.After(time.Minute):;
+ case <- exit_chan:return;
+ }
+ F.Get(`Liveing`)
+ if !c.Liveing {break}
+
+ F.Get(`Live`)
+ if len(c.Live)==0 {break}
+ }
+ Savestream_wait()
+ }()
+ }
+
link := flv_get_link(c.Live[0])
if e := rr.Reqf(reqf.Rval{
Url:link,
SaveToPath:savestream.path + ".flv",
Timeout:-1,
}); e != nil{l.L(`W: `,e)}
+
+ close(exit_chan)
l.L(`I: `,"结束")
Ass_f("", time.Now())//ass
l.L(`I: `,"保存到", savestream.path)
Ass_f(savestream.path+"0", time.Now())
- last_download := ""
- expires := 0
+ var last_download = []string{"",""}
+ expires := time.Now().Add(time.Minute*2).Unix()
+
+ var (
+ path_front string
+ path_behind string
+ )
+
for savestream.cancel.Islive() {
- links,file_add,exp,e := hls_get_link(c.Live[0],last_download)
- expires = exp
- if e != nil {l.L(`E: `,e);break}
- if len(links) == 0 {
+ links_and_Base,file_add,exp,e := hls_get_link(c.Live[0],last_download[1])
+
+ if e != nil && !reqf.IsTimeout(e) {
+ l.L(`E: `,e)
+ break
+ }
+
+ if len(links_and_Base) == 0 {
time.Sleep(time.Second)
continue
}
+ //qn in expect , set expires
+ if c.Live_want_qn >= c.Live_qn {
+ expires = int64(exp)
+ }
+
+ //use guess
+ if last_download[1] != "" {
+ previou,_ := strconv.Atoi(last_download[1][:len(last_download[1])-4])
+ now,_ := strconv.Atoi(links_and_Base[0][1][:len(links_and_Base[0][1])-4])
+ if previou != now-1 {
+ if diff := now - previou;diff > 15 {
+ l.L(`W: `,`diff too large `,diff)
+ break
+ } else {
+ 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...)
+ }
+ }
+ {//links_and_Base
+ if path_front == "" || path_behind == "" {
+ u, e := url.Parse(links_and_Base[0][0])
+ if e != nil {
+ l.L(`E: `,`fault to enable guess`,e)
+ return
+ }
+ 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"
+ links_and_Base = append([][]string{[]string{
+ path_front+base+path_behind,
+ base}},links_and_Base...)
+ }
+ }
+ }
+ }
+
f := p.File()
f.FileWR(p.Filel{
File:savestream.path+"0.m3u8.dtmp",
Context:[]interface{}{file_add},
})
- for i:=0;i<len(links);i+=1 {
- filename := path.Base(links[i])
- if query_offset := strings.Index(filename, "?");query_offset != -1 {
- filename = filename[:query_offset]
- }
+ for i:=0;i<len(links_and_Base);i+=1 {
go func(url,path string){
r := reqf.Req()
if e := r.Reqf(reqf.Rval{
Url:url,
- Retry:2,
+ Retry:3,
SleepTime:1,
SaveToPath:path,
- Timeout:4,
+ Timeout:3,
}); e != nil{l.L(`W: `,e)}
- }(links[i],savestream.path+filename)
- last_download = links[i]
+ }(links_and_Base[i][0],savestream.path+links_and_Base[i][1])
+ last_download = links_and_Base[i]
}
//m3u8_url 将过期
- if expires != 0 && p.Sys().GetSTime() > int64(expires+60) {
+ if p.Sys().GetSTime()+60 > expires {
F.Get(`Liveing`)
if !c.Liveing {break}
F.Get(`Live`)
if len(c.Live)==0 {break}
+
+ // no expect qn
+ if c.Live_want_qn < c.Live_qn {
+ expires = time.Now().Add(time.Minute*2).Unix()
+ }
} else {
time.Sleep(time.Second)
}
//已func形式调用,将会停止保存直播流
func Savestream_wait(){
if !savestream.cancel.Islive() {return}
- // qn, ok := c.K_v.LoadV("flv直播流清晰度").(float64)
- // if !ok || qn < 0 {return}
savestream.cancel.Done()
c.Log.Base(`savestream`).L(`I: `,"等待停止")
)
/*
- 数据为WS_OP_MESSAGE类型的数据分派
+ 数据为WS_OP_MESSAGE类型的数据分派https://baidu.com
*/
var msglog = c.Log.Base(`Msg`)
//Msg类型数据处理方法map
var Msg_map = map[string]func(replyF, string) {
+ `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,
msglog.Base_add("Unknow").L(`E: `, s)
}
+//大乱斗pk开始
+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{
+ msglog.L(`E: `, e)
+ return
+ }
+ Gui_show(j.Data.Title,`0room`)
+ msglog.L(`I`, j.Data.Title)
+}
+
+//大乱斗pk状态
+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{
+ 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)
+ }
+}
+
//msg-特别礼物
func (replyF) vtr_gift_lottery(s string){
msglog := msglog.Base_add("特别礼物")
--- /dev/null
+package part
+
+type PK_BATTLE_PRE_NEW struct {
+ Cmd string `json:"cmd"`
+ PkStatus int `json:"pk_status"`
+ PkID int `json:"pk_id"`
+ Timestamp int `json:"timestamp"`
+ Data struct {
+ BattleType int `json:"battle_type"`
+ MatchType int `json:"match_type"`
+ Uname string `json:"uname"`
+ Face string `json:"face"`
+ UID int `json:"uid"`
+ RoomID int `json:"room_id"`
+ SeasonID int `json:"season_id"`
+ PreTimer int `json:"pre_timer"`
+ PkVotesName string `json:"pk_votes_name"`
+ EndWinTask interface{} `json:"end_win_task"`
+ } `json:"data"`
+ Roomid int `json:"roomid"`
+}
\ No newline at end of file
--- /dev/null
+package part
+
+type PK_BATTLE_PROCESS_NEW struct {
+ Cmd string `json:"cmd"`
+ PkID int `json:"pk_id"`
+ PkStatus int `json:"pk_status"`
+ Timestamp int `json:"timestamp"`
+ Data struct {
+ BattleType int `json:"battle_type"`
+ InitInfo struct {
+ RoomID int `json:"room_id"`
+ Votes int `json:"votes"`
+ BestUname string `json:"best_uname"`
+ } `json:"init_info"`
+ MatchInfo struct {
+ RoomID int `json:"room_id"`
+ Votes int `json:"votes"`
+ BestUname string `json:"best_uname"`
+ } `json:"match_info"`
+ } `json:"data"`
+}
\ No newline at end of file
--- /dev/null
+package part
+
+type PK_BATTLE_START_NEW struct {
+ Cmd string `json:"cmd"`
+ PkID int `json:"pk_id"`
+ PkStatus int `json:"pk_status"`
+ Timestamp int `json:"timestamp"`
+ Data struct {
+ BattleType int `json:"battle_type"`
+ FinalHitVotes int `json:"final_hit_votes"`
+ PkStartTime int `json:"pk_start_time"`
+ PkFrozenTime int `json:"pk_frozen_time"`
+ PkEndTime int `json:"pk_end_time"`
+ PkVotesType int `json:"pk_votes_type"`
+ PkVotesAdd int `json:"pk_votes_add"`
+ PkVotesName string `json:"pk_votes_name"`
+ StarLightMsg string `json:"star_light_msg"`
+ } `json:"data"`
+ Roomid string `json:"roomid"`
+}
\ No newline at end of file
--- /dev/null
+package part
+
+type PK_LOTTERY_START struct {
+ Cmd string `json:"cmd"`
+ Data struct {
+ AssetAnimationPic string `json:"asset_animation_pic"`
+ AssetIcon string `json:"asset_icon"`
+ FromUser struct {
+ Face string `json:"face"`
+ UID int `json:"uid"`
+ Uname string `json:"uname"`
+ } `json:"from_user"`
+ ID int `json:"id"`
+ MaxTime int `json:"max_time"`
+ PkID int `json:"pk_id"`
+ RoomID int `json:"room_id"`
+ ThankText string `json:"thank_text"`
+ Time int `json:"time"`
+ TimeWait int `json:"time_wait"`
+ Title string `json:"title"`
+ Weight int `json:"weight"`
+ } `json:"data"`
+}