# -*- encoding: utf-8 -*-
"""
Author: Hmily
GitHub: https://github.com/ihmily
Date: 2023-07-15 23:15:00
Update: 2025-07-19 17:43:00
Copyright (c) 2023-2025 by Hmily, All Rights Reserved.
Function: Get live stream data.
"""
import hashlib
import random
import subprocess
import time
import uuid
from operator import itemgetter
import urllib.parse
import urllib.error
from typing import List
import httpx
import ssl
import re
import json
import execjs
import urllib.request
# from . import JS_SCRIPT_PATH
import utils
from utils import trace_error_decorator, generate_random_string
from logger import script_path
from room import get_sec_user_id, get_unique_id, UnsupportedUrlError
from http_clients.async_http import async_req
import os
import sys
from pathlib import Path
from initializer import check_node
current_file_path = Path(__file__).resolve()
current_dir = current_file_path.parent
JS_SCRIPT_PATH = current_dir / 'javascript'
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
OptionalStr = str | None
OptionalDict = dict | None
def get_params(url: str, params: str) -> OptionalStr:
parsed_url = urllib.parse.urlparse(url)
query_params = urllib.parse.parse_qs(parsed_url.query)
if params in query_params:
return query_params[params][0]
async def get_play_url_list(m3u8: str, proxy: OptionalStr = None, header: OptionalDict = None,
abroad: bool = False) -> List[str]:
resp = await async_req(url=m3u8, proxy_addr=proxy, headers=header, abroad=abroad)
play_url_list = []
for i in resp.split('\n'):
if i.startswith('https://'):
play_url_list.append(i.strip())
if not play_url_list:
for i in resp.split('\n'):
if i.strip().endswith('m3u8'):
play_url_list.append(i.strip())
bandwidth_pattern = re.compile(r'BANDWIDTH=(\d+)')
bandwidth_list = bandwidth_pattern.findall(resp)
url_to_bandwidth = {url: int(bandwidth) for bandwidth, url in zip(bandwidth_list, play_url_list)}
play_url_list = sorted(play_url_list, key=lambda url: url_to_bandwidth[url], reverse=True)
return play_url_list
@trace_error_decorator
async def get_douyin_app_stream_data(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Referer': 'https://live.douyin.com/',
'Cookie': 'ttwid=1%7CB1qls3GdnZhUov9o2NxOMxxYS2ff6OSvEWbv0ytbES4%7C1680522049%7C280d802d6d478e3e78d0c807f7c487e7ffec0ae4e5fdd6a0fe74c3c6af149511; my_rd=1; passport_csrf_token=3ab34460fa656183fccfb904b16ff742; passport_csrf_token_default=3ab34460fa656183fccfb904b16ff742; d_ticket=9f562383ac0547d0b561904513229d76c9c21; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; store-region=cn-fj; store-region-src=uid; LOGIN_STATUS=1; __security_server_data_status=1; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; pwa2=%223%7C0%7C3%7C0%22; download_guide=%223%2F20230729%2F0%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.6%7D; strategyABtestKey=%221690824679.923%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1536%2C%5C%22screen_height%5C%22%3A864%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A150%7D%22; VIDEO_FILTER_MEMO_SELECT=%7B%22expireTime%22%3A1691443863751%2C%22type%22%3Anull%7D; home_can_add_dy_2_desktop=%221%22; __live_version__=%221.1.1.2169%22; device_web_cpu_core=8; device_web_memory_size=8; xgplayer_user_id=346045893336; csrf_session_id=2e00356b5cd8544d17a0e66484946f28; odin_tt=724eb4dd23bc6ffaed9a1571ac4c757ef597768a70c75fef695b95845b7ffcd8b1524278c2ac31c2587996d058e03414595f0a4e856c53bd0d5e5f56dc6d82e24004dc77773e6b83ced6f80f1bb70627; __ac_nonce=064caded4009deafd8b89; __ac_signature=_02B4Z6wo00f01HLUuwwAAIDBh6tRkVLvBQBy9L-AAHiHf7; ttcid=2e9619ebbb8449eaa3d5a42d8ce88ec835; webcast_leading_last_show_time=1691016922379; webcast_leading_total_show_times=1; webcast_local_quality=sd; live_can_add_dy_2_desktop=%221%22; msToken=1JDHnVPw_9yTvzIrwb7cQj8dCMNOoesXbA_IooV8cezcOdpe4pzusZE7NB7tZn9TBXPr0ylxmv-KMs5rqbNUBHP4P7VBFUu0ZAht_BEylqrLpzgt3y5ne_38hXDOX8o=; msToken=jV_yeN1IQKUd9PlNtpL7k5vthGKcHo0dEh_QPUQhr8G3cuYv-Jbb4NnIxGDmhVOkZOCSihNpA2kvYtHiTW25XNNX_yrsv5FN8O6zm3qmCIXcEe0LywLn7oBO2gITEeg=; tt_scid=mYfqpfbDjqXrIGJuQ7q-DlQJfUSG51qG.KUdzztuGP83OjuVLXnQHjsz-BRHRJu4e986'
}
if cookies:
headers['Cookie'] = cookies
async def get_app_data(room_id: str, sec_uid: str) -> dict:
app_params = {
"verifyFp": "verify_lxj5zv70_7szNlAB7_pxNY_48Vh_ALKF_GA1Uf3yteoOY",
"type_id": "0",
"live_id": "1",
"room_id": room_id,
"sec_user_id": sec_uid,
"version_code": "99.99.99",
"app_id": "1128"
}
api2 = f'https://webcast.amemv.com/webcast/room/reflow/info/?{urllib.parse.urlencode(app_params)}'
json_str2 = await async_req(url=api2, proxy_addr=proxy_addr, headers=headers)
json_data2 = json.loads(json_str2)['data']
room_data2 = json_data2['room']
room_data2['anchor_name'] = room_data2['owner']['nickname']
return room_data2
try:
web_rid = url.split('?')[0].split('live.douyin.com/')
if len(web_rid) > 1:
web_rid = web_rid[1]
params = {
"aid": "6383",
"app_name": "douyin_web",
"live_id": "1",
"device_platform": "web",
"language": "zh-CN",
"browser_language": "zh-CN",
"browser_platform": "Win32",
"browser_name": "Chrome",
"browser_version": "116.0.0.0",
"web_rid": web_rid
}
api = f'https://live.douyin.com/webcast/room/web/enter/?{urllib.parse.urlencode(params)}'
json_str = await async_req(url=api, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)['data']
room_data = json_data['data'][0]
room_data['anchor_name'] = json_data['user']['nickname']
else:
try:
data = await get_sec_user_id(url, proxy_addr=proxy_addr)
_room_id, _sec_uid = data
room_data = await get_app_data(_room_id, _sec_uid)
except UnsupportedUrlError:
unique_id = await get_unique_id(url, proxy_addr=proxy_addr)
return await get_douyin_stream_data(f'https://live.douyin.com/{unique_id}')
if room_data['status'] == 2:
if 'stream_url' not in room_data:
raise RuntimeError(
"The live streaming type or gameplay is not supported on the computer side yet, please use the "
"app to share the link for recording."
)
live_core_sdk_data = room_data['stream_url']['live_core_sdk_data']
pull_datas = room_data['stream_url']['pull_datas']
if live_core_sdk_data:
if pull_datas:
key = list(pull_datas.keys())[0]
json_str = pull_datas[key]['stream_data']
else:
json_str = live_core_sdk_data['pull_data']['stream_data']
json_data = json.loads(json_str)
if 'origin' in json_data['data']:
stream_data = live_core_sdk_data['pull_data']['stream_data']
origin_data = json.loads(stream_data)['data']['origin']['main']
sdk_params = json.loads(origin_data['sdk_params'])
origin_hls_codec = sdk_params.get('VCodec') or ''
origin_url_list = json_data['data']['origin']['main']
origin_m3u8 = {'ORIGIN': origin_url_list["hls"] + '&codec=' + origin_hls_codec}
origin_flv = {'ORIGIN': origin_url_list["flv"] + '&codec=' + origin_hls_codec}
hls_pull_url_map = room_data['stream_url']['hls_pull_url_map']
flv_pull_url = room_data['stream_url']['flv_pull_url']
room_data['stream_url']['hls_pull_url_map'] = {**origin_m3u8, **hls_pull_url_map}
room_data['stream_url']['flv_pull_url'] = {**origin_flv, **flv_pull_url}
except Exception as e:
print(f"Error message: {e} Error line: {e.__traceback__.tb_lineno}")
room_data = {'anchor_name': ""}
return room_data
@trace_error_decorator
async def get_douyin_stream_data(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Referer': 'https://live.douyin.com/',
'Cookie': 'ttwid=1%7CB1qls3GdnZhUov9o2NxOMxxYS2ff6OSvEWbv0ytbES4%7C1680522049%7C280d802d6d478e3e78d0c807f7c487e7ffec0ae4e5fdd6a0fe74c3c6af149511; my_rd=1; passport_csrf_token=3ab34460fa656183fccfb904b16ff742; passport_csrf_token_default=3ab34460fa656183fccfb904b16ff742; d_ticket=9f562383ac0547d0b561904513229d76c9c21; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; store-region=cn-fj; store-region-src=uid; LOGIN_STATUS=1; __security_server_data_status=1; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; pwa2=%223%7C0%7C3%7C0%22; download_guide=%223%2F20230729%2F0%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.6%7D; strategyABtestKey=%221690824679.923%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1536%2C%5C%22screen_height%5C%22%3A864%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A150%7D%22; VIDEO_FILTER_MEMO_SELECT=%7B%22expireTime%22%3A1691443863751%2C%22type%22%3Anull%7D; home_can_add_dy_2_desktop=%221%22; __live_version__=%221.1.1.2169%22; device_web_cpu_core=8; device_web_memory_size=8; xgplayer_user_id=346045893336; csrf_session_id=2e00356b5cd8544d17a0e66484946f28; odin_tt=724eb4dd23bc6ffaed9a1571ac4c757ef597768a70c75fef695b95845b7ffcd8b1524278c2ac31c2587996d058e03414595f0a4e856c53bd0d5e5f56dc6d82e24004dc77773e6b83ced6f80f1bb70627; __ac_nonce=064caded4009deafd8b89; __ac_signature=_02B4Z6wo00f01HLUuwwAAIDBh6tRkVLvBQBy9L-AAHiHf7; ttcid=2e9619ebbb8449eaa3d5a42d8ce88ec835; webcast_leading_last_show_time=1691016922379; webcast_leading_total_show_times=1; webcast_local_quality=sd; live_can_add_dy_2_desktop=%221%22; msToken=1JDHnVPw_9yTvzIrwb7cQj8dCMNOoesXbA_IooV8cezcOdpe4pzusZE7NB7tZn9TBXPr0ylxmv-KMs5rqbNUBHP4P7VBFUu0ZAht_BEylqrLpzgt3y5ne_38hXDOX8o=; msToken=jV_yeN1IQKUd9PlNtpL7k5vthGKcHo0dEh_QPUQhr8G3cuYv-Jbb4NnIxGDmhVOkZOCSihNpA2kvYtHiTW25XNNX_yrsv5FN8O6zm3qmCIXcEe0LywLn7oBO2gITEeg=; tt_scid=mYfqpfbDjqXrIGJuQ7q-DlQJfUSG51qG.KUdzztuGP83OjuVLXnQHjsz-BRHRJu4e986'
}
if cookies:
headers['Cookie'] = cookies
try:
origin_url_list = None
html_str = await async_req(url=url, proxy_addr=proxy_addr, headers=headers)
match_json_str = re.search(r'(\{\\"state\\":.*?)]\\n"]\)', html_str)
if not match_json_str:
match_json_str = re.search(r'(\{\\"common\\":.*?)]\\n"]\)
',
html_str, re.DOTALL)[0]
except Exception:
raise ConnectionError("Please check if your network can access the TikTok website normally")
json_data = json.loads(json_str)
return json_data
@trace_error_decorator
async def get_kuaishou_stream_data(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
}
if cookies:
headers['Cookie'] = cookies
try:
html_str = await async_req(url=url, proxy_addr=proxy_addr, headers=headers)
except Exception as e:
print(f"Failed to fetch data from {url}.{e}")
return {"type": 1, "is_live": False}
try:
json_str = re.search('', html_str)[0]
json_data = json.loads(json_str)
rid = json_data['pageProps']['room']['roomInfo']['roomInfo']['rid']
headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0'
url2 = f'https://www.douyu.com/betard/{rid}'
json_str = await async_req(url=url2, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)
result = {
"anchor_name": json_data['room']['nickname'],
"is_live": False
}
if json_data['room']['videoLoop'] == 0 and json_data['room']['show_status'] == 1:
result["title"] = json_data['room']['room_name'].replace(' ', '')
result["is_live"] = True
result["room_id"] = json_data['room']['room_id']
return result
@trace_error_decorator
async def get_douyu_stream_data(rid: str, rate: str = '-1', proxy_addr: OptionalStr = None,
cookies: OptionalStr = None) -> dict:
did = '10000000000000000000000000003306'
params_list = await get_token_js(rid, did, proxy_addr=proxy_addr)
headers = {
'User-Agent': 'ios/7.830 (ios 17.0; ; iPhone 15 (A2846/A3089/A3090/A3092))',
'Referer': 'https://m.douyu.com/3125893?rid=3125893&dyshid=0-96003918aa5365bc6dcb4933000316p1&dyshci=181',
'Cookie': 'dy_did=413b835d2ae00270f0c69f6400031601; acf_did=413b835d2ae00270f0c69f6400031601; Hm_lvt_e99aee90ec1b2106afe7ec3b199020a7=1692068308,1694003758; m_did=96003918aa5365bc6dcb4933000316p1; dy_teen_mode=%7B%22uid%22%3A%22472647365%22%2C%22status%22%3A0%2C%22birthday%22%3A%22%22%2C%22password%22%3A%22%22%7D; PHPSESSID=td59qi2fu2gepngb8mlehbeme3; acf_auth=94fc9s%2FeNj%2BKlpU%2Br8tZC3Jo9sZ0wz9ClcHQ1akL2Nhb6ZyCmfjVWSlR3LFFPuePWHRAMo0dt9vPSCoezkFPOeNy4mYcdVOM1a8CbW0ZAee4ipyNB%2Bflr58; dy_auth=bec5yzM8bUFYe%2FnVAjmUAljyrsX%2FcwRW%2FyMHaoArYb5qi8FS9tWR%2B96iCzSnmAryLOjB3Qbeu%2BBD42clnI7CR9vNAo9mva5HyyL41HGsbksx1tEYFOEwxSI; wan_auth37wan=5fd69ed5b27fGM%2FGoswWwDo%2BL%2FRMtnEa4Ix9a%2FsH26qF0sR4iddKMqfnPIhgfHZUqkAk%2FA1d8TX%2B6F7SNp7l6buIxAVf3t9YxmSso8bvHY0%2Fa6RUiv8; acf_uid=472647365; acf_username=472647365; acf_nickname=%E7%94%A8%E6%88%B776576662; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F24_; acf_ct=0; acf_ltkid=25305099; acf_biz=1; acf_stk=90754f8ed18f0c24; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=1694003778'
}
if cookies:
headers['Cookie'] = cookies
data = {
'v': params_list[0],
'did': params_list[1],
'tt': params_list[2],
'sign': params_list[3], # 10分钟有效期
'ver': '22011191',
'rid': rid,
'rate': rate, # 0蓝光、3超清、2高清、-1默认
}
# app_api = 'https://m.douyu.com/hgapi/livenc/room/getStreamUrl'
app_api = f'https://www.douyu.com/lapi/live/getH5Play/{rid}'
json_str = await async_req(url=app_api, proxy_addr=proxy_addr, headers=headers, data=data)
json_data = json.loads(json_str)
return json_data
@trace_error_decorator
async def get_yy_stream_data(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Referer': 'https://www.yy.com/',
'Cookie': 'hd_newui=0.2103068903976506; hdjs_session_id=0.4929014850884579; hdjs_session_time=1694004002636; hiido_ui=0.923076230899782'
}
if cookies:
headers['Cookie'] = cookies
html_str = await async_req(url=url, proxy_addr=proxy_addr, headers=headers)
anchor_name = re.search('nick: "(.*?)",\n\\s+logo', html_str).group(1)
cid = re.search('sid : "(.*?)",\n\\s+ssid', html_str, re.DOTALL).group(1)
data = '{"head":{"seq":1701869217590,"appidstr":"0","bidstr":"121","cidstr":"' + cid + '","sidstr":"' + cid + '","uid64":0,"client_type":108,"client_ver":"5.17.0","stream_sys_ver":1,"app":"yylive_web","playersdk_ver":"5.17.0","thundersdk_ver":"0","streamsdk_ver":"5.17.0"},"client_attribute":{"client":"web","model":"web0","cpu":"","graphics_card":"","os":"chrome","osversion":"0","vsdk_version":"","app_identify":"","app_version":"","business":"","width":"1920","height":"1080","scale":"","client_type":8,"h265":0},"avp_parameter":{"version":1,"client_type":8,"service_type":0,"imsi":0,"send_time":1701869217,"line_seq":-1,"gear":4,"ssl":1,"stream_format":0}}'
data_bytes = data.encode('utf-8')
params = {
"uid": "0",
"cid": cid,
"sid": cid,
"appid": "0",
"sequence": "1701869217590",
"encode": "json"
}
url2 = f'https://stream-manager.yy.com/v3/channel/streams?{urllib.parse.urlencode(params)}'
json_str = await async_req(url=url2, data=data_bytes, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)
json_data['anchor_name'] = anchor_name
params = {
'uid': '',
'sid': cid,
'ssid': cid,
'_': int(time.time() * 1000),
}
detail_api = f'https://www.yy.com/live/detail?{urllib.parse.urlencode(params)}'
json_str2 = await async_req(detail_api, proxy_addr=proxy_addr, headers=headers)
json_data2 = json.loads(json_str2)
json_data['title'] = json_data2['data']['roomName']
return json_data
@trace_error_decorator
async def get_bilibili_room_info_h5(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> str:
headers = {
'user-agent': 'Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) '
'SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36',
'accept-language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'cookie': '',
'origin': 'https://live.bilibili.com',
'referer': 'https://live.bilibili.com/26066074',
}
if cookies:
headers['cookie'] = cookies
room_id = url.split('?')[0].rsplit('/', maxsplit=1)[1]
api = f'https://api.live.bilibili.com/xlive/web-room/v1/index/getH5InfoByRoom?room_id={room_id}'
json_str = await async_req(api, proxy_addr=proxy_addr, headers=headers)
room_info = json.loads(json_str)
title = room_info['data']['room_info']['title'] if room_info.get('data') else ''
return title
@trace_error_decorator
async def get_bilibili_room_info(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
}
if cookies:
headers['Cookie'] = cookies
try:
room_id = url.split('?')[0].rsplit('/', maxsplit=1)[1]
json_str = await async_req(f'https://api.live.bilibili.com/room/v1/Room/room_init?id={room_id}',
proxy_addr=proxy_addr, headers=headers)
room_info = json.loads(json_str)
uid = room_info['data']['uid']
live_status = True if room_info['data']['live_status'] == 1 else False
api = f'https://api.live.bilibili.com/live_user/v1/Master/info?uid={uid}'
json_str2 = await async_req(url=api, proxy_addr=proxy_addr, headers=headers)
anchor_info = json.loads(json_str2)
anchor_name = anchor_info['data']['info']['uname']
title = await get_bilibili_room_info_h5(url, proxy_addr, cookies)
return {"anchor_name": anchor_name, "live_status": live_status, "room_url": url, "title": title}
except Exception as e:
print(e)
return {"anchor_name": '', "live_status": False, "room_url": url}
@trace_error_decorator
async def get_bilibili_stream_data(url: str, qn: str = '10000', platform: str = 'web', proxy_addr: OptionalStr = None,
cookies: OptionalStr = None) -> OptionalStr:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'origin': 'https://live.bilibili.com',
'referer': 'https://live.bilibili.com/26066074',
}
if cookies:
headers['Cookie'] = cookies
room_id = url.split('?')[0].rsplit('/', maxsplit=1)[1]
params = {
'cid': room_id,
'qn': qn,
'platform': platform,
}
play_api = f'https://api.live.bilibili.com/room/v1/Room/playUrl?{urllib.parse.urlencode(params)}'
json_str = await async_req(play_api, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)
if json_data and json_data['code'] == 0:
for i in json_data['data']['durl']:
if 'd1--cn-gotcha' in i['url']:
return i['url']
return json_data['data']['durl'][-1]['url']
else:
params = {
"room_id": room_id,
"protocol": "0,1",
"format": "0,1,2",
"codec": "0,1,2",
"qn": qn,
"platform": "web",
"ptype": "8",
"dolby": "5",
"panorama": "1",
"hdr_type": "0,1"
}
# 此接口因网页上有限制, 需要配置登录后的cookie才能获取最高画质
api = f'https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo?{urllib.parse.urlencode(params)}'
json_str = await async_req(api, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)
if json_data['data']['live_status'] == 0:
print("The anchor did not start broadcasting.")
return
playurl_info = json_data['data']['playurl_info']
format_list = playurl_info['playurl']['stream'][0]['format']
stream_data_list = format_list[0]['codec']
sorted_stream_list = sorted(stream_data_list, key=itemgetter("current_qn"), reverse=True)
# qn: 30000=杜比 20000=4K 10000=原画 400=蓝光 250=超清 150=高清 80=流畅
video_quality_options = {'10000': 0, '400': 1, '250': 2, '150': 3, '80': 4}
qn_count = len(sorted_stream_list)
select_stream_index = min(video_quality_options[qn], qn_count - 1)
stream_data: dict = sorted_stream_list[select_stream_index]
base_url = stream_data['base_url']
host = stream_data['url_info'][0]['host']
extra = stream_data['url_info'][0]['extra']
m3u8_url = host + base_url + extra
return m3u8_url
@trace_error_decorator
async def get_xhs_stream_url(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'ios/7.830 (ios 17.0; ; iPhone 15 (A2846/A3089/A3090/A3092))',
'xy-common-params': 'platform=iOS&sid=session.1722166379345546829388',
'referer': 'https://app.xhs.cn/',
}
if cookies:
headers['Cookie'] = cookies
if "xhslink.com" in url:
url = await async_req(url, proxy_addr=proxy_addr, headers=headers, redirect_url=True)
host_id = get_params(url, "host_id")
user_id = re.search("/user/profile/(.*?)(?=/|\\?|$)", url)
user_id = user_id.group(1) if user_id else host_id
result = {"anchor_name": '', "is_live": False}
html_str = await async_req(url, proxy_addr=proxy_addr, headers=headers)
match_data = re.search("", html_str)
if match_data:
json_str = match_data.group(1).replace("undefined", "null")
json_data = json.loads(json_str)
if json_data.get("liveStream"):
stream_data = json_data["liveStream"]
if stream_data.get("liveStatus") == "success":
room_info = stream_data["roomData"]["roomInfo"]
title = room_info.get("roomTitle")
if title and "回放" not in title:
live_link = room_info["deeplink"]
anchor_name = get_params(live_link, "host_nickname")
flv_url = get_params(live_link, "flvUrl")
room_id = flv_url.split('live/')[1].split('.')[0]
flv_url = f"http://live-source-play.xhscdn.com/live/{room_id}.flv"
m3u8_url = flv_url.replace('.flv', '.m3u8')
result |= {
"anchor_name": anchor_name,
"is_live": True,
"title": title,
"flv_url": flv_url,
"m3u8_url": m3u8_url,
'record_url': flv_url
}
return result
profile_url = f"https://www.xiaohongshu.com/user/profile/{user_id}"
html_str = await async_req(profile_url, proxy_addr=proxy_addr, headers=headers)
anchor_name = re.search("
@(.*?) 的个人主页", html_str)
if anchor_name:
result["anchor_name"] = anchor_name.group(1)
return result
@trace_error_decorator
async def get_bigo_stream_url(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': 'https://www.bigo.tv/',
}
if cookies:
headers['Cookie'] = cookies
if 'bigo.tv' not in url:
html_str = await async_req(url, proxy_addr=proxy_addr, headers=headers)
web_url = re.search(
'',
html_str).group(1)
room_id = web_url.split('&h=')[-1]
else:
if '&h=' in url:
room_id = url.split('&h=')[-1]
else:
room_id = re.search('www.bigo.tv/cn/(\\w+)', url).group(1)
data = {'siteId': room_id} # roomId
url2 = 'https://ta.bigo.tv/official_website/studio/getInternalStudioInfo'
json_str = await async_req(url=url2, proxy_addr=proxy_addr, headers=headers, data=data)
json_data = json.loads(json_str)
anchor_name = json_data['data']['nick_name']
live_status = json_data['data']['alive']
result = {"anchor_name": anchor_name, "is_live": False}
if live_status == 1:
live_title = json_data['data']['roomTopic']
m3u8_url = json_data['data']['hls_src']
result['m3u8_url'] = m3u8_url
result['record_url'] = m3u8_url
result |= {"title": live_title, "is_live": True, "m3u8_url": m3u8_url, 'record_url': m3u8_url}
elif result['anchor_name'] == '':
html_str = await async_req(url=f'https://www.bigo.tv/cn/{room_id}', proxy_addr=proxy_addr, headers=headers)
result['anchor_name'] = re.search('欢迎来到(.*?)的直播间', html_str, re.DOTALL).group(1)
return result
@trace_error_decorator
async def get_blued_stream_url(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'ios/7.830 (ios 17.0; ; iPhone 15 (A2846/A3089/A3090/A3092))',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
}
if cookies:
headers['Cookie'] = cookies
html_str = await async_req(url=url, proxy_addr=proxy_addr, headers=headers)
json_str = re.search('decodeURIComponent\\(\"(.*?)\"\\)\\),window\\.Promise', html_str, re.DOTALL).group(1)
json_str = urllib.parse.unquote(json_str)
json_data = json.loads(json_str)
anchor_name = json_data['userInfo']['name']
live_status = json_data['userInfo']['onLive']
result = {"anchor_name": anchor_name, "is_live": False}
if live_status:
m3u8_url = json_data['liveInfo']['liveUrl']
result |= {"is_live": True, "m3u8_url": m3u8_url, 'record_url': m3u8_url}
return result
@trace_error_decorator
async def login_sooplive(username: str, password: str, proxy_addr: OptionalStr = None) -> OptionalStr:
if len(username) < 6 or len(password) < 10:
raise RuntimeError("sooplive login failed! Please enter the correct account and password for the sooplive "
"platform in the config.ini file.")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'https://play.sooplive.co.kr',
'Referer': 'https://play.sooplive.co.kr/superbsw123/277837074',
}
data = {
'szWork': 'login',
'szType': 'json',
'szUid': username,
'szPassword': password,
'isSaveId': 'true',
'isSavePw': 'true',
'isSaveJoin': 'true',
'isLoginRetain': 'Y',
}
url = 'https://login.sooplive.co.kr/app/LoginAction.php'
try:
cookie_dict = await async_req(url, proxy_addr=proxy_addr, headers=headers,
data=data, return_cookies=True, timeout=20)
cookie_str = '; '.join([f"{k}={v}" for k, v in cookie_dict.items()])
return cookie_str
except Exception as e:
print(f"An error occurred during login: {e}")
raise Exception(
"sooplive login failed, please check if the account password in the configuration file is correct."
)
@trace_error_decorator
async def get_sooplive_cdn_url(broad_no: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Origin': 'https://play.sooplive.co.kr',
'Referer': 'https://play.sooplive.co.kr/oul282/249469582',
'Content-Type': 'application/x-www-form-urlencoded',
}
if cookies:
headers['Cookie'] = cookies
params = {
'return_type': 'gcp_cdn',
'use_cors': 'false',
'cors_origin_url': 'play.sooplive.co.kr',
'broad_key': f'{broad_no}-common-master-hls',
'time': '8361.086329376785',
}
url2 = 'http://livestream-manager.sooplive.co.kr/broad_stream_assign.html?' + urllib.parse.urlencode(params)
json_str = await async_req(url=url2, proxy_addr=proxy_addr, headers=headers, abroad=True)
json_data = json.loads(json_str)
return json_data
@trace_error_decorator
async def get_sooplive_tk(url: str, rtype: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> str | tuple:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
'Origin': 'https://play.sooplive.co.kr',
'Referer': 'https://play.sooplive.co.kr/secretx/250989857',
'Content-Type': 'application/x-www-form-urlencoded',
}
if cookies:
headers['Cookie'] = cookies
split_url = url.split('/')
bj_id = split_url[3] if len(split_url) < 6 else split_url[5]
room_password = get_params(url, "pwd")
if not room_password:
room_password = ''
data = {
'bid': bj_id,
'bno': '',
'type': rtype,
'pwd': room_password,
'player_type': 'html5',
'stream_type': 'common',
'quality': 'master',
'mode': 'landing',
'from_api': '0',
'is_revive': 'false',
}
url2 = f'https://live.sooplive.co.kr/afreeca/player_live_api.php?bjid={bj_id}'
json_str = await async_req(url=url2, proxy_addr=proxy_addr, headers=headers, data=data, abroad=True)
json_data = json.loads(json_str)
if rtype == 'aid':
token = json_data["CHANNEL"]["AID"]
return token
else:
bj_name = json_data['CHANNEL']['BJNICK']
bj_id = json_data['CHANNEL']['BJID']
return f"{bj_name}-{bj_id}", json_data['CHANNEL']['BNO']
@trace_error_decorator
async def get_sooplive_stream_data(
url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None,
username: OptionalStr = None, password: OptionalStr = None
) -> dict:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Referer': 'https://m.sooplive.co.kr/',
'Content-Type': 'application/x-www-form-urlencoded',
}
if cookies:
headers['Cookie'] = cookies
split_url = url.split('/')
bj_id = split_url[3] if len(split_url) < 6 else split_url[5]
data = {
'bj_id': bj_id,
'broad_no': '',
'agent': 'web',
'confirm_adult': 'true',
'player_type': 'webm',
'mode': 'live',
}
url2 = 'http://api.m.sooplive.co.kr/broad/a/watch'
json_str = await async_req(url=url2, proxy_addr=proxy_addr, headers=headers, data=data, abroad=True)
json_data = json.loads(json_str)
if 'user_nick' in json_data['data']:
anchor_name = json_data['data']['user_nick']
if "bj_id" in json_data['data']:
anchor_name = f"{anchor_name}-{json_data['data']['bj_id']}"
else:
anchor_name = ''
result = {"anchor_name": anchor_name or '' ,"is_live": False}
async def get_url_list(m3u8: str) -> List[str]:
resp = await async_req(url=m3u8, proxy_addr=proxy_addr, headers=headers, abroad=True)
play_url_list = []
url_prefix = m3u8.rsplit('/', maxsplit=1)[0] + '/'
for i in resp.split('\n'):
if i.startswith('auth_playlist'):
play_url_list.append(url_prefix + i.strip())
bandwidth_pattern = re.compile(r'BANDWIDTH=(\d+)')
bandwidth_list = bandwidth_pattern.findall(resp)
url_to_bandwidth = {purl: int(bandwidth) for bandwidth, purl in zip(bandwidth_list, play_url_list)}
play_url_list = sorted(play_url_list, key=lambda purl: url_to_bandwidth[purl], reverse=True)
return play_url_list
if not anchor_name:
async def handle_login() -> OptionalStr:
cookie = await login_sooplive(username, password, proxy_addr=proxy_addr)
if 'AuthTicket=' in cookie:
print("sooplive platform login successful! Starting to fetch live streaming data...")
return cookie
async def fetch_data(cookie, _result) -> dict:
aid_token = await get_sooplive_tk(url, rtype='aid', proxy_addr=proxy_addr, cookies=cookie)
_anchor_name, _broad_no = await get_sooplive_tk(url, rtype='info', proxy_addr=proxy_addr, cookies=cookie)
_view_url_data = await get_sooplive_cdn_url(_broad_no, proxy_addr=proxy_addr)
_view_url = _view_url_data['view_url']
_m3u8_url = _view_url + '?aid=' + aid_token
_result |= {
"anchor_name": _anchor_name,
"is_live": True,
"m3u8_url": _m3u8_url,
'play_url_list': await get_url_list(_m3u8_url),
'new_cookies': cookie
}
return _result
if json_data['data']['code'] == -3001:
print("sooplive live stream failed to retrieve, the live stream just ended.")
return result
elif json_data['data']['code'] == -3002:
print("sooplive live stream retrieval failed, the live needs 19+, you are not logged in.")
print("Attempting to log in to the sooplive live streaming platform with your account and password, "
"please ensure it is configured.")
new_cookie = await handle_login()
if new_cookie and len(new_cookie) > 0:
return await fetch_data(new_cookie, result)
raise RuntimeError("sooplive login failed, please check if the account and password are correct")
elif json_data['data']['code'] == -3004:
if cookies and len(cookies) > 0:
return await fetch_data(cookies, result)
else:
raise RuntimeError("sooplive login failed, please check if the account and password are correct")
elif json_data['data']['code'] == -6001:
print("error message:Please check if the input sooplive live room address "
"is correct.")
return result
if json_data['result'] == 1 and anchor_name:
broad_no = json_data['data']['broad_no']
hls_authentication_key = json_data['data']['hls_authentication_key']
view_url_data = await get_sooplive_cdn_url(broad_no, proxy_addr=proxy_addr)
view_url = view_url_data['view_url']
m3u8_url = view_url + '?aid=' + hls_authentication_key
result |= {'is_live': True, 'm3u8_url': m3u8_url, 'play_url_list': await get_url_list(m3u8_url)}
result['new_cookies'] = None
return result
@trace_error_decorator
async def get_netease_stream_data(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
headers = {
'accept': 'application/json, text/plain, */*',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'referer': 'https://cc.163.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0',
}
if cookies:
headers['Cookie'] = cookies
url = url + '/' if url[-1] != '/' else url
html_str = await async_req(url=url, proxy_addr=proxy_addr, headers=headers)
json_str = re.search('