From f8d48fbee1a43eb7bb18d3458508f43e8e61e28c Mon Sep 17 00:00:00 2001 From: qydysky Date: Sun, 18 Apr 2021 23:46:08 +0800 Subject: [PATCH] =?utf8?q?=E6=8F=90=E9=AB=98hls=E8=8E=B7=E5=8F=96=E6=88=90?= =?utf8?q?=E5=8A=9F=E7=8E=87=EF=BC=8C=E6=8F=90=E9=AB=98=E6=B5=81=E6=9C=8D?= =?utf8?q?=E5=8A=A1=E6=B5=81=E7=95=85=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- F/api.go | 16 +++---- README.md | 8 +++- Reply/F.go | 114 +++++++++++++++++++++++++------------------------ Reply/Reply.go | 6 +-- bili_danmu.go | 4 +- 5 files changed, 78 insertions(+), 70 deletions(-) diff --git a/F/api.go b/F/api.go index 46b1d49..9a8be44 100644 --- a/F/api.go +++ b/F/api.go @@ -328,12 +328,12 @@ func Html() (missKey []string) { }, } - want_type := name_map[`flv`] + want_type := name_map[`hls`] if v,ok := c.K_v.LoadV(`直播流类型`).(string);ok { if v,ok := name_map[v];ok { want_type = v } else { - apilog.L(`I: `, `未找到`,v,`,默认flv`) + apilog.L(`I: `, `未找到`,v,`,默认hls`) } } else { apilog.L(`T: `, `默认flv`) @@ -570,15 +570,15 @@ func getRoomPlayInfo() (missKey []string) { }, } - want_type := name_map[`flv`] + want_type := name_map[`hls`] if v,ok := c.K_v.LoadV(`直播流类型`).(string);ok { if v,ok := name_map[v];ok { want_type = v } else { - apilog.L(`I: `, `未找到`,v,`,默认flv`) + apilog.L(`I: `, `未找到`,v,`,默认hls`) } } else { - apilog.L(`T: `, `默认flv`) + apilog.L(`T: `, `默认hls`) } for _,v := range j.Data.PlayurlInfo.Playurl.Stream { @@ -708,15 +708,15 @@ func getRoomPlayInfoByQn() (missKey []string) { }, } - want_type := name_map[`flv`] + want_type := name_map[`hls`] if v,ok := c.K_v.LoadV(`直播流类型`).(string);ok { if v,ok := name_map[v];ok { want_type = v } else { - apilog.L(`I: `, `未找到`,v,`,默认flv`) + apilog.L(`I: `, `未找到`,v,`,默认hls`) } } else { - apilog.L(`T: `, `默认flv`) + apilog.L(`T: `, `默认hls`) } for _,v := range j.Data.PlayurlInfo.Playurl.Stream { diff --git a/README.md b/README.md index 8456ebc..e879028 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ golang go version go1.15 linux/amd64 - [x] 排行榜 #### 当前支持功能 -以下内容可能过时,点击查看[~~当前支持功能(旧)~~](https://github.com/qydysky/bili_danmu/blob/9731f69d06a15645c6cb1d41379520a5a3f519f1/Reply/F.go#L26),[功能配置(新)](https://github.com/qydysky/bili_danmu/blob/maintenance/demo/config/config_K_v.json) +以下内容可能过时,点击查看[功能配置](https://github.com/qydysky/bili_danmu/blob/maintenance/demo/config/config_K_v.json) +- [x] 直播流服务 - [x] 每天自动发送将要过期的银瓜子礼物(默认发送3天内过期的) - [x] 保持当前已点亮的粉丝牌总是点亮 - [x] 银瓜子自动兑换硬币 @@ -111,8 +112,11 @@ golang go version go1.15 linux/amd64 开启之后,启动会显示服务地址,在局域网内打开网址可以取得所有直播流的串流地址。 - dtmp结尾:当前正在获取的流,播放此链接时进度将保持当前流进度 -- flv结尾:保存完毕的直播流,播放此链接时将从头开始播放 +- flv/m3u8结尾:保存完毕的直播流,播放此链接时将从头开始播放 - ass结尾:保存完毕的直播流字幕,有些播放器会在串流时获取此文件 +- m4s结尾:hls切片 + +**特殊的:路径为`/now`(例:当服务地址为下方的38259口时,此对应的路径为`http://192.168.31.245:38259/now`),会重定向到当前正在获取的流,播放此链接时进度将保持当前流进度** 服务地址也可通过命令行` room`查看。 diff --git a/Reply/F.go b/Reply/F.go index 7f3d782..75f130c 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -218,9 +218,9 @@ func init(){ c.Danmu_Main_mq.Pull_tag(msgq.FuncMap{ `savestream`:func(data interface{})(bool){ if savestream.cancel.Islive() { - Saveflv_wait() + Savestream_wait() } else { - go Saveflvf() + go Savestreamf() } return false @@ -229,7 +229,7 @@ func init(){ } //已go func形式调用,将会获取直播流 -func Saveflvf(){ +func Savestreamf(){ l := c.Log.Base(`savestream`) //避免多次开播导致的多次触发 @@ -265,28 +265,6 @@ func Saveflvf(){ var ( no_found_link = errors.New("no_found_link") - // next_link = func(links []string,last_link string) (link string,err error) { - // if len(links) == 0 { - // err = no_found_link - // return - // } - - // var found bool - - // link = links[0] - - // if last_link == "" {return} - - // for i:=0;i= len(c.Live) {cuLinkIndex = 0} time.Sleep(time.Second*5) continue } } - stream_type_is_flv := strings.Contains(c.Live[0],"flv") - - if stream_type_is_flv { + if strings.Contains(c.Live[0],"flv") { l.L(`I: `,"保存到", savestream.path + ".flv") Ass_f(savestream.path, time.Now()) @@ -525,7 +498,7 @@ func Saveflvf(){ Retry:2, SleepTime:1, SaveToPath:path, - Timeout:3, + Timeout:4, }); e != nil{l.L(`W: `,e)} }(links[i],savestream.path+filename) last_download = links[i] @@ -549,7 +522,7 @@ func Saveflvf(){ if !savestream.cancel.Islive() {break}//cancel /* - Saveflv需要外部组件 + Savestream需要外部组件 ffmpeg http://ffmpeg.org/download.html */ // if p.Checkfile().IsExist(savestream.path+".flv"){ @@ -567,7 +540,7 @@ func Saveflvf(){ } //已func形式调用,将会停止保存直播流 -func Saveflv_wait(){ +func Savestream_wait(){ if !savestream.cancel.Islive() {return} // qn, ok := c.K_v.LoadV("flv直播流清晰度").(float64) // if !ok || qn < 0 {return} @@ -1161,6 +1134,7 @@ func AutoSend_silver_gift() { //直播保存位置Web服务 func init() { + flog := flog.Base_add(`直播Web服务`) if port_f,ok := c.K_v.LoadV(`直播保存位置Web服务`).(float64);ok && port_f >= 0 { port := int(port_f) @@ -1182,25 +1156,20 @@ func init() { }) s.Handle(map[string]func(http.ResponseWriter,*http.Request){ `/`:func(w http.ResponseWriter,r *http.Request){ - //header w.Header().Set("Access-Control-Allow-Origin", "*") var path string = r.URL.Path[1:] - if filepath.Ext(path) == `.dtmp` { - // //最新直播流 - // if path == `now` && savestream.path != `` { - // path = filepath.Base(savestream.path)+".flv.dtmp" - // } + if !p.Checkfile().IsExist(base_dir+path) { + w.WriteHeader(http.StatusNotFound) + return + } + + if filepath.Ext(path) == `.dtmp` { if strings.Contains(path,"flv") { path = base_dir+path - if !p.Checkfile().IsExist(path) { - w.WriteHeader(http.StatusNotFound) - return - } - w.Header().Set("Connection", "Keep-Alive") w.Header().Set("Content-Type", "video/x-flv") w.Header().Set("X-Content-Type-Options", "nosniff") @@ -1212,7 +1181,7 @@ func init() { go func(){ if err := Stream(path,byteC,cancel);err != nil { - flog.Base_add(`直播Web服务`).L(`T: `,err); + flog.L(`T: `,err); return } }() @@ -1236,7 +1205,7 @@ func init() { f,err := os.OpenFile(m3u8_file,os.O_RDONLY,0644) if err != nil { - flog.Base_add(`直播Web服务`).L(`E: `,err); + flog.L(`E: `,err); return } defer f.Close() @@ -1244,12 +1213,13 @@ func init() { w.Header().Set("Cache-Control", "max-age=1") w.Header().Set("Content-Type", "application/vnd.apple.mpegurl") w.Header().Set("Connection", "Keep-Alive") + w.WriteHeader(http.StatusOK) var res []byte { buf := make([]byte, 200) if _,err := f.Read(buf);err != nil { - flog.Base_add(`直播Web服务`).L(`E: `,err); + flog.L(`E: `,err); return } fin_offset := bytes.LastIndex(buf, []byte("EXT-X-MEDIA-SEQUENCE:"))+21 @@ -1299,7 +1269,7 @@ func init() { break } - if sign := buf[m4s_end_offset-6] - 48;sign == 0 || sign == 5 {break} + if sign := buf[m4s_end_offset-6] - 48;sign%3 == 0 {break} start_offset += 7 } @@ -1314,7 +1284,7 @@ func init() { } else { break } - if i>15 {break} + if i>6 {break} end_offset += 7 } // end_offset = bytes.LastIndex(buf, []byte("#EXTINF")) @@ -1328,7 +1298,7 @@ func init() { A,B,e := seed_m4s(f) if e != nil { - flog.Base_add(`直播Web服务`).L(`E: `,`error when seed_m4s`, e); + flog.L(`E: `,`error when seed_m4s`, e); return } @@ -1338,7 +1308,7 @@ func init() { } if _,err = w.Write(res);err != nil { - flog.Base_add(`直播Web服务`).L(`E: `,err); + flog.L(`E: `,err); return } } @@ -1346,12 +1316,46 @@ func init() { http.FileServer(http.Dir(base_dir)).ServeHTTP(w,r) } }, + `/now`:func(w http.ResponseWriter,r *http.Request){ + //header + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Cache-Control", "max-age=3") + + //最新直播流 + if savestream.path == `` { + flog.L(`I: `,`还没有下载直播流`) + w.WriteHeader(http.StatusNotFound) + return + } + + path := filepath.Base(savestream.path) + if strings.Contains(c.Live[0],"flv") { + path += ".flv.dtmp" + } else { + path += "/0.m3u8.dtmp" + } + + if !p.Checkfile().IsExist(base_dir+path) { + flog.L(`I: `,`还没有下载直播流`) + w.WriteHeader(http.StatusNotFound) + } else { + u, e := url.Parse("../"+path) + if e != nil { + flog.L(`E: `,e) + w.WriteHeader(http.StatusServiceUnavailable) + return + } + w.Header().Set("Location", r.URL.ResolveReference(u).String()) + w.WriteHeader(http.StatusTemporaryRedirect) + } + return + }, `/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.Base_add(`直播Web服务`).L(`I: `,`启动于`,c.Stream_url) + flog.L(`I: `,`启动于`,c.Stream_url) } } \ No newline at end of file diff --git a/Reply/Reply.go b/Reply/Reply.go index c4efe13..f4e6965 100644 --- a/Reply/Reply.go +++ b/Reply/Reply.go @@ -435,10 +435,10 @@ func (replyF) preparing(s string) { msglog.L(`E: `, "roomid", roomid) return } else { - {//附加功能 obs结束 saveflv结束 + {//附加功能 obs结束 `savestream`结束 Obs_R(false) Obsf(false) - Saveflv_wait() + Savestream_wait() go ShowRevf() c.Liveing = false } @@ -463,7 +463,7 @@ func (replyF) live(s string) { {//附加功能 obs录播 Obsf(true) Obs_R(true) - go Saveflvf() + go Savestreamf() } { c.Rev = 0.0 //营收 diff --git a/bili_danmu.go b/bili_danmu.go index 8e430b7..7bbacd8 100644 --- a/bili_danmu.go +++ b/bili_danmu.go @@ -261,7 +261,7 @@ func Demo(roomid ...int) { {//附加功能 进房间发送弹幕 直播流保存 营收 go reply.Entry_danmu() - go reply.Saveflvf() + go reply.Savestreamf() go reply.ShowRevf() //小心心 go F.F_x25Kn() @@ -296,7 +296,7 @@ func Demo(roomid ...int) { if break_sign {break} } {//附加功能 直播流停止 - reply.Saveflv_wait() + reply.Savestream_wait() reply.Save_to_json(-1, []interface{}{`{}]`}) } p.Sys().Timeoutf(1) -- 2.39.2