From be1b6b03fcb5bc4aa3b0e2f5a926416b5debcbe6 Mon Sep 17 00:00:00 2001 From: Mal'Ganis Date: Thu, 28 Apr 2022 03:19:59 +0800 Subject: [PATCH 01/18] remove x-p2p format link --- douyu.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/douyu.py b/douyu.py index b8d619c..5a0f65f 100644 --- a/douyu.py +++ b/douyu.py @@ -11,9 +11,9 @@ class DouYu: """ 可用来替换返回链接中的主机部分 - 两个阿里的CDN: - dyscdnali1.douyucdn.cn - dyscdnali3.douyucdn.cn + 两个网宿的CDN: + vplay1a.douyucdn.cn + vplay3a.douyucdn.cn 墙外不用带尾巴的akm cdn: hls3-akm.douyucdn.cn hlsa-akm.douyucdn.cn @@ -127,8 +127,7 @@ def get_real_url(self): else: key = self.get_js() real_url = {} - real_url["flv"] = "http://dyscdnali1.douyucdn.cn/live/{}.flv?uuid=".format(key) - real_url["x-p2p"] = "http://tx2play1.douyucdn.cn/live/{}.xs?uuid=".format(key) + real_url["flv"] = "http://vplay1a.douyucdn.cn/live/{}.flv?uuid=".format(key) return real_url if __name__ == '__main__': From ec517f0a88ec90509298e72dcb35725ccc39ae56 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 1 Jul 2022 01:51:45 +0800 Subject: [PATCH 02/18] fix douyu --- douyu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/douyu.py b/douyu.py index 5a0f65f..0ec0243 100644 --- a/douyu.py +++ b/douyu.py @@ -127,7 +127,7 @@ def get_real_url(self): else: key = self.get_js() real_url = {} - real_url["flv"] = "http://vplay1a.douyucdn.cn/live/{}.flv?uuid=".format(key) + real_url["flv"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) return real_url if __name__ == '__main__': From 6b1626d23d0af7bd7d1c0f52150aa1a229e568e9 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Sat, 20 Aug 2022 14:48:37 +0800 Subject: [PATCH 03/18] add wang su cdn --- douyu.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/douyu.py b/douyu.py index 0ec0243..dcdea57 100644 --- a/douyu.py +++ b/douyu.py @@ -12,8 +12,8 @@ class DouYu: """ 可用来替换返回链接中的主机部分 两个网宿的CDN: - vplay1a.douyucdn.cn - vplay3a.douyucdn.cn + vplay1a.douyucdn.cn(失效) + vplay3a.douyucdn.cn(失效) 墙外不用带尾巴的akm cdn: hls3-akm.douyucdn.cn hlsa-akm.douyucdn.cn @@ -127,7 +127,8 @@ def get_real_url(self): else: key = self.get_js() real_url = {} - real_url["flv"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) + real_url["flv1"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) + real_url["flv2"] = "http://ws-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) return real_url if __name__ == '__main__': From 5d63026c07c7333803a2e918ece66e175ec37137 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Sat, 20 Aug 2022 15:06:55 +0800 Subject: [PATCH 04/18] add wang su cdn --- douyu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/douyu.py b/douyu.py index dcdea57..b2db890 100644 --- a/douyu.py +++ b/douyu.py @@ -1,5 +1,5 @@ # 获取斗鱼直播间的真实流媒体地址,默认最高画质 -# 使用 https://github.com/wbt5/real-url/issues/185 中两位大佬@wjxgzz @4bbu6j5885o3gpv6ss8找到的的CDN,在此感谢! +# 使用 两位大佬@limitcool @wc7086,在此感谢! import hashlib import re import time From 7329d7cd6e958e11e934f1c6cee28e8d4f550847 Mon Sep 17 00:00:00 2001 From: i7az Date: Thu, 22 Sep 2022 01:54:19 +0800 Subject: [PATCH 05/18] douyu m3u8 --- douyu.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/douyu.py b/douyu.py index b2db890..11e7dec 100644 --- a/douyu.py +++ b/douyu.py @@ -3,7 +3,7 @@ import hashlib import re import time - +import json import execjs import requests @@ -79,13 +79,13 @@ def get_js(self): js = execjs.compile(func_sign) params = js.call('sign', self.rid, self.did, self.t10) - params += '&ver=219032101&rid={}&rate=-1'.format(self.rid) + params += '&ver=219032101&rid={}&rate=0'.format(self.rid) url = 'https://m.douyu.com/api/room/ratestream' res = self.s.post(url, params=params).text key = re.search(r'(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(.m3u8|/playlist)', res).group(1) - return key + return res def get_pc_js(self, cdn='ws-h5', rate=0): """ @@ -129,9 +129,14 @@ def get_real_url(self): real_url = {} real_url["flv1"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) real_url["flv2"] = "http://ws-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) + try: + real_url["m3u8"] = json.loads(s.get_js())["data"]["url"] + except: + pass return real_url if __name__ == '__main__': r = input('输入斗鱼直播间号:\n') s = DouYu(r) print(s.get_real_url()) + From 49ce3febfbdfd390a048e61a155e456e373a361a Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Sun, 25 Sep 2022 22:36:38 +0800 Subject: [PATCH 06/18] fix douyu.py get wrong flv links --- douyu.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/douyu.py b/douyu.py index 11e7dec..ca143f0 100644 --- a/douyu.py +++ b/douyu.py @@ -84,8 +84,10 @@ def get_js(self): url = 'https://m.douyu.com/api/room/ratestream' res = self.s.post(url, params=params).text key = re.search(r'(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(.m3u8|/playlist)', res).group(1) - - return res + data = {} + data["res"] = res + data["key"] = key + return data def get_pc_js(self, cdn='ws-h5', rate=0): """ @@ -117,20 +119,23 @@ def get_pc_js(self, cdn='ws-h5', rate=0): return res def get_real_url(self): + data = {} error, key = self.get_pre() if error == 0: + data["key"] = key pass elif error == 102: raise Exception('房间不存在') elif error == 104: raise Exception('房间未开播') else: - key = self.get_js() + data = self.get_js() real_url = {} - real_url["flv1"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) - real_url["flv2"] = "http://ws-tct.douyucdn.cn/live/{}.flv?uuid=".format(key) + real_url["flv1"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(data["key"]) + real_url["flv2"] = "http://ws-tct.douyucdn.cn/live/{}.flv?uuid=".format(data["key"]) + try: - real_url["m3u8"] = json.loads(s.get_js())["data"]["url"] + real_url["m3u8"] = json.loads(data["res"])["data"]["url"] except: pass return real_url From d8cbca7b672821c691ee9a81cb31849dca22e8ed Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Wed, 14 Dec 2022 15:16:55 +0800 Subject: [PATCH 07/18] Compatible with python3.11, upgrade yarl to 1.8.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7c3be85..1509cf1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,4 @@ requests==2.26.0 six==1.15.0 typing-extensions==3.7.4.3 urllib3==1.26.5 -yarl==1.5.1 +yarl==1.8.2 From 679e67d125c36181e373900fd8ad8315a77fc814 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Tue, 14 Feb 2023 09:09:25 +0800 Subject: [PATCH 08/18] fix huya.py get only one chunk --- huya.py | 108 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/huya.py b/huya.py index 011cc54..a171b13 100644 --- a/huya.py +++ b/huya.py @@ -1,31 +1,73 @@ # 获取虎牙直播的真实流媒体地址。 - +import json +import math import requests import re +import time import base64 -import urllib.parse import hashlib -import time +from urllib.parse import parse_qs, urlencode +from datetime import datetime +import random + +def live(info): + stream_info = dict({'flv':{},'hls':{}}) + cdn_type = dict({'AL': '阿里', 'TX': '腾讯', 'HW': '华为', 'HS': '火山'}) + uid = get_anonymous_uid() + for s in info["roomInfo"]["tLiveInfo"]["tLiveStreamInfo"]["vStreamInfo"]["value"]: + if s["sFlvUrl"]: + q = dict(parse_qs(s["sFlvAntiCode"])) + q["ver"] = ["1"] + q["sv"] = ["2110211124"] + q["seqid"] = [str(int(uid) + int(datetime.now().timestamp() * 1000))] + q["uid"] = [str(uid)] + q["uuid"] = [str(get_uuid())] + ss = hashlib.md5("{}|{}|{}".format(q["seqid"][0], q["ctype"][0], q["t"][0]).encode("UTF-8")).hexdigest() + q["fm"][0] = base64.b64decode(q["fm"][0]).decode('utf-8').replace("$0", q["uid"][0]).replace("$1", s[ + "sStreamName"]).replace("$2", ss).replace("$3", q["wsTime"][0]) + q["wsSecret"][0] = hashlib.md5(q["fm"][0].encode("UTF-8")).hexdigest() + del q["fm"] + del q["txyp"] + qs = urlencode({x: y[0] for x, y in q.items()}) + stream_info["flv"][cdn_type[s["sCdnType"]]] = "{}/{}.{}?{}".format(s["sFlvUrl"], s["sStreamName"], + s["sFlvUrlSuffix"], qs) + if s["sHlsUrl"]: + q = dict(parse_qs(s["sHlsAntiCode"])) + q["ver"] = ["1"] + q["sv"] = ["2110211124"] + q["seqid"] = [str(int(uid) + int(datetime.now().timestamp() * 1000))] + q["uid"] = [str(uid)] + q["uuid"] = [str(get_uuid())] + ss = hashlib.md5("{}|{}|{}".format(q["seqid"][0], q["ctype"][0], q["t"][0]).encode("UTF-8")).hexdigest() + q["fm"][0] = base64.b64decode(q["fm"][0]).decode('utf-8').replace("$0", q["uid"][0]).replace("$1", s[ + "sStreamName"]).replace("$2", ss).replace("$3", q["wsTime"][0]) + q["wsSecret"][0] = hashlib.md5(q["fm"][0].encode("UTF-8")).hexdigest() + del q["fm"] + del q["txyp"] + qs = urlencode({x: y[0] for x, y in q.items()}) + stream_info["hls"][cdn_type[s["sCdnType"]]] = "{}/{}.{}?{}".format(s["sHlsUrl"], s["sStreamName"], + s["sHlsUrlSuffix"], qs) + return stream_info -def live(e): - i, b = e.split('?') - r = i.split('/') - s = re.sub(r'.(flv|m3u8)', '', r[-1]) - c = b.split('&', 3) - c = [i for i in c if i != ''] - n = {i.split('=')[0]: i.split('=')[1] for i in c} - fm = urllib.parse.unquote(n['fm']) - u = base64.b64decode(fm).decode('utf-8') - p = u.split('_')[0] - f = str(int(time.time() * 1e7)) - l = n['wsTime'] - t = '0' - h = '_'.join([p, t, s, f, l]) - m = hashlib.md5(h.encode('utf-8')).hexdigest() - y = c[-1] - url = "{}?wsSecret={}&wsTime={}&u={}&seqid={}&{}".format(i, m, l, t, f, y) - return url + +def get_anonymous_uid(): + url = "https://udblgn.huya.com/web/anonymousLogin" + resp = requests.post(url, json={ + "appId": 5002, + "byPass": 3, + "context": "", + "version": "2.4", + "data": {} + }) + return resp.json()["data"]["uid"] + + +def get_uuid(): + # Number((Date.now() % 1e10 * 1e3 + (1e3 * Math.random() | 0)) % 4294967295)) + now = datetime.now().timestamp() * 1000 + rand = random.randint(0, 1000) | 0 + return int((now % 10000000000 * 1000 + rand) % 4294967295) def get_real_url(room_id): @@ -37,22 +79,20 @@ def get_real_url(room_id): 'Chrome/75.0.3770.100 Mobile Safari/537.36 ' } response = requests.get(url=room_url, headers=header).text - liveLineUrl = re.findall(r'"liveLineUrl":"([\s\S]*?)",', response)[0] - liveline = base64.b64decode(liveLineUrl).decode('utf-8') - if liveline: - if 'replay' in liveline: - return '直播录像:' + liveline - else: - liveline = live(liveline) - real_url = ("https:" + liveline).replace("hls", "flv").replace("m3u8", "flv") + room_info_str = re.findall(r'\ window.HNF_GLOBAL_INIT = (.*) \', response)[0] + room_info = json.loads(room_info_str) + if room_info["roomInfo"]["eLiveStatus"] == 2: + print('该直播间源地址为:') + return live(room_info) else: - real_url = '未开播或直播间不存在' - except: - real_url = '未开播或直播间不存在' + real_url = '未开播' + + except Exception as e: + print(e) + real_url = '直播间不存在' return real_url rid = input('输入虎牙直播房间号:\n') real_url = get_real_url(rid) -print('该直播间源地址为:') -print(real_url) \ No newline at end of file +print(real_url) From a07ca9e78c54138ae3945a34ae594291d263608e Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Tue, 14 Feb 2023 15:44:50 +0800 Subject: [PATCH 09/18] change douyu.py and huya.py output result to json format --- douyu.py | 3 ++- huya.py | 55 +++++++++++++++++++++++++------------------------------ 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/douyu.py b/douyu.py index ca143f0..902aba7 100644 --- a/douyu.py +++ b/douyu.py @@ -6,6 +6,7 @@ import json import execjs import requests +import json class DouYu: @@ -138,7 +139,7 @@ def get_real_url(self): real_url["m3u8"] = json.loads(data["res"])["data"]["url"] except: pass - return real_url + return json.dumps(real_url, indent=2, ensure_ascii=False) if __name__ == '__main__': r = input('输入斗鱼直播间号:\n') diff --git a/huya.py b/huya.py index a171b13..4054fb6 100644 --- a/huya.py +++ b/huya.py @@ -12,45 +12,40 @@ def live(info): - stream_info = dict({'flv':{},'hls':{}}) + stream_info = dict({'flv': {}, 'hls': {}}) cdn_type = dict({'AL': '阿里', 'TX': '腾讯', 'HW': '华为', 'HS': '火山'}) uid = get_anonymous_uid() for s in info["roomInfo"]["tLiveInfo"]["tLiveStreamInfo"]["vStreamInfo"]["value"]: if s["sFlvUrl"]: - q = dict(parse_qs(s["sFlvAntiCode"])) - q["ver"] = ["1"] - q["sv"] = ["2110211124"] - q["seqid"] = [str(int(uid) + int(datetime.now().timestamp() * 1000))] - q["uid"] = [str(uid)] - q["uuid"] = [str(get_uuid())] - ss = hashlib.md5("{}|{}|{}".format(q["seqid"][0], q["ctype"][0], q["t"][0]).encode("UTF-8")).hexdigest() - q["fm"][0] = base64.b64decode(q["fm"][0]).decode('utf-8').replace("$0", q["uid"][0]).replace("$1", s[ - "sStreamName"]).replace("$2", ss).replace("$3", q["wsTime"][0]) - q["wsSecret"][0] = hashlib.md5(q["fm"][0].encode("UTF-8")).hexdigest() - del q["fm"] - del q["txyp"] - qs = urlencode({x: y[0] for x, y in q.items()}) stream_info["flv"][cdn_type[s["sCdnType"]]] = "{}/{}.{}?{}".format(s["sFlvUrl"], s["sStreamName"], - s["sFlvUrlSuffix"], qs) + s["sFlvUrlSuffix"], + process_anticode(s["sFlvAntiCode"], uid, + s["sStreamName"])) if s["sHlsUrl"]: - q = dict(parse_qs(s["sHlsAntiCode"])) - q["ver"] = ["1"] - q["sv"] = ["2110211124"] - q["seqid"] = [str(int(uid) + int(datetime.now().timestamp() * 1000))] - q["uid"] = [str(uid)] - q["uuid"] = [str(get_uuid())] - ss = hashlib.md5("{}|{}|{}".format(q["seqid"][0], q["ctype"][0], q["t"][0]).encode("UTF-8")).hexdigest() - q["fm"][0] = base64.b64decode(q["fm"][0]).decode('utf-8').replace("$0", q["uid"][0]).replace("$1", s[ - "sStreamName"]).replace("$2", ss).replace("$3", q["wsTime"][0]) - q["wsSecret"][0] = hashlib.md5(q["fm"][0].encode("UTF-8")).hexdigest() - del q["fm"] - del q["txyp"] - qs = urlencode({x: y[0] for x, y in q.items()}) stream_info["hls"][cdn_type[s["sCdnType"]]] = "{}/{}.{}?{}".format(s["sHlsUrl"], s["sStreamName"], - s["sHlsUrlSuffix"], qs) + s["sHlsUrlSuffix"], + process_anticode(s["sHlsAntiCode"], uid, + s["sStreamName"])) return stream_info +def process_anticode(anticode, uid, streamname): + q = dict(parse_qs(anticode)) + q["ver"] = ["1"] + q["sv"] = ["2110211124"] + q["seqid"] = [str(int(uid) + int(datetime.now().timestamp() * 1000))] + q["uid"] = [str(uid)] + q["uuid"] = [str(get_uuid())] + ss = hashlib.md5("{}|{}|{}".format(q["seqid"][0], q["ctype"][0], q["t"][0]).encode("UTF-8")).hexdigest() + q["fm"][0] = base64.b64decode(q["fm"][0]).decode('utf-8').replace("$0", q["uid"][0]).replace("$1", + streamname).replace( + "$2", ss).replace("$3", q["wsTime"][0]) + q["wsSecret"][0] = hashlib.md5(q["fm"][0].encode("UTF-8")).hexdigest() + del q["fm"] + del q["txyp"] + return urlencode({x: y[0] for x, y in q.items()}) + + def get_anonymous_uid(): url = "https://udblgn.huya.com/web/anonymousLogin" resp = requests.post(url, json={ @@ -83,7 +78,7 @@ def get_real_url(room_id): room_info = json.loads(room_info_str) if room_info["roomInfo"]["eLiveStatus"] == 2: print('该直播间源地址为:') - return live(room_info) + real_url = json.dumps(live(room_info), indent=2, ensure_ascii=False) else: real_url = '未开播' From 0d5a57ce1a166d39297b9dd0233cdcda4fe241c2 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Thu, 16 Feb 2023 15:03:20 +0800 Subject: [PATCH 10/18] fix "txyp" error in huya.py --- huya.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/huya.py b/huya.py index 4054fb6..9d14757 100644 --- a/huya.py +++ b/huya.py @@ -42,7 +42,8 @@ def process_anticode(anticode, uid, streamname): "$2", ss).replace("$3", q["wsTime"][0]) q["wsSecret"][0] = hashlib.md5(q["fm"][0].encode("UTF-8")).hexdigest() del q["fm"] - del q["txyp"] + if "txyp" in q: + del q["txyp"] return urlencode({x: y[0] for x, y in q.items()}) From efb3ba5971a630e9c73b2bf9f00f8c0bf9ada5ee Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Thu, 16 Feb 2023 15:18:21 +0800 Subject: [PATCH 11/18] fix missing "WS" cdn type in huya.py --- huya.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/huya.py b/huya.py index 9d14757..924ae93 100644 --- a/huya.py +++ b/huya.py @@ -13,7 +13,7 @@ def live(info): stream_info = dict({'flv': {}, 'hls': {}}) - cdn_type = dict({'AL': '阿里', 'TX': '腾讯', 'HW': '华为', 'HS': '火山'}) + cdn_type = dict({'AL': '阿里', 'TX': '腾讯', 'HW': '华为', 'HS': '火山', 'WS': '网宿'}) uid = get_anonymous_uid() for s in info["roomInfo"]["tLiveInfo"]["tLiveStreamInfo"]["vStreamInfo"]["value"]: if s["sFlvUrl"]: From 8101e9e50ce496cf80c835cac69c1cf7e6550ea4 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Fri, 17 Feb 2023 09:14:09 +0800 Subject: [PATCH 12/18] remove huomao.py --- huomao.py | 75 ------------------------------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 huomao.py diff --git a/huomao.py b/huomao.py deleted file mode 100644 index b49c20a..0000000 --- a/huomao.py +++ /dev/null @@ -1,75 +0,0 @@ -# 获取火猫直播的真实流媒体地址,默认为最高画质. -# 获取的流媒体地址如:http://live-lx-hdl.huomaotv.cn/live/qvCESZ?t=1573928152&r=377789475848&stream=qvCESZ&rid=oubvc2y3v&token=44a7f115f0af496e268bcbb7cdbb63b1&url=http%3A%2F%2Flive-lx-hdl.huomaotv.cn%2Flive%2FqvCESZ&from=huomaoh5room -# 实际上使用http://live-lx-hdl.huomaotv.cn/live/qvCESZ?token=44a7f115f0af496e268bcbb7cdbb63b1,即可播放 -# 链接中lx可替换cdn(lx,tx,ws,js,jd2等),媒体类型可为.flv或.m3u8,码率可为BL8M,BL4M,TD,BD,HD,SD - -import requests -import time -import hashlib -import re - - -class HuoMao: - - def __init__(self, rid): - """ - 火猫直播已经倒闭了 - Args: - rid: 房间号 - """ - self.rid = rid - - @staticmethod - def get_videoids(rid): - room_url = f'https://www.huomao.com/mobile/mob_live/{rid}' - response = requests.get(url=room_url).text - try: - videoids = re.findall(r'var stream = "([\w\W]+?)";', response)[0] - except IndexError: - videoids = 0 - return videoids - - @staticmethod - def get_token(videoids): - tt = str(int((time.time() * 1000))) - token = hashlib.md5(f'{videoids}huomaoh5room{tt}6FE26D855E1AEAE090E243EB1AF73685'.encode('utf-8')).hexdigest() - return token - - def get_real_url(self): - videoids = self.get_videoids(self.rid) - if videoids: - token = self.get_token(videoids) - room_url = 'https://www.huomao.com/swf/live_data' - post_data = { - 'cdns': 1, - 'streamtype': 'live', - 'VideoIDS': videoids, - 'from': 'huomaoh5room', - 'time': time, - 'token': token - } - response = requests.post(url=room_url, data=post_data).json() - roomStatus = response.get('roomStatus', 0) - if roomStatus == '1': - real_url_flv = response.get('streamList')[-1].get('list')[0].get('url') - real_url_m3u8 = response.get('streamList')[-1].get('list_hls')[0].get('url') - real_url = [real_url_flv, real_url_m3u8.replace('_480', '')] - else: - raise Exception('直播间未开播') - else: - raise Exception('直播间不存在') - return real_url - - -def get_real_url(rid): - try: - hm = HuoMao(rid) - return hm.get_real_url() - except Exception as e: - print('Exception:', e) - return False - - -if __name__ == '__main__': - r = input('请输入火猫直播房间号:\n') - print(get_real_url(r)) From 63ff2e1d41cb9559bd12c5db775bf3b11b66803e Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Wed, 22 Feb 2023 22:44:24 +0800 Subject: [PATCH 13/18] huya.py add history playback link --- huya.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/huya.py b/huya.py index 924ae93..c85a4c2 100644 --- a/huya.py +++ b/huya.py @@ -1,9 +1,7 @@ # 获取虎牙直播的真实流媒体地址。 import json -import math import requests import re -import time import base64 import hashlib from urllib.parse import parse_qs, urlencode @@ -80,6 +78,9 @@ def get_real_url(room_id): if room_info["roomInfo"]["eLiveStatus"] == 2: print('该直播间源地址为:') real_url = json.dumps(live(room_info), indent=2, ensure_ascii=False) + elif room_info["roomInfo"]["eLiveStatus"] == 3: + print('该直播间正在回放历史直播,低清晰度源地址为:') + real_url = "https:{}".format(base64.b64decode(room_info["roomProfile"]["liveLineUrl"]).decode('utf-8')) else: real_url = '未开播' From c3877e637c55057709629052b790f8b9be7f0892 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Fri, 24 Feb 2023 17:01:29 +0800 Subject: [PATCH 14/18] remove 95xiu.py --- 95xiu.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 95xiu.py diff --git a/95xiu.py b/95xiu.py deleted file mode 100644 index ef94568..0000000 --- a/95xiu.py +++ /dev/null @@ -1,38 +0,0 @@ -# 95秀:http://www.95.cn/ - -import requests -import re - - -class JWXiu: - - def __init__(self, rid): - self.rid = rid - - def get_real_url(self): - with requests.Session() as s: - res = s.get(f'https://www.95.cn/{self.rid}.html').text - try: - uid = re.search(r'"uid":(\d+),', res).group(1) - status = re.search(r'"is_offline":"(\d)"', res).group(1) - except AttributeError: - raise Exception('没有找到直播间') - if status == '0': - real_url = f'https://play1.95xiu.com/app/{uid}.flv' - return real_url - else: - raise Exception('未开播') - - -def get_real_url(rid): - try: - jwx = JWXiu(rid) - return jwx.get_real_url() - except Exception as e: - print('Exception:', e) - return False - - -if __name__ == '__main__': - r = input('输入95秀房间号:\n') - print(get_real_url(r)) From 56c3c324c678bd5d29adc629ac05b9ee03d43347 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Sun, 26 Feb 2023 22:20:54 +0800 Subject: [PATCH 15/18] fix douyu.py --- douyu.py | 49 ++----------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/douyu.py b/douyu.py index 902aba7..3b307b3 100644 --- a/douyu.py +++ b/douyu.py @@ -3,23 +3,12 @@ import hashlib import re import time -import json import execjs import requests import json class DouYu: - """ - 可用来替换返回链接中的主机部分 - 两个网宿的CDN: - vplay1a.douyucdn.cn(失效) - vplay3a.douyucdn.cn(失效) - 墙外不用带尾巴的akm cdn: - hls3-akm.douyucdn.cn - hlsa-akm.douyucdn.cn - hls1a-akm.douyucdn.cn - """ def __init__(self, rid): """ @@ -80,7 +69,7 @@ def get_js(self): js = execjs.compile(func_sign) params = js.call('sign', self.rid, self.did, self.t10) - params += '&ver=219032101&rid={}&rate=0'.format(self.rid) + params += '&ver=22107261&rid={}&rate=-1'.format(self.rid) url = 'https://m.douyu.com/api/room/ratestream' res = self.s.post(url, params=params).text @@ -90,50 +79,16 @@ def get_js(self): data["key"] = key return data - def get_pc_js(self, cdn='ws-h5', rate=0): - """ - 通过PC网页端的接口获取完整直播源。 - :param cdn: 主线路ws-h5、备用线路tct-h5 - :param rate: 1流畅;2高清;3超清;4蓝光4M;0蓝光8M或10M - :return: JSON格式 - """ - res = self.s.get('https://www.douyu.com/' + str(self.rid)).text - result = re.search(r'(vdwdae325w_64we[\s\S]*function ub98484234[\s\S]*?)function', res).group(1) - func_ub9 = re.sub(r'eval.*?;}', 'strc;}', result) - js = execjs.compile(func_ub9) - res = js.call('ub98484234') - - v = re.search(r'v=(\d+)', res).group(1) - rb = DouYu.md5(self.rid + self.did + self.t10 + v) - - func_sign = re.sub(r'return rt;}\);?', 'return rt;}', res) - func_sign = func_sign.replace('(function (', 'function sign(') - func_sign = func_sign.replace('CryptoJS.MD5(cb).toString()', '"' + rb + '"') - - js = execjs.compile(func_sign) - params = js.call('sign', self.rid, self.did, self.t10) - - params += '&cdn={}&rate={}'.format(cdn, rate) - url = 'https://www.douyu.com/lapi/live/getH5Play/{}'.format(self.rid) - res = self.s.post(url, params=params).json() - - return res - def get_real_url(self): data = {} error, key = self.get_pre() - if error == 0: - data["key"] = key - pass - elif error == 102: + if error == 102: raise Exception('房间不存在') elif error == 104: raise Exception('房间未开播') else: data = self.get_js() real_url = {} - real_url["flv1"] = "http://akm-tct.douyucdn.cn/live/{}.flv?uuid=".format(data["key"]) - real_url["flv2"] = "http://ws-tct.douyucdn.cn/live/{}.flv?uuid=".format(data["key"]) try: real_url["m3u8"] = json.loads(data["res"])["data"]["url"] From e7ff61268966773310adf276427b69af7a04aa13 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Tue, 28 Feb 2023 01:52:41 +0800 Subject: [PATCH 16/18] douyu.py using dynamic did --- douyu.py | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/douyu.py b/douyu.py index 3b307b3..1128269 100644 --- a/douyu.py +++ b/douyu.py @@ -1,5 +1,4 @@ # 获取斗鱼直播间的真实流媒体地址,默认最高画质 -# 使用 两位大佬@limitcool @wc7086,在此感谢! import hashlib import re import time @@ -47,12 +46,23 @@ def get_pre(self): } res = self.s.post(url, headers=headers, data=data).json() error = res['error'] - data = res['data'] - key = '' - if data: - rtmp_live = data['rtmp_live'] - key = re.search(r'(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(/playlist|.m3u8)', rtmp_live).group(1) - return error, key + return error + + def get_did(self): + did = '10000000000000000000000000001501' + url = "https://passport.douyu.com/lapi/did/api/get?client_id=25&_={}&callback=axiosJsonpCallback1".format(self.t13) + headers = { + 'referer': 'https://m.douyu.com/' + } + try: + res = self.s.get(url=url,headers=headers).text + result = json.loads(re.search(r"axiosJsonpCallback1\((.*)\)", res).group(1)) + if result["error"] == 0: + if "did" in result["data"]: + did = result["data"]["did"] + except Exception as e: + print(e) + return did def get_js(self): result = re.search(r'(function ub98484234.*)\s(var.*)', self.res).group() @@ -72,32 +82,27 @@ def get_js(self): params += '&ver=22107261&rid={}&rate=-1'.format(self.rid) url = 'https://m.douyu.com/api/room/ratestream' - res = self.s.post(url, params=params).text - key = re.search(r'(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(.m3u8|/playlist)', res).group(1) - data = {} - data["res"] = res - data["key"] = key - return data + res = self.s.post(url, params=params).json() + return res def get_real_url(self): - data = {} - error, key = self.get_pre() + real_url = {} + error = self.get_pre() if error == 102: raise Exception('房间不存在') elif error == 104: raise Exception('房间未开播') else: - data = self.get_js() - real_url = {} - - try: - real_url["m3u8"] = json.loads(data["res"])["data"]["url"] - except: - pass + try: + data = self.get_js() + real_url["m3u8"] = data["data"]["url"] + except: + pass return json.dumps(real_url, indent=2, ensure_ascii=False) if __name__ == '__main__': r = input('输入斗鱼直播间号:\n') s = DouYu(r) + s.did = s.get_did() print(s.get_real_url()) From 98c60972f6e853609b4cb9e71cd0c8a972d18b24 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Sun, 2 Apr 2023 22:33:31 +0800 Subject: [PATCH 17/18] fix douyu danmu in python 3.10+ --- danmu/danmaku/__init__.py | 13 ++++++++----- requirements.txt | 26 +++++++++++++------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/danmu/danmaku/__init__.py b/danmu/danmaku/__init__.py index 15964b5..af9fba0 100644 --- a/danmu/danmaku/__init__.py +++ b/danmu/danmaku/__init__.py @@ -1,5 +1,6 @@ import asyncio import re +import ssl import aiohttp @@ -35,6 +36,8 @@ def __init__(self, url, q): self.__stop = False self.__dm_queue = q self.__link_status = True + self.__ssl_ctx = ssl.create_default_context() + self.__ssl_ctx.set_ciphers("DEFAULT") if 'http://' == url[:7] or 'https://' == url[:8]: self.__url = url else: @@ -69,7 +72,7 @@ def __init__(self, url, q): async def init_ws(self): ws_url, reg_datas = await self.__site.get_ws_info(self.__url) - self.__ws = await self.__hs.ws_connect(ws_url) + self.__ws = await self.__hs.ws_connect(ws_url, ssl_context=self.__ssl_ctx) if reg_datas: for reg_data in reg_datas: if self.__u == 'qf.56.com' or self.__u == 'laifeng.com' or self.__u == 'look.163.com': @@ -102,7 +105,7 @@ async def fetch_danmaku(self): async def init_ws_huajiao(self): rid = re.search(r'\d+', self.__url).group(0) s = self.__site(rid) - self.__ws = await self.__hs.ws_connect(self.__site.ws_url) + self.__ws = await self.__hs.ws_connect(self.__site.ws_url, ssl_context=self.__ssl_ctx) await self.__ws.send_bytes(s.sendHandshakePack()) count = 0 async for msg in self.__ws: @@ -117,7 +120,7 @@ async def init_ws_huajiao(self): count += 1 async def init_ws_acfun(self): - self.__ws = await self.__hs.ws_connect(self.__site.ws_url) + self.__ws = await self.__hs.ws_connect(self.__site.ws_url, ssl_context=self.__ssl_ctx) await self.__ws.send_bytes(self.__s.encode_packet('register')) async def ping_acfun(self): @@ -147,7 +150,7 @@ async def fetch_danmaku_acfun(self): await self.__dm_queue.put(m) async def init_ws_173(self): - self.__ws = await self.__hs.ws_connect(self.__site.ws_url) + self.__ws = await self.__hs.ws_connect(self.__site.ws_url, ssl_context=self.__ssl_ctx) await self.__ws.send_bytes(self.__s.pack('startup')) await asyncio.sleep(1) await self.__ws.send_bytes(self.__s.pack('enterroomreq')) @@ -170,7 +173,7 @@ async def fetch_danmaku_173(self): await self.__dm_queue.put(m) async def init_ws_yy(self): - self.__ws = await self.__hs.ws_connect(self.__site.ws_url) + self.__ws = await self.__hs.ws_connect(self.__site.ws_url, ssl_context=self.__ssl_ctx) await self.__ws.send_bytes(self.__s.LoginUDB()) async def heartbeat_yy(self): diff --git a/requirements.txt b/requirements.txt index 1509cf1..764664e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,15 @@ -aiohttp==3.7.4 -async-timeout==3.0.1 -attrs==20.2.0 -certifi==2020.6.20 -chardet==3.0.4 -idna==2.10 -multidict==4.7.6 -protobuf==3.12.2 -pycryptodome==3.9.8 +aiohttp==3.8.4 +async-timeout==4.0.2 +attrs==22.2.0 +certifi==2022.12.7 +chardet==5.1.0 +idna==3.4 +multidict==6.0.4 +protobuf==3.20.* +pycryptodome==3.17 PyExecJS==1.5.1 -requests==2.26.0 -six==1.15.0 -typing-extensions==3.7.4.3 -urllib3==1.26.5 +requests==2.28.2 +six==1.16.0 +typing-extensions==4.5.0 +urllib3==1.26.15 yarl==1.8.2 From 60896640f8991239e6294a6c46c21465a5db88b8 Mon Sep 17 00:00:00 2001 From: Daniel Wu Date: Wed, 2 Aug 2023 21:47:19 +0800 Subject: [PATCH 18/18] fix: add HY cdn type --- huya.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/huya.py b/huya.py index c85a4c2..18944e9 100644 --- a/huya.py +++ b/huya.py @@ -11,7 +11,7 @@ def live(info): stream_info = dict({'flv': {}, 'hls': {}}) - cdn_type = dict({'AL': '阿里', 'TX': '腾讯', 'HW': '华为', 'HS': '火山', 'WS': '网宿'}) + cdn_type = dict({'AL': '阿里', 'TX': '腾讯', 'HW': '华为', 'HS': '火山', 'WS': '网宿', 'HY': '虎牙'}) uid = get_anonymous_uid() for s in info["roomInfo"]["tLiveInfo"]["tLiveStreamInfo"]["vStreamInfo"]["value"]: if s["sFlvUrl"]: