抓取直播源教程_国外浏览器有哪些

(77) 2024-06-11 13:01:01

抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第1张
距离上次抓取直播源已经过去一年了,最近发现官方已更新了,以前的方法并不适用了,于是再来一波解析,小伙伴们准备好发车了吗?

一、版本回顾

之前直播源解析是从H5版本入手,直接从H5页面拿到全局变量liveLineUrl,这是一个m3u8的播放地址,可以用VLC等播放器直接播放;另发现 一起看 模块的直播源地址和一般房间不同,fm参数已经变成了seqid
抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第2张

如今版本发现全局变量中的liveLineUrl已经变成加密的了,类似 Ly9hbC5obHMuaHV5YS5jb20vc3JjLzE2Nzc5NDIzMzMtMTY3Nzk0MjMzMy03MjA2NzA3NDQ0ODA4OTQxNTY4LTMzNTYwMDgxMjItMTAwNTctQS0wLTEubTN1OD9yYXRpbz0yMDAwJndzU2VjcmV0PWVkZjE3ZmNkMTk0ZDY2ZTY4MjIxYjQ4NjIxMWZiNWEyJndzVGltZT02Mzc2MmJiNSZmbT1SRmR4T0VKalNqTm9Oa1JLZERaVVdWOGtNRjhrTVY4a01sOGtNdyUzRCUzRCZjdHlwZT10YXJzX21vYmlsZSZmcz1iZ2N0JnNwaGRjZG49YWxfNy10eF8zLWpzXzMtd3NfNy1iZF8yLWh3XzImc3BoZERDPWh1eWEmc3BoZD0yNjRfKi0yNjVfKiZleHNwaGQ9MjY0XzUwMCwmdD0xMDM=,炸一看感觉很可怕,其实跟简单这个就是典型的 base64 加密,我们用 在线工具 解密一下,得到结果 //al.hls.****.com/src/--0--10057-A-0-1.m3u8?ratio=2000&wsSecret=edf17fcd194d66e68221bfb5a2&wsTime=63762bb5&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103

上面播放地址看似正常但是不能播放。。。
抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第3张

二、地址参数解析

查看下真实的播放地址是:

抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第4张

对比发现m3u8格式变成了flv格式,这个其实不重要,官方支持多种直播源格式,但是里面有 seqid 参数,没有 fm 参数,其他的参数其实在全局变量都有(只保留了重要的数据):

