diff --git a/cat/lib/huya.js b/cat/lib/huya.js new file mode 100644 index 0000000..2c3d9d6 --- /dev/null +++ b/cat/lib/huya.js @@ -0,0 +1,65 @@ +import './crypto-js.js' +import './uri.min.js' + +/** + * 虎牙 js 本地代理 + * 周星星,proxy://js_proxy?js=../js/huya.js&id=11342412 + */ + +function decode_live_url_info(anticode) { + const query = new Uri(`?${anticode}`) + const fm = base64Decode(query.getQueryParamValue("fm")) + return { + hash_prefix: fm.split('_')[0], + uuid: query.getQueryParamValue("uuid") || '', + ctype: query.getQueryParamValue("ctype") || '', + txyp: query.getQueryParamValue("txyp") || '', + fs: query.getQueryParamValue("fs") || '', + t: query.getQueryParamValue("t") || '', + } +} + +function get_live_url_info(roomId) { + const text = http.get(`https://www.huya.com/${roomId}`, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36' + } + }).text() + const streamInfo = text.match(/stream: ([\s\S]*?)\n/g)?.[0] ?? text.match(/"stream": "([\s\S]*?)"/g)?.[0] + if (!streamInfo) return null + const liveData = JSON.parse(text.match(/stream: ([\s\S]*?)\n/g)?.[0].substring('stream:'.length).trim()) + const live_url_infos = {} + liveData.data[0].gameStreamInfoList.forEach(streamInfo => { + let live_url_info = {} + const sCdnType = streamInfo['sCdnType'] + live_url_info['stream_name'] = streamInfo['sStreamName'] + live_url_info['base_url'] = streamInfo['sHlsUrl'] + live_url_info['hls_url'] = `${streamInfo['sHlsUrl']}/${streamInfo['sStreamName']}.${streamInfo['sHlsUrlSuffix']}` + live_url_infos[sCdnType] = { ...live_url_info, ...decode_live_url_info(streamInfo['sHlsAntiCode']) } + }) + return live_url_infos +} + +function getRealUrl(roomId, userId = 1463993859134, cnd = 'AL', ratio = '') { + let urls = [] + let seqid = (new Date().getTime() + userId).toString(); + let wsTime = (Math.floor(Date.now() / 1000) + 3600).toString(16); + const liveUrlInfos = get_live_url_info(roomId) + if (!liveUrlInfos) return null + const liveUrlInfo = liveUrlInfos[cnd] || Object.values(liveUrlInfos)[0] + if (!liveUrlInfo) return null + let hash0 = CryptoJS.MD5(`${seqid}|${liveUrlInfo.ctype}|${liveUrlInfo.t}`) + let hash1 = CryptoJS.MD5(`${liveUrlInfo.hash_prefix}_${userId}_${liveUrlInfo.stream_name}_${hash0}_${wsTime}`) + let url = ''; + if (liveUrlInfo.ctype.includes('mobile')) { + return `${liveUrlInfo.hls_url}?wsSecret=${hash1}&wsTime=${wsTime}&uuid=${liveUrlInfo.uuid}&uid=${userId}&seqid=${seqid}&ratio=${ratio}&txyp=${liveUrlInfo.txyp}&fs=${liveUrlInfo.fs}&ctype=${liveUrlInfo.ctype}&ver=1&t=${liveUrlInfo.t}`; + } else { + return `${liveUrlInfo.hls_url}?wsSecret=${hash1}&wsTime=${wsTime}&seqid=${seqid}&ctype=${liveUrlInfo.ctype}&ver=1&txyp=${liveUrlInfo.txyp}&fs=${liveUrlInfo.fs}&ratio=${ratio}&u=${userId}&t=${liveUrlInfo.t}&sv=2107230339`; + } +} + +export default function huya_proxy(params) { + const url = getRealUrl(params.id) + return url ? [302, "", "", { Location: url, Status: "302 Found" }] : [404, '', ''] +} \ No newline at end of file