mirror of
https://github.com/wbt5/real-url.git
synced 2025-07-28 12:15:52 +08:00
Compare commits
13 Commits
afcd4d3be8
...
f24c64ca4b
Author | SHA1 | Date | |
---|---|---|---|
|
f24c64ca4b | ||
|
b06ec4ca96 | ||
|
0f40b42d76 | ||
|
fe41cd305d | ||
|
a36b9aae75 | ||
|
7ad608a279 | ||
|
071a83cd3a | ||
|
155c47a67a | ||
|
3312118315 | ||
|
aad15f17b1 | ||
|
022929fef3 | ||
|
0235aa6f77 | ||
|
9a3fdbbddc |
@ -8,7 +8,7 @@
|
||||
|
||||
**26** 个直播平台的直播源获取:斗鱼直播、虎牙直播、哔哩哔哩直播、战旗直播、网易 CC 直播、火猫直播、企鹅电竞、YY 直播、一直播、快手直播、花椒直播、映客直播、西瓜直播、触手直播、NOW 直播、抖音直播,爱奇艺直播、酷狗直播、龙珠直播、PPS 奇秀直播、六间房、17 直播、来疯直播、优酷轮播台、网易 look 直播、千帆直播。
|
||||
|
||||
**9** 个直播平台的弹幕获取:斗鱼直播、虎牙直播、哔哩哔哩直播、快手直播、火猫直播、企鹅电竞、花椒直播、映客直播、网易CC直播。
|
||||
**14** 个直播平台的弹幕获取:斗鱼直播、虎牙直播、哔哩哔哩直播、快手直播、火猫直播、企鹅电竞、花椒直播、映客直播、网易CC直播、酷狗直播、龙珠直播、PPS奇秀、搜狐千帆、战旗直播。
|
||||
|
||||
## 运行
|
||||
|
||||
@ -23,7 +23,9 @@
|
||||
|
||||
## 更新
|
||||
|
||||
### 2020.07.11:新增网易CC直播弹幕获取
|
||||
### 2020.07.18:新增酷狗、龙珠、PPS奇秀、搜狐千帆、战旗直播等5个平台的弹幕获取
|
||||
|
||||
2020.07.11:新增网易CC直播弹幕获取
|
||||
|
||||
2020.07.05:新增花椒直播、映客直播弹幕获取;更新虎牙直播源
|
||||
|
||||
|
@ -11,6 +11,11 @@ from .egame import eGame
|
||||
from .huajiao import HuaJiao
|
||||
from .inke import Inke
|
||||
from .cc import CC
|
||||
from .kugou import KuGou
|
||||
from .zhanqi import ZhanQi
|
||||
from .longzhu import LongZhu
|
||||
from .pps import QiXiu
|
||||
from .qf import QF
|
||||
|
||||
__all__ = ['DanmakuClient']
|
||||
|
||||
@ -36,7 +41,12 @@ class DanmakuClient:
|
||||
'egame.qq.com': eGame,
|
||||
'huajiao.com': HuaJiao,
|
||||
'inke.cn': Inke,
|
||||
'cc.163.com': CC}.items():
|
||||
'cc.163.com': CC,
|
||||
'fanxing.kugou.com': KuGou,
|
||||
'zhanqi.tv': ZhanQi,
|
||||
'longzhu.com': LongZhu,
|
||||
'pps.tv': QiXiu,
|
||||
'qf.56.com': QF}.items():
|
||||
if re.match(r'^(?:http[s]?://)?.*?%s/(.+?)$' % u, url):
|
||||
self.__site = s
|
||||
self.__u = u
|
||||
@ -49,14 +59,21 @@ class DanmakuClient:
|
||||
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)
|
||||
for reg_data in reg_datas:
|
||||
await self.__ws.send_bytes(reg_data)
|
||||
if reg_datas:
|
||||
for reg_data in reg_datas:
|
||||
if self.__u == 'qf.56.com':
|
||||
await self.__ws.send_str(reg_data)
|
||||
else:
|
||||
await self.__ws.send_bytes(reg_data)
|
||||
|
||||
async def heartbeats(self):
|
||||
while not self.__stop:
|
||||
while not self.__stop and self.__site.heartbeat:
|
||||
await asyncio.sleep(self.__site.heartbeatInterval)
|
||||
try:
|
||||
await self.__ws.send_bytes(self.__site.heartbeat)
|
||||
if self.__u == 'qf.56.com':
|
||||
await self.__ws.send_str(self.__site.heartbeat)
|
||||
else:
|
||||
await self.__ws.send_bytes(self.__site.heartbeat)
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -70,7 +87,7 @@ class DanmakuClient:
|
||||
await asyncio.sleep(1)
|
||||
await self.init_ws()
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
async def init_ws_huajiao(self):
|
||||
rid = re.search(r'\d+', self.__url).group(0)
|
||||
s = self.__site(rid)
|
||||
@ -88,17 +105,10 @@ class DanmakuClient:
|
||||
await self.__dm_queue.put(m)
|
||||
count += 1
|
||||
await self.heartbeats()
|
||||
|
||||
async def init_ws_inke(self):
|
||||
ws_url = await self.__site.get_ws_info(self.__url)
|
||||
self.__ws = await self.__hs.ws_connect(ws_url)
|
||||
await self.fetch_danmaku()
|
||||
|
||||
async def start(self):
|
||||
if self.__u == 'huajiao.com':
|
||||
await self.init_ws_huajiao()
|
||||
elif self.__u == 'inke.cn':
|
||||
await self.init_ws_inke()
|
||||
else:
|
||||
await self.init_ws()
|
||||
await asyncio.gather(
|
||||
|
@ -5,6 +5,7 @@ import json
|
||||
|
||||
|
||||
class Inke:
|
||||
heartbeat = None
|
||||
|
||||
@staticmethod
|
||||
async def get_ws_info(url):
|
||||
@ -16,7 +17,7 @@ class Inke:
|
||||
async with session.get(cr) as resp:
|
||||
res = await resp.text()
|
||||
wss_url = json.loads(res).get('url')
|
||||
return wss_url
|
||||
return wss_url, None
|
||||
|
||||
@staticmethod
|
||||
def decode_msg(data):
|
||||
|
250
danmu/danmaku/kugou.proto
Normal file
250
danmu/danmaku/kugou.proto
Normal file
@ -0,0 +1,250 @@
|
||||
syntax = "proto2";
|
||||
package KuGouPack;
|
||||
|
||||
message LoginRequest {
|
||||
optional int32 cmd = 1;
|
||||
optional int32 roomid = 2;
|
||||
optional int64 kugouid = 3;
|
||||
optional string token = 4;
|
||||
optional string key = 5;
|
||||
optional int32 appid = 6;
|
||||
optional int32 platid = 7;
|
||||
optional int32 subplatid = 8;
|
||||
optional string version = 9;
|
||||
optional string deviceNo = 10;
|
||||
optional string imei = 11;
|
||||
optional int32 v = 12;
|
||||
optional int32 referer = 13;
|
||||
optional int32 clientid = 14;
|
||||
optional string soctoken = 15;
|
||||
optional string offset = 16;
|
||||
optional int32 appChannel = 17;
|
||||
optional string sid = 18;
|
||||
optional int32 source = 19;
|
||||
optional string uuid = 20;
|
||||
optional string systemVersion = 21;
|
||||
optional string entryid = 22;
|
||||
optional string socsid = 23;
|
||||
optional string deviceid = 24;
|
||||
}
|
||||
|
||||
message LoginResponse {
|
||||
optional string nickname = 1;
|
||||
optional int32 richlevel = 2;
|
||||
optional int64 userid = 3;
|
||||
optional int64 kugouid = 4;
|
||||
optional int32 fanstags = 5;
|
||||
optional int32 v = 6;
|
||||
optional int32 referer = 7;
|
||||
optional string wellcomes = 8;
|
||||
optional string img = 9;
|
||||
}
|
||||
|
||||
message ErrorResponse {
|
||||
optional int32 cmd = 1;
|
||||
optional int32 type = 2;
|
||||
optional int64 seq = 3;
|
||||
optional int32 status = 4;
|
||||
optional int32 errorno = 5;
|
||||
optional string msg = 6;
|
||||
optional string socsid = 7;
|
||||
}
|
||||
|
||||
message ChatResponse {
|
||||
optional string chatmsg = 1;
|
||||
optional int64 senderid = 2;
|
||||
optional int64 senderkugouid = 3;
|
||||
optional string sendername = 4;
|
||||
optional int32 senderrichlevel = 5;
|
||||
optional int64 receiverid = 6;
|
||||
optional int64 receiverkugouid = 7;
|
||||
optional string receivername = 8;
|
||||
optional int32 receiverrichlevel = 9;
|
||||
optional int32 issecrect = 10;
|
||||
optional AdditionalInfo additionalInfo = 11;
|
||||
optional int32 v = 12;
|
||||
optional int64 seq = 13;
|
||||
optional int32 isa = 14;
|
||||
optional int32 rlight = 15;
|
||||
optional int32 rdiffExp = 16;
|
||||
optional int32 rsvip = 17;
|
||||
optional int32 rsvipl = 18;
|
||||
optional string mac = 19;
|
||||
optional Complain complain = 20;
|
||||
}
|
||||
|
||||
message AdditionalInfo {
|
||||
optional int32 fanstags = 1;
|
||||
}
|
||||
|
||||
message Complain {
|
||||
optional string msg = 1;
|
||||
optional string url = 2;
|
||||
}
|
||||
|
||||
message Message {
|
||||
optional string offset = 1;
|
||||
optional int32 ack = 2;
|
||||
optional int32 rpt = 3;
|
||||
optional string msgId = 4;
|
||||
optional MCompression compression = 5;
|
||||
optional MCodec codec = 6;
|
||||
optional bytes content = 7;
|
||||
}
|
||||
|
||||
message ContentMessage {
|
||||
optional int32 cmd = 1;
|
||||
optional bytes content = 2;
|
||||
optional int32 roomid = 3;
|
||||
optional int64 receiverid = 4;
|
||||
optional int64 receiverkugouid = 5;
|
||||
optional int64 senderid = 6;
|
||||
optional int64 sendkugouid = 7;
|
||||
optional int32 appId = 8;
|
||||
optional int64 gid = 9;
|
||||
optional int32 rpt = 10;
|
||||
optional int64 time = 11;
|
||||
optional int64 plev = 12;
|
||||
optional int64 pvalue = 13;
|
||||
optional bytes ext = 14;
|
||||
optional Sinfo sinfo = 15;
|
||||
optional MCodec codec = 16;
|
||||
optional Risk risk = 17;
|
||||
}
|
||||
|
||||
message Risk {
|
||||
optional string m = 1;
|
||||
optional string l = 2;
|
||||
optional int32 t = 3;
|
||||
}
|
||||
|
||||
message Sinfo {
|
||||
optional int32 light = 1;
|
||||
optional int32 de = 2;
|
||||
optional int32 svip = 3;
|
||||
optional int32 svipl = 4;
|
||||
optional int32 ck = 5;
|
||||
optional string ckname = 6;
|
||||
optional string skname = 7;
|
||||
optional string ckid = 8;
|
||||
optional string ckimg = 9;
|
||||
optional string logo = 10;
|
||||
optional int32 sex = 11;
|
||||
optional int32 bt = 12;
|
||||
}
|
||||
|
||||
enum MCompression {
|
||||
M_NONE = 0;
|
||||
M_GZIP = 1;
|
||||
M_LZ4 = 2;
|
||||
M_SNAPPY = 3;
|
||||
M_ZSTD = 4;
|
||||
}
|
||||
|
||||
enum MCodec {
|
||||
M_JSON = 0;
|
||||
M_PROTOBUF = 1;
|
||||
}
|
||||
|
||||
message Extension {
|
||||
optional int32 ui = 1;
|
||||
optional int32 isSpRoom = 2;
|
||||
optional StliVo stli = 3;
|
||||
optional VipDataVo vipData = 4;
|
||||
optional UsingMountVo usingMount = 5;
|
||||
optional UsingMedalVo usingMedal = 6;
|
||||
optional HonorMedalVo honorMedal = 7;
|
||||
optional UserGuardVo userGuard = 8;
|
||||
optional LittleGuardVo littleGuard = 9;
|
||||
optional WoreUserPlateVo defaultPlate = 10;
|
||||
optional string plateName = 11;
|
||||
optional int32 starCard = 12;
|
||||
optional int32 external = 13;
|
||||
optional string exMemo = 14;
|
||||
optional bool p = 15;
|
||||
optional int32 worship = 16;
|
||||
optional BubbleVo bubble = 17;
|
||||
optional int32 z = 18;
|
||||
optional int32 isGoldFans = 19;
|
||||
optional string token = 20;
|
||||
optional int64 kugouId = 21;
|
||||
optional StarFollowerVo starFollower = 22;
|
||||
optional int32 v_tme = 23;
|
||||
optional int32 v_kg = 24;
|
||||
optional string ar = 25;
|
||||
optional int32 isAndroid = 26;
|
||||
optional int32 clientPlat = 27;
|
||||
optional int32 blackCard = 28;
|
||||
optional int32 v_l = 29;
|
||||
optional BossGroupVo bossGroup = 30;
|
||||
optional CeremonyVo ceremony = 31;
|
||||
optional int32 referer = 32;
|
||||
optional int32 isNew = 33;
|
||||
}
|
||||
|
||||
message StliVo {
|
||||
optional int32 st = 1;
|
||||
optional int32 sl = 2;
|
||||
optional int32 isAdmin = 3;
|
||||
}
|
||||
|
||||
message VipDataVo {
|
||||
optional int32 v = 1;
|
||||
optional string c = 2;
|
||||
optional int32 vl = 3;
|
||||
}
|
||||
|
||||
message UsingMountVo {
|
||||
optional string id = 1;
|
||||
optional string n = 2;
|
||||
optional string swf = 3;
|
||||
optional string bi = 4;
|
||||
optional string si = 5;
|
||||
optional string p = 6;
|
||||
optional int32 s = 7;
|
||||
}
|
||||
|
||||
message UsingMedalVo {
|
||||
optional string medalList = 1;
|
||||
}
|
||||
|
||||
message HonorMedalVo {
|
||||
optional string honorList = 1;
|
||||
}
|
||||
|
||||
message UserGuardVo {
|
||||
optional string g = 1;
|
||||
optional string i = 2;
|
||||
}
|
||||
|
||||
message LittleGuardVo {
|
||||
optional int32 l = 1;
|
||||
optional int32 g = 2;
|
||||
}
|
||||
|
||||
message WoreUserPlateVo {
|
||||
optional int64 kid = 1;
|
||||
optional string plateName = 2;
|
||||
optional int32 type = 3;
|
||||
optional int32 l = 4;
|
||||
optional int32 i = 5;
|
||||
}
|
||||
|
||||
message BubbleVo {
|
||||
optional int32 id = 1;
|
||||
optional string bg = 2;
|
||||
}
|
||||
|
||||
message StarFollowerVo {
|
||||
optional int32 l = 1;
|
||||
}
|
||||
|
||||
message BossGroupVo {
|
||||
optional int64 gid = 1;
|
||||
optional string gn = 2;
|
||||
optional int32 gr = 3;
|
||||
}
|
||||
|
||||
message CeremonyVo {
|
||||
optional int32 pl = 1;
|
||||
}
|
229
danmu/danmaku/kugou.py
Normal file
229
danmu/danmaku/kugou.py
Normal file
@ -0,0 +1,229 @@
|
||||
from . import kugou_pb2 as pb
|
||||
import struct
|
||||
import requests
|
||||
|
||||
|
||||
class InitKugou:
|
||||
|
||||
def __init__(self):
|
||||
self.MAGIC = {
|
||||
'index': 0,
|
||||
'length': 1,
|
||||
'value': 100
|
||||
}
|
||||
self.VERSION = {
|
||||
'index': 1,
|
||||
'length': 2,
|
||||
'value': 1
|
||||
}
|
||||
self.TYPE = {
|
||||
'index': 2,
|
||||
'length': 1,
|
||||
'value': 1
|
||||
}
|
||||
self.HEADER = {
|
||||
'index': 3,
|
||||
'length': 2,
|
||||
'value': 12
|
||||
}
|
||||
self.CMD = {
|
||||
'index': 4,
|
||||
'length': 4,
|
||||
'value': 0
|
||||
}
|
||||
self.PAYLOAD = {
|
||||
'index': 5,
|
||||
'length': 4,
|
||||
'value': 0
|
||||
}
|
||||
self.ATTR = {
|
||||
'index': 6,
|
||||
'length': 1,
|
||||
'value': 0
|
||||
}
|
||||
self.CRC = {
|
||||
'index': 7,
|
||||
'length': 2,
|
||||
'value': 0
|
||||
}
|
||||
self.SKIP = {
|
||||
'index': 8,
|
||||
'length': 1,
|
||||
'value': 0
|
||||
}
|
||||
|
||||
self.f = [self.MAGIC, self.VERSION, self.TYPE, self.HEADER,
|
||||
self.CMD, self.PAYLOAD, self.ATTR, self.CRC, self.SKIP]
|
||||
|
||||
def reg(self, rid):
|
||||
url = 'https://fx2.service.kugou.com/socket_scheduler/pc/v2/address.jsonp'
|
||||
payload = {
|
||||
'rid': rid,
|
||||
'_v': '7.0.0',
|
||||
'_p': 0,
|
||||
'pv': 20191231,
|
||||
'at': 102,
|
||||
'cid': 105
|
||||
}
|
||||
|
||||
with requests.Session() as s:
|
||||
r = s.get(url, params=payload).json()
|
||||
soctoken = r['data']['soctoken']
|
||||
|
||||
reg_data = {
|
||||
'appid': 1010,
|
||||
'clientid': 105,
|
||||
'cmd': 201,
|
||||
'deviceNo': '4edc0e89-ccaf-452c-bce4-00f4cb6bb5bb',
|
||||
'kugouid': 0,
|
||||
'platid': 18,
|
||||
'referer': 0,
|
||||
'roomid': rid,
|
||||
'sid': '8b9b79a7-a742-4397-fcc0-94efa3a1c920',
|
||||
'soctoken': soctoken,
|
||||
'v': 20191231
|
||||
}
|
||||
|
||||
a = pb.LoginRequest()
|
||||
for k, v in reg_data.items():
|
||||
setattr(a, k, v)
|
||||
b = pb.Message()
|
||||
b.content = a.SerializeToString()
|
||||
e = b.SerializeToString()
|
||||
reg = self.encode_(e, reg_data['cmd'])
|
||||
return reg
|
||||
|
||||
def g(self, *e):
|
||||
if len(e) > 1 and e[1]:
|
||||
t = e[1]
|
||||
else:
|
||||
t = 12
|
||||
n = 0
|
||||
i = 0
|
||||
e = e[0]
|
||||
while i < e and i < len(self.f):
|
||||
n += self.f[i]['length']
|
||||
i += 1
|
||||
if e == len(self.f):
|
||||
return n + t - 12
|
||||
else:
|
||||
return n
|
||||
|
||||
def encode_(self, e, t):
|
||||
n = len(self.f)
|
||||
i = len(e)
|
||||
# r = self.g(n) + i
|
||||
self.PAYLOAD['value'] = i
|
||||
self.CMD['value'] = t
|
||||
|
||||
buf = b''
|
||||
for s in self.f:
|
||||
# offset = self.g(s['index'])
|
||||
value = s['value']
|
||||
if s['length'] == 1:
|
||||
fmt = '!b'
|
||||
elif s['length'] == 2:
|
||||
fmt = '!h'
|
||||
else:
|
||||
fmt = '!i'
|
||||
buf += struct.pack(fmt, value)
|
||||
|
||||
buf += struct.pack('!i', i)
|
||||
buf = buf[:self.g(n)] + e
|
||||
return buf
|
||||
|
||||
def v(self, e, t):
|
||||
if t['length'] == 1:
|
||||
fmt = '!b'
|
||||
elif t['length'] == 2:
|
||||
fmt = '!h'
|
||||
else:
|
||||
fmt = '!i'
|
||||
r, = struct.unpack_from(fmt, e, self.g(t['index']))
|
||||
return r
|
||||
|
||||
# def k(self, e, i, o=False):
|
||||
# if i == 1:
|
||||
# a = zlib.decompress(e)
|
||||
# elif i == 2:
|
||||
# a = zlib.decompress(e)
|
||||
# else:
|
||||
# a = e
|
||||
# if o:
|
||||
# return self.r(a)
|
||||
# else:
|
||||
# return a
|
||||
#
|
||||
# def r(self, e):
|
||||
# pass
|
||||
|
||||
def decode_(self, message):
|
||||
t = len(message)
|
||||
n = len(self.f)
|
||||
|
||||
if t <= 0:
|
||||
return {}
|
||||
|
||||
if self.v(message, self.TYPE) == 0:
|
||||
return {}
|
||||
|
||||
r = self.v(message, self.HEADER)
|
||||
cmd = self.v(message, self.CMD) # cmd
|
||||
a = self.g(n, r)
|
||||
|
||||
if t < a:
|
||||
return {}
|
||||
|
||||
o = message[a:] # payloadBuffer
|
||||
|
||||
if not o or not cmd:
|
||||
return
|
||||
|
||||
msgs = []
|
||||
msg = {'name': '', 'content': '', 'msg_type': 'other'}
|
||||
if cmd == 201 or cmd == 501:
|
||||
# CMD
|
||||
# 201:LoginResponse,欢迎信息;
|
||||
# 501:ChatResponse,聊天信息;
|
||||
# 602:ContentMessage,礼物信息;
|
||||
# 901:ErrorResponse;
|
||||
s = pb.Message()
|
||||
s.ParseFromString(o)
|
||||
if s.codec == 1:
|
||||
# if s.compression:
|
||||
# s.content = self.k(s.content, s.compression)
|
||||
s1 = pb.ContentMessage()
|
||||
s1.ParseFromString(s.content)
|
||||
if s1.codec == 1:
|
||||
# if hasattr(s1, 'compression'):
|
||||
# s1.content = self.k(s1.content, s1.compression)
|
||||
s2 = pb.ChatResponse()
|
||||
s2.ParseFromString(s1.content)
|
||||
if cmd == 201:
|
||||
msg['name'] = 'SYS'
|
||||
msg['content'] = s2.receivername.replace('%nick', s2.chatmsg)
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif cmd == 501:
|
||||
msg['name'] = s2.sendername
|
||||
msg['content'] = s2.chatmsg
|
||||
msg['msg_type'] = 'danmaku'
|
||||
msgs.append(msg.copy())
|
||||
return msgs
|
||||
|
||||
|
||||
class KuGou:
|
||||
heartbeat = b'\x64\x00\x01\x00'
|
||||
wss_url = 'wss://chat1wss.kugou.com/acksocket'
|
||||
heartbeatInterval = 10
|
||||
s = InitKugou()
|
||||
|
||||
@staticmethod
|
||||
async def get_ws_info(url):
|
||||
rid = url.split('/')[-1]
|
||||
reg_data = KuGou.s.reg(int(rid))
|
||||
return KuGou.wss_url, [reg_data]
|
||||
|
||||
@staticmethod
|
||||
def decode_msg(data):
|
||||
msgs = KuGou.s.decode_(data)
|
||||
return msgs
|
2047
danmu/danmaku/kugou_pb2.py
Normal file
2047
danmu/danmaku/kugou_pb2.py
Normal file
File diff suppressed because one or more lines are too long
37
danmu/danmaku/longzhu.py
Normal file
37
danmu/danmaku/longzhu.py
Normal file
@ -0,0 +1,37 @@
|
||||
import json
|
||||
import aiohttp
|
||||
import re
|
||||
|
||||
|
||||
class LongZhu:
|
||||
heartbeat = None
|
||||
|
||||
@staticmethod
|
||||
async def get_ws_info(url):
|
||||
rid = url.split('/')[-1]
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get('http://m.longzhu.com/' + rid) as resp:
|
||||
res1 = await resp.text()
|
||||
roomid = re.search(r'var roomId = (\d+);', res1).group(1)
|
||||
async with session.get('http://idc-gw.longzhu.com/mbidc?roomId=' + roomid) as resp2:
|
||||
res2 = json.loads(await resp2.text())
|
||||
ws_url = res2['data']['redirect_to'] + '?room_id=' + roomid
|
||||
return ws_url, None
|
||||
|
||||
@staticmethod
|
||||
def decode_msg(message):
|
||||
msgs = []
|
||||
msg = {'name': '', 'content': '', 'msg_type': 'other'}
|
||||
message = json.loads(message)
|
||||
type_ = message['type']
|
||||
# type_ == 'gift' 礼物
|
||||
if type_ == 'chat':
|
||||
msg['name'] = message['msg']['user']['username']
|
||||
msg['content'] = (message['msg']['content']).strip()
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif type_ == 'commonjoin':
|
||||
msg['name'] = message['msg']['user']['username']
|
||||
msg['content'] = message['msg']['userMessage']
|
||||
msg['msg_type'] = 'danmaku'
|
||||
msgs.append(msg.copy())
|
||||
return msgs
|
73
danmu/danmaku/pps.py
Normal file
73
danmu/danmaku/pps.py
Normal file
@ -0,0 +1,73 @@
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
import json
|
||||
|
||||
|
||||
class QiXiu:
|
||||
heartbeat = None
|
||||
|
||||
@staticmethod
|
||||
async def get_ws_info(url):
|
||||
rid = url.split('/')[-1]
|
||||
s = bytes([57, 77, 83, 73, 53, 86, 85, 71, 50, 81, 74, 80, 66, 52, 78, 54, 68, 48, 81,
|
||||
83, 89, 87, 69, 72, 67, 90, 83, 75, 84, 49, 77, 50, 84, 65, 75, 88]).decode('utf-8')
|
||||
# ua = 'User-Agent'
|
||||
# ak = deviceid = md5(str(int(time.time() * 1e3)) + ua + '0000')
|
||||
ak = deviceid = '118d2ae703e62992263e6741afbb5627'
|
||||
e = {
|
||||
'ag': 1,
|
||||
'ak': ak,
|
||||
'at': 3,
|
||||
'd': deviceid,
|
||||
'n': 1,
|
||||
'p': 1,
|
||||
'r': rid,
|
||||
'v': '1.01.0801'
|
||||
}
|
||||
i = ''
|
||||
for k, v in e.items():
|
||||
i += '{}={}|'.format(k, str(v))
|
||||
e['sg'] = hashlib.md5((i + s).encode('utf-8')).hexdigest()
|
||||
ws_url = 'ws://qx-ws.iqiyi.com/ws?' + urllib.parse.urlencode(e)
|
||||
return ws_url, None
|
||||
|
||||
@staticmethod
|
||||
def decode_msg(data):
|
||||
message = json.loads(data)
|
||||
msgs = []
|
||||
msg = {'name': '', 'content': '', 'msg_type': 'other'}
|
||||
for ms in message:
|
||||
m = ms['ct']
|
||||
type_ = ms['t']
|
||||
# 200001:进场消息
|
||||
# 300001:聊天信息
|
||||
# 102001:礼物
|
||||
# 1100002:礼物
|
||||
# 400001:人气值
|
||||
# 5000010:升级
|
||||
# 700095:live_score
|
||||
# 700091:排名
|
||||
# 其他:系统消息
|
||||
if type_ == 300001:
|
||||
msg['name'] = m['op_userInfo']['nick_name']
|
||||
msg['content'] = m['msg']
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif type_ == 102001:
|
||||
msg['name'] = m['op_userInfo']['nick_name']
|
||||
num = m['op_info']['num']
|
||||
gift = m['op_info']['name']
|
||||
msg['content'] = '送出{}个{}'.format(num, gift)
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif type_ in [200001, 1100002, 110001, 3019, 3022, 3002, 3024]:
|
||||
msg['name'] = 'SYS'
|
||||
info = m['op_info'].get('public_chat_msg', 0)
|
||||
if not info:
|
||||
info = m['op_info']['roll_chat_msg']
|
||||
content = ''
|
||||
items = info['items']
|
||||
for item in items:
|
||||
content += item.get('content', '')
|
||||
msg['content'] = content
|
||||
msg['msg_type'] = 'danmaku'
|
||||
msgs.append(msg.copy())
|
||||
return msgs
|
65
danmu/danmaku/qf.py
Normal file
65
danmu/danmaku/qf.py
Normal file
@ -0,0 +1,65 @@
|
||||
import aiohttp
|
||||
import json
|
||||
import time
|
||||
|
||||
|
||||
class QF:
|
||||
heartbeat = '2::'
|
||||
heartbeatInterval = 30
|
||||
|
||||
@staticmethod
|
||||
async def get_ws_info(url):
|
||||
rid = url.split('/')[-1]
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get('https://conn-chat.qf.56.com/socket.io/1/') as resp:
|
||||
res = await resp.text()
|
||||
sessid = res.split(':')[0]
|
||||
ws_url = 'wss://conn-chat.qf.56.com/socket.io/1/websocket/' + sessid
|
||||
# e = 2
|
||||
t = 'connector-sio.entryHandler.enter'
|
||||
s = {
|
||||
'userId': '',
|
||||
'aq': 0,
|
||||
'roomId': rid,
|
||||
'token': '',
|
||||
'ip': '',
|
||||
'recet': 0,
|
||||
'params': {
|
||||
'referFrom': '0'
|
||||
},
|
||||
'apType': 0,
|
||||
'timestamp': int(time.time() * 1e3)
|
||||
}
|
||||
r = json.dumps(s, separators=(',', ':'))
|
||||
if len(t) > 255:
|
||||
raise Exception('route maxlength is overflow')
|
||||
reg_data = '3:::' + '\x00\x00\x00\x02 ' + t + r
|
||||
return ws_url, [reg_data]
|
||||
|
||||
@staticmethod
|
||||
def decode_msg(message):
|
||||
msgs = []
|
||||
msg = {'name': '', 'content': '', 'msg_type': 'other'}
|
||||
type_ = message[0]
|
||||
if type_ == '3':
|
||||
data = json.loads(message[4:])
|
||||
route = data.get('route', 0)
|
||||
body = data['body']
|
||||
if route == 'onUserLog': # 入场信息
|
||||
msg['name'] = 'SYS'
|
||||
msg['content'] = body['userName'] + ' 来了'
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif route == 'onChat': # 弹幕
|
||||
msg['name'] = body['userName']
|
||||
msg['content'] = body['content']
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif route == 'onGift': # 弹幕
|
||||
msg['name'] = 'SYS'
|
||||
msg['content'] = body['userName'] + ' 送礼物 ' + body['giftName']
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif route == 'onBc': # 弹幕
|
||||
msg['name'] = 'SYS'
|
||||
msg['content'] = body['userName'] + ':' + body['msg']
|
||||
msg['msg_type'] = 'danmaku'
|
||||
msgs.append(msg.copy())
|
||||
return msgs
|
63
danmu/danmaku/zhanqi.py
Normal file
63
danmu/danmaku/zhanqi.py
Normal file
@ -0,0 +1,63 @@
|
||||
import json
|
||||
import struct
|
||||
import aiohttp
|
||||
|
||||
|
||||
class ZhanQi:
|
||||
heartbeat = b'\xbb\xcc\x00\x00\x00\x00\x15\x00\x00\x00\x10\'{"cmdid": "keeplive"}'
|
||||
wss_url = 'wss://gw.zhanqi.tv/'
|
||||
heartbeatInterval = 30
|
||||
|
||||
@staticmethod
|
||||
async def get_ws_info(url):
|
||||
reg_datas = []
|
||||
rid = url.split('/')[-1]
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get('https://m.zhanqi.tv/api/static/v2.1/room/domain/{}.json'.format(rid)) as resp:
|
||||
info = json.loads(await resp.text())
|
||||
roomid = info['data']['id']
|
||||
async with session.get('https://m.zhanqi.tv/api/public/room.viewer') as resp2:
|
||||
res = json.loads(await resp2.text())
|
||||
gid = res['data']['gid']
|
||||
sid = res['data']['sid']
|
||||
timestamp = res['data']['timestamp']
|
||||
|
||||
login = {
|
||||
'cmdid': 'loginreq',
|
||||
'roomid': int(roomid),
|
||||
'chatroomid': 0,
|
||||
'gid': gid,
|
||||
'sid': sid,
|
||||
't': 0,
|
||||
'r': 0,
|
||||
'device': 1,
|
||||
'fhost': 'mzhanqi',
|
||||
'uid': 0,
|
||||
'timestamp': timestamp
|
||||
}
|
||||
body = json.dumps(login, separators=(',', ':'))
|
||||
head = struct.pack('<HIIH', 0xCCBB, 0, len(body), 10000)
|
||||
reg_data = head + body.encode()
|
||||
reg_datas.append(reg_data)
|
||||
return ZhanQi.wss_url, reg_datas
|
||||
|
||||
@staticmethod
|
||||
def decode_msg(message):
|
||||
message = (message[12:])
|
||||
data = json.loads(message)
|
||||
msgs = []
|
||||
msg = {'name': '', 'content': '', 'msg_type': 'other'}
|
||||
if data['cmdid'] == 'chatmessage': # 聊天信息
|
||||
msg['name'] = data['fromname']
|
||||
msg['content'] = data['content']
|
||||
msg['msg_type'] = 'danmaku'
|
||||
elif data['cmdid'] == 'Gift.Display': # 礼物信息
|
||||
pass
|
||||
elif data['cmdid'] == 'Prop.Display': # 礼物信息
|
||||
pass
|
||||
elif data['cmdid'] == 'getuc': # 人气数
|
||||
pass
|
||||
elif data['cmdid'] == 'loginresp': # 欢迎信息
|
||||
pass
|
||||
msgs.append(msg.copy())
|
||||
return msgs
|
@ -21,7 +21,7 @@ async def main(url):
|
||||
|
||||
|
||||
a = input('请输入直播间地址:\n')
|
||||
asyncio.run(main(a))
|
||||
asyncio.run(main())
|
||||
|
||||
# 虎牙:https://www.huya.com/11352915
|
||||
# 斗鱼:https://www.douyu.com/85894
|
||||
@ -32,3 +32,8 @@ asyncio.run(main(a))
|
||||
# 花椒直播:https://www.huajiao.com/l/303344861?qd=hu
|
||||
# 映客直播:https://www.inke.cn/liveroom/index.html?uid=87493223&id=1593906372018299
|
||||
# CC直播:https://cc.163.com/363936598/
|
||||
# 酷狗直播:https://fanxing.kugou.com/1676290
|
||||
# 战旗直播
|
||||
# 龙珠直播:http://star.longzhu.com/wsde135864219
|
||||
# PPS奇秀直播:https://x.pps.tv/room/208337
|
||||
# 搜狐千帆直播:https://qf.56.com/520208a
|
||||
|
4
huya.py
4
huya.py
@ -42,7 +42,7 @@ def get_real_url(room_id):
|
||||
liveLineUrl = re.findall(r'liveLineUrl = "([\s\S]*?)";', response)[0]
|
||||
if liveLineUrl:
|
||||
if 'replay' in liveLineUrl:
|
||||
return '直播录像:' + liveLineUrl
|
||||
return '直播录像:https:' + liveLineUrl
|
||||
else:
|
||||
liveLineUrl = live(liveLineUrl)
|
||||
real_url = ["https:" + liveLineUrl, "https:" + re.sub(r'_\d{4}.m3u8', '.m3u8', liveLineUrl)]
|
||||
@ -56,4 +56,4 @@ def get_real_url(room_id):
|
||||
rid = input('请输入虎牙房间号:\n')
|
||||
real_url = get_real_url(rid)
|
||||
print('该直播间源地址为:')
|
||||
print(real_url)
|
||||
print(real_url)
|
||||
|
Loading…
x
Reference in New Issue
Block a user