{ 
    "roomProfile": { 
    "lUid": , "liveLineUrl": "Ly9hbC5obHMuaHV5YS5jb20vc3JjLzM2NzEzODYzMi0zNjcxMzg2MzItMTU3Njg0ODQxNzUzODE3OTA3Mi03MzQ0MDA3MjAtMTAwNTctQS0wLTEtaW1ncGx1cy5tM3U4P3JhdGlvPTIwMDAmd3NTZWNyZXQ9ZTY3NzBjZTEwNTc5ZDVkZmU3ZjAzNjM1MWRiMzgzMmYmd3NUaW1lPTYzNzYzODE4JmZtPVJGZHhPRUpqU2pOb05rUktkRFpVV1Y4a01GOGtNVjhrTWw4a013JTNEJTNEJmN0eXBlPXRhcnNfbW9iaWxlJmZzPWJnY3Qmc3BoZGNkbj1hbF83LXR4XzMtanNfMy13c183LWJkXzItaHdfMiZzcGhkREM9aHV5YSZzcGhkPTI2NF8qLTI2NV8qJmV4c3BoZD0yNjRfNTAwLCZ0PTEwMw==" }, "roomInfo": { 
    "tLiveInfo": { 
    "tLiveStreamInfo": { 
    "vStreamInfo": { 
    "value": [ { 
    "sCdnType": "AL", "iIsMaster": 0, "lChannelId": , "lSubChannelId": , "lPresenterUid": , "sStreamName": "----10057-A-0-1-imgplus", "sFlvUrl": "http://al.flv.****.com/src", "sFlvUrlSuffix": "flv", "sFlvAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", "sHlsUrl": "http://al.hls.****.com/src", "sHlsUrlSuffix": "m3u8", "sHlsAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", "iIsP2PSupport": 2, "sP2pUrl": "http://al.p2p.****.com/****live", "sP2pUrlSuffix": "slice", "sP2pAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103" }, { 
    "sCdnType": "TX", "iIsMaster": 0, "lChannelId": , "lSubChannelId": , "lPresenterUid": , "sStreamName": "----10057-A-0-1-imgplus", "sFlvUrl": "http://tx.flv.****.com/src", "sFlvUrlSuffix": "flv", "sFlvAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", "sHlsUrl": "http://tx.hls.****.com/src", "sHlsUrlSuffix": "m3u8", "sHlsAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", "sP2pUrl": "http://tx.p2p.****.com/****live", "sP2pUrlSuffix": "slice", "sP2pAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103" }, { 
    "sCdnType": "HW", "iIsMaster": 0, "lChannelId": , "lSubChannelId": , "lPresenterUid": , "sStreamName": "----10057-A-0-1-imgplus", "sFlvUrl": "http://hw.flv.****.com/src", "sFlvUrlSuffix": "flv", "sFlvAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", "sHlsUrl": "http://hw.hls.****.com/src", "sHlsUrlSuffix": "m3u8", "sHlsAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", "iIsP2PSupport": 2, "sP2pUrl": "http://hw.p2p.****.com/****live", "sP2pUrlSuffix": "slice", "sP2pAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103" } ] }, "iDefaultLiveStreamBitRate": 2000, "sDefaultLiveStreamLine": "AL", "sDefaultLiveStreamSuffix": "m3u8", "sDefaultLiveStreamUrl": "http://al.hls.****.com/src/----10057-A-0-1-imgplus.m3u8?ratio=2000&wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103" } } } } 

可以看到地址的参数基本都有了,也有很多默认的地址,但是都不能直接播放,不是真实的播放地址,也没有看到 seqid 参数,所以我们还是需要去找一下。

大致步骤:

  1. 找到 seqid 位置,getAnticodeSeqid 方法是关键
    抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第5张

  2. 搜索 getAnticodeSeqid 方法,在官方sdk中
    抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第6张

  3. 搜索 getAnticode 相关的方法
    抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第7张
    getAnticode 方法进入加密方法,这个方法返回的参数里面包含了 seqid ,接下来就是找出各个加密方法和参数:
    抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第8张
    this[Qt]fm 参数解密出来的,结果类似 DWq8BcJ3h6DJt6TY_$0_$1_$2_$3

抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第9张

this[ae]sStreamName
this[zt]wsTime

  1. 加密方法和参数,总结以下方法:
// 主方法 export function getUrl(sStreamName, wsTime, fm){ 
    let uid = ; // uid 可固定 let ue = uid + 68; // Date.now(); 这里时间和wsTime等参数需要配套使用,这里做测试使用,正常是需要使用Date.now(); let s = Kt(""+ue+"|tars_mobile|103") let r = uid let n = parseFm(fm).replace("$0", r).replace("$1", sStreamName).replace("$2", s).replace("$3", wsTime); let o = `wsSecret=${ 
     Kt(n)}&wsTime=${ 
     wsTime}&seqid=${ 
     ue}&ctype=tars_mobile&ver=1`; let se = ["fs=bgct","sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2","sphdDC=****","sphd=264_*-265_*","t=103","exsphd=264_500,264_2000,"]; let res = sStreamName + ".flv?" + o + "&" + se.join('&') return res; } // 以上用的加密方法 function parseFm(_fm){ 
    let fm = decodeURI(_fm); fm = unescape(fm); return atob(fm) } function Kt(t){ 
    return Bt(Ht(t)) } function Bt(t) { 
    var e, i, s = ""; for (i = 0; i < t.length; i += 1) e = t.charCodeAt(i), s += "0abcdef".charAt(e >>> 4 & 15) + "0abcdef".charAt(15 & e); return s } function Ht(t) { 
    return function(t) { 
    return Vt(Ft(xt(t), 8 * t.length)) }(Gt(t)) } function Gt(t) { 
    return unescape(encodeURIComponent(t)) } function Vt(t) { 
    var e, i = "", s = 32 * t.length; for (e = 0; e < s; e += 8) i += String.fromCharCode(t[e >> 5] >>> e % 32 & 255); return i } function Ft(t, e) { 
    var i, s, r, n, a; t[e >> 5] |= 128 << e % 32, t[14 + (e + 64 >>> 9 << 4)] = e; var o = , h = - , u = - , l = ; for (i = 0; i < t.length; i += 16) s = o, r = h, n = u, a = l, o = Dt(o, h, u, l, t[i], 7, -), l = Dt(l, o, h, u, t[i + 1], 12, -), u = Dt(u, l, o, h, t[i + 2], 17, ), h = Dt(h, u, l, o, t[i + 3], 22, -), o = Dt(o, h, u, l, t[i + 4], 7, -), l = Dt(l, o, h, u, t[i + 5], 12, ), u = Dt(u, l, o, h, t[i + 6], 17, -), h = Dt(h, u, l, o, t[i + 7], 22, -), o = Dt(o, h, u, l, t[i + 8], 7, ), l = Dt(l, o, h, u, t[i + 9], 12, -), u = Dt(u, l, o, h, t[i + 10], 17, -42063), h = Dt(h, u, l, o, t[i + 11], 22, -), o = Dt(o, h, u, l, t[i + 12], 7, ), l = Dt(l, o, h, u, t[i + 13], 12, -), u = Dt(u, l, o, h, t[i + 14], 17, -), o = bt(o, h = Dt(h, u, l, o, t[i + 15], 22, ), u, l, t[i + 1], 5, -), l = bt(l, o, h, u, t[i + 6], 9, -), u = bt(u, l, o, h, t[i + 11], 14, ), h = bt(h, u, l, o, t[i], 20, -), o = bt(o, h, u, l, t[i + 5], 5, -), l = bt(l, o, h, u, t[i + 10], 9, ), u = bt(u, l, o, h, t[i + 15], 14, -), h = bt(h, u, l, o, t[i + 4], 20, -), o = bt(o, h, u, l, t[i + 9], 5, ), l = bt(l, o, h, u, t[i + 14], 9, -), u = bt(u, l, o, h, t[i + 3], 14, -), h = bt(h, u, l, o, t[i + 8], 20, ), o = bt(o, h, u, l, t[i + 13], 5, -), l = bt(l, o, h, u, t[i + 2], 9, -), u = bt(u, l, o, h, t[i + 7], 14, ), o = Nt(o, h = bt(h, u, l, o, t[i + 12], 20, -), u, l, t[i + 5], 4, -), l = Nt(l, o, h, u, t[i + 8], 11, -), u = Nt(u, l, o, h, t[i + 11], 16, ), h = Nt(h, u, l, o, t[i + 14], 23, -), o = Nt(o, h, u, l, t[i + 1], 4, -), l = Nt(l, o, h, u, t[i + 4], 11, ), u = Nt(u, l, o, h, t[i + 7], 16, -), h = Nt(h, u, l, o, t[i + 10], 23, -), o = Nt(o, h, u, l, t[i + 13], 4, ), l = Nt(l, o, h, u, t[i], 11, -), u = Nt(u, l, o, h, t[i + 3], 16, -), h = Nt(h, u, l, o, t[i + 6], 23, ), o = Nt(o, h, u, l, t[i + 9], 4, -), l = Nt(l, o, h, u, t[i + 12], 11, -), u = Nt(u, l, o, h, t[i + 15], 16, ), o = Mt(o, h = Nt(h, u, l, o, t[i + 2], 23, -), u, l, t[i], 6, -), l = Mt(l, o, h, u, t[i + 7], 10, ), u = Mt(u, l, o, h, t[i + 14], 15, -), h = Mt(h, u, l, o, t[i + 5], 21, -), o = Mt(o, h, u, l, t[i + 12], 6, ), l = Mt(l, o, h, u, t[i + 3], 10, -), u = Mt(u, l, o, h, t[i + 10], 15, -), h = Mt(h, u, l, o, t[i + 1], 21, -), o = Mt(o, h, u, l, t[i + 8], 6, ), l = Mt(l, o, h, u, t[i + 15], 10, -), u = Mt(u, l, o, h, t[i + 6], 15, -), h = Mt(h, u, l, o, t[i + 13], 21, ), o = Mt(o, h, u, l, t[i + 4], 6, -), l = Mt(l, o, h, u, t[i + 11], 10, -), u = Mt(u, l, o, h, t[i + 2], 15, ), h = Mt(h, u, l, o, t[i + 9], 21, -), o = Ut(o, s), h = Ut(h, r), u = Ut(u, n), l = Ut(l, a); return [o, h, u, l] } function xt(t) { 
    var e, i = []; for (i[(t.length >> 2) - 1] = void 0, e = 0; e < i.length; e += 1) i[e] = 0; var s = 8 * t.length; for (e = 0; e < s; e += 8) i[e >> 5] |= (255 & t.charCodeAt(e / 8)) << e % 32; return i } function Ut(t, e) { 
    var i = (65535 & t) + (65535 & e); return (t >> 16) + (e >> 16) + (i >> 16) << 16 | 65535 & i } function wt(t, e, i, s, r, n) { 
    return Ut((a = Ut(Ut(e, t), Ut(s, n))) << (o = r) | a >>> 32 - o, i); var a, o } function Dt(t, e, i, s, r, n, a) { 
    return wt(e & i | ~e & s, t, e, r, n, a) } function bt(t, e, i, s, r, n, a) { 
    return wt(e & s | i & ~s, t, e, r, n, a) } function Nt(t, e, i, s, r, n, a) { 
    return wt(e ^ i ^ s, t, e, r, n, a) } function Mt(t, e, i, s, r, n, a) { 
    return wt(i ^ (e | ~s), t, e, r, n, a) } 

三、验证

"sCdnType": "AL", "sStreamName": "----10057-A-0-1-imgplus", "sFlvUrl": "http://al.flv.****.com/src", "sFlvUrlSuffix": "flv", "sFlvAntiCode": "wsSecret=e6770ce10579d5dfe7f036351db3832f&wsTime=&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&exsphd=264_500,&t=103", 

我们以 AL 这个源为例:

let sStreamName = "----10057-A-0-1-imgplus" let wsTime = "b0" let fm = "RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D" let url = 'https://al.flv.****.com/src/'+getUrl(sStreamName, wsTime , fm )+"&ratio=2000&uid=&uuid=&t=103&sv=" console.log(url) "https://al.flv.****.com/src/----10057-A-0-1-imgplus.flv?wsSecret=c3c8fad04a27af2cf36c8304a&wsTime=b0&seqid=10&ctype=tars_mobile&ver=1&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=****&sphd=264_*-265_*&t=103&exsphd=264_500,264_2000,&ratio=2000&uid=&uuid=&t=103&sv=" 

抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第10张

抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第11张

四、总结

因测试需要,getUrl 中的时间我根据请求的时间写死了,实际是需要根据请求页面返回的时间来算,wsTime 是与时间有关的

直播源的对象在: HNF_GLOBAL_INIT.roomInfo.tLiveInfo.tLiveStreamInfo.vStreamInfo
抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第12张

抓取直播源教程_国外浏览器有哪些 (https://mushiming.com/)  第13张

THE END

发表回复