This commit is contained in:
eric
2025-09-27 10:06:54 +08:00
parent 5feee23ef0
commit 9f64fed2ec
40 changed files with 12235 additions and 57 deletions

14
src/__init__.py Normal file
View File

@@ -0,0 +1,14 @@
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'
execute_dir = os.path.split(os.path.realpath(sys.argv[0]))[0]
node_execute_dir = Path(execute_dir) / 'node'
current_env_path = os.environ.get('PATH')
os.environ['PATH'] = str(node_execute_dir) + os.pathsep + current_env_path
check_node()

View File

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
import httpx
from typing import Dict, Any
from .. import utils
OptionalStr = str | None
OptionalDict = Dict[str, Any] | None
async def async_req(
url: str,
proxy_addr: OptionalStr = None,
headers: OptionalDict = None,
data: dict | bytes | None = None,
json_data: dict | list | None = None,
timeout: int = 20,
redirect_url: bool = False,
return_cookies: bool = False,
include_cookies: bool = False,
abroad: bool = False,
content_conding: str = 'utf-8',
verify: bool = False,
http2: bool = True
) -> OptionalDict | OptionalStr | tuple:
if headers is None:
headers = {}
try:
proxy_addr = utils.handle_proxy_addr(proxy_addr)
if data or json_data:
async with httpx.AsyncClient(proxy=proxy_addr, timeout=timeout, verify=verify, http2=http2) as client:
response = await client.post(url, data=data, json=json_data, headers=headers)
else:
async with httpx.AsyncClient(proxy=proxy_addr, timeout=timeout, verify=verify, http2=http2) as client:
response = await client.get(url, headers=headers, follow_redirects=True)
if redirect_url:
return str(response.url)
elif return_cookies:
cookies_dict = {name: value for name, value in response.cookies.items()}
return (response.text, cookies_dict) if include_cookies else cookies_dict
else:
resp_str = response.text
except Exception as e:
resp_str = str(e)
return resp_str
async def get_response_status(url: str, proxy_addr: OptionalStr = None, headers: OptionalDict = None,
timeout: int = 10, abroad: bool = False, verify: bool = False, http2=False) -> bool:
try:
proxy_addr = utils.handle_proxy_addr(proxy_addr)
async with httpx.AsyncClient(proxy=proxy_addr, timeout=timeout, verify=verify) as client:
response = await client.head(url, headers=headers, follow_redirects=True)
return response.status_code == 200
except Exception as e:
print(e)
return False

View File

@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
import gzip
import urllib.parse
import urllib.error
import requests
import ssl
import json
import urllib.request
no_proxy_handler = urllib.request.ProxyHandler({})
opener = urllib.request.build_opener(no_proxy_handler)
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 sync_req(
url: str,
proxy_addr: OptionalStr = None,
headers: OptionalDict = None,
data: dict | bytes | None = None,
json_data: dict | list | None = None,
timeout: int = 20,
redirect_url: bool = False,
abroad: bool = False,
content_conding: str = 'utf-8'
) -> str:
if headers is None:
headers = {}
try:
if proxy_addr:
proxies = {
'http': proxy_addr,
'https': proxy_addr
}
if data or json_data:
response = requests.post(
url, data=data, json=json_data, headers=headers, proxies=proxies, timeout=timeout
)
else:
response = requests.get(url, headers=headers, proxies=proxies, timeout=timeout)
if redirect_url:
return response.url
resp_str = response.text
else:
if data and not isinstance(data, bytes):
data = urllib.parse.urlencode(data).encode(content_conding)
if json_data and isinstance(json_data, (dict, list)):
data = json.dumps(json_data).encode(content_conding)
req = urllib.request.Request(url, data=data, headers=headers)
try:
if abroad:
response = urllib.request.urlopen(req, timeout=timeout)
else:
response = opener.open(req, timeout=timeout)
if redirect_url:
return response.url
content_encoding = response.info().get('Content-Encoding')
try:
if content_encoding == 'gzip':
with gzip.open(response, 'rt', encoding=content_conding) as gzipped:
resp_str = gzipped.read()
else:
resp_str = response.read().decode(content_conding)
finally:
response.close()
except urllib.error.HTTPError as e:
if e.code == 400:
resp_str = e.read().decode(content_conding)
else:
raise
except urllib.error.URLError as e:
print(f"URL Error: {e}")
raise
except Exception as e:
print(f"An error occurred: {e}")
raise
except Exception as e:
resp_str = str(e)
return resp_str

220
src/initializer.py Normal file
View File

@@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-
"""
Author: Hmily
GitHub:https://github.com/ihmily
Copyright (c) 2024 by Hmily, All Rights Reserved.
"""
import os
import subprocess
import sys
import platform
import zipfile
from pathlib import Path
import requests
import re
import distro
from tqdm import tqdm
from .logger import logger
current_platform = platform.system()
execute_dir = os.path.split(os.path.realpath(sys.argv[0]))[0]
current_env_path = os.environ.get('PATH')
def unzip_file(zip_path: str | Path, extract_to: str | Path, delete: bool = True) -> None:
if not os.path.exists(extract_to):
os.makedirs(extract_to)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(extract_to)
if delete and os.path.exists(zip_path):
os.remove(zip_path)
def install_nodejs_windows():
try:
logger.warning("Node.js is not installed.")
logger.debug("Installing the stable version of Node.js for Windows...")
response = requests.get('https://nodejs.cn/download/')
if response.status_code == 200:
match = re.search('https://npmmirror.com/mirrors/node/(v.*?)/node-(v.*?)-x64.msi',
response.text)
if match:
version = match.group(1)
system_bit = 'x64' if '32' not in platform.machine() else 'x86'
url = f'https://npmmirror.com/mirrors/node/{version}/node-{version}-win-{system_bit}.zip'
else:
logger.error("Failed to retrieve the download URL for the latest version of Node.js...")
return
full_file_name = url.rsplit('/', maxsplit=1)[-1]
zip_file_path = Path(execute_dir) / full_file_name
if Path(zip_file_path).exists():
logger.debug("Node.js installation file already exists, start install...")
else:
response = requests.get(url, stream=True)
total_size = int(response.headers.get('Content-Length', 0))
block_size = 1024
with tqdm(total=total_size, unit="B", unit_scale=True,
ncols=100, desc=f'Downloading Node.js ({version})') as t:
with open(zip_file_path, 'wb') as f:
for data in response.iter_content(block_size):
t.update(len(data))
f.write(data)
unzip_file(zip_file_path, execute_dir)
extract_dir_path = str(zip_file_path).rsplit('.', maxsplit=1)[0]
f_path, f_name = os.path.splitext(zip_file_path)
new_extract_dir_path = Path(f_path).parent / 'node'
if Path(extract_dir_path).exists() and not Path(new_extract_dir_path).exists():
os.rename(extract_dir_path, new_extract_dir_path)
os.environ['PATH'] = execute_dir + '/node' + os.pathsep + current_env_path
result = subprocess.run(["node", "-v"], capture_output=True)
if result.returncode == 0:
logger.debug('Node.js installation was successful. Restart for changes to take effect')
else:
logger.debug('Node.js installation failed')
return True
else:
logger.error("Failed to retrieve the Node.js version page")
except Exception as e:
logger.error(f"type: {type(e).__name__}, Node.js installation failed {e}")
def install_nodejs_centos():
try:
logger.warning("Node.js is not installed.")
logger.debug("Installing the latest version of Node.js for CentOS...")
result = subprocess.run('curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/nodesource/rpm/setup_lts.x | '
'bash -', shell=True, capture_output=True)
if result.returncode != 0:
logger.error("Failed to run NodeSource installation script")
return
result = subprocess.run(['yum', 'install', '-y', 'epel-release'], capture_output=True)
if result.returncode != 0:
logger.error("Failed to install EPEL repository")
return
result = subprocess.run(['yum', 'install', '-y', 'nodejs'], capture_output=True)
if result.returncode == 0:
logger.debug('Node.js installation was successful. Restart for changes to take effect.')
return True
else:
logger.error("Node.js installation failed")
except Exception as e:
logger.error(f"type: {type(e).__name__}, Node.js installation failed {e}")
def install_nodejs_ubuntu():
try:
logger.warning("Node.js is not installed.")
logger.debug("Installing the latest version of Node.js for Ubuntu...")
install_script = 'curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -'
result = subprocess.run(install_script, shell=True, capture_output=True)
if result.returncode != 0:
logger.error("Failed to run NodeSource installation script")
return
install_command = ['apt', 'install', '-y', 'nodejs']
result = subprocess.run(install_command, capture_output=True)
if result.returncode == 0:
logger.debug('Node.js installation was successful. Restart for changes to take effect.')
return True
else:
logger.error("Node.js installation failed")
except Exception as e:
logger.error(f"type: {type(e).__name__}, Node.js installation failed, {e}")
def install_nodejs_mac():
logger.warning("Node.js is not installed.")
logger.debug("Installing the latest version of Node.js for macOS...")
try:
result = subprocess.run(["brew", "install", "node"], capture_output=True)
if result.returncode == 0:
logger.debug('Node.js installation was successful. Restart for changes to take effect.')
return True
else:
logger.error("Node.js installation failed")
except subprocess.CalledProcessError as e:
logger.error(f"Failed to install Node.js using Homebrew. {e}")
logger.error("Please install Node.js manually or check your Homebrew installation.")
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
def get_package_manager():
dist_id = distro.id()
if dist_id in ["centos", "fedora", "rhel", "amzn", "oracle", "scientific", "opencloudos", "alinux"]:
return "RHS"
else:
return "DBS"
def install_nodejs() -> bool:
if current_platform == "Windows":
return install_nodejs_windows()
elif current_platform == "Linux":
os_type = get_package_manager()
if os_type == "RHS":
return install_nodejs_centos()
else:
return install_nodejs_ubuntu()
elif current_platform == "Darwin":
return install_nodejs_mac()
else:
logger.debug(f"Node.js auto installation is not supported on this platform: {current_platform}. "
f"Please install Node.js manually.")
return False
def ensure_nodejs_installed(func):
def wrapper(*args, **kwargs):
try:
result = subprocess.run(['node', '-v'], capture_output=True)
version = result.stdout.strip()
if result.returncode == 0 and version:
return func(*args, **kwargs)
except FileNotFoundError:
pass
return False
def wrapped_func(*args, **kwargs):
if sys.version_info >= (3, 7):
res = wrapper(*args, **kwargs)
else:
res = wrapper(*args, **kwargs)
if not res:
install_nodejs()
res = wrapper(*args, **kwargs)
if not res:
raise RuntimeError("Node.js is not installed.")
return func(*args, **kwargs)
return wrapped_func
def check_nodejs_installed() -> bool:
try:
result = subprocess.run(['node', '-v'], capture_output=True)
version = result.stdout.strip()
if result.returncode == 0 and version:
return True
except FileNotFoundError:
pass
return False
def check_node() -> bool:
if not check_nodejs_installed():
return install_nodejs()

1
src/javascript/crypto-js.min.js vendored Normal file

File diff suppressed because one or more lines are too long

539
src/javascript/haixiu.js Normal file
View File

@@ -0,0 +1,539 @@
var closeGeetest = !1, _a123 = "haija1c7", _b2x = "xiuhc2a6", _c3y = "anchc3a5", _dx34 = "famic7a2", _hf_constants1 = "sowh1e", _hf_constants2 = "1000ha", _hf_constants3 = "butr12", _hf_constants4 = "2000h5", _gf_constants1 = "lehaaj", _gf_constants2 = "1000ax", _gf_constants3 = "lehaData"
let CryptoJS = null;
function EnmoliParamter() {
this._a123 = eval("_hf_constants1"),
this._b2x = eval("_hf_constants2"),
this._c3y = eval("_hf_constants3"),
this._dx34 = eval("_hf_constants4"),
this.getA123 = function() {
return this._a123
}
,
this.getB2X = function() {
return this._b2x
}
,
this.getC3Y = function() {
return this._c3y
}
,
this.getDX34 = function() {
return this._dx34
}
}
EnmoliParamter.prototype = {
aa: function(e, t) {
if (e === t)
return e;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e;
if (e.arity === t.arity && e.string === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ac: function(e, t) {
if (e === t)
return e;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e;
if (e.id === t.id && (e = CryptoJS.MD5(e) + ""),
e.arity1 === t.arity && e.string2 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ad: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e;
if (e.arity2 === t.arity && e.string3 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ae: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity3 === t.arity && e.string4 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
af: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity4 === t.arity && e.string5 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ah: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e;
if (e.arity6 === t.arity && e.string9 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ai: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e;
if (e.arity2 === t.arity5 && e.string === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
aj: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity44 === t.arity42 && e.string === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ak: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity21 === t.arity322 && e.string === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
ax: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity22 === t.arity32 && e.string === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
az: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity42 === t.arity57 && e.string2 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return e
},
are_similar: function(e, t) {
if (e === t)
return !0;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return !0;
return !0
}
return e
}
if (Array.isArray(t))
return e;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity === t.arity && e.string === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third);
case "function":
case "regexp":
return e;
default:
return !0
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e.second.string === t.second.string && "(string)" === t.second.id;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return e.second.string === t.second.string && "(string)" === e.second.id
}
return !1
},
ayz: function(e, t) {
if (e === t)
return e;
if (Array.isArray(e)) {
if (Array.isArray(t) && e.length === t.length) {
var i;
for (i = 0; i < e.length; i += 1)
if (!this.are_similar(e[i], t[i]))
return e;
return e
}
return e
}
if (Array.isArray(t))
return t;
if ("(number)" === e.id && "(number)" === t.id)
return e.number === t.number;
if (e.arity42 === t.arity57 && e.string2 === t.string)
switch (e.arity) {
case "prefix":
case "suffix":
case "infix":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second);
case "ternary":
return this.are_similar(e.first, t.first) && this.are_similar(e.second, t.second) && this.are_similar(e.third, t.third)
}
else {
if ("." === e.id && "[" === t.id && "infix" === t.arity)
return e;
if ("[" === e.id && "infix" === e.arity && "." === t.id)
return t
}
return this.getA123().substring(4) + this.getB2X().substring(4) + this.getC3Y().substring(4) + this.getDX34().substring(4)
}
}
function EnmoliSubmiter() {}
EnmoliSubmiter.prototype = {
bsq: function(e) {
var t = this.pf(e)
, i = this.as(t);
return this.brm(i)
},
pf: function(e) {
var t = {};
for (var i in e)
"" !== e[i] && (t[i] = e[i]);
return t
},
as: function(e) {
for (var t = {}, i = Object.keys(e).sort(), o = 0; o < i.length; o++) {
var n = i[o];
t[n] = e[n]
}
return t
},
brm: function(e) {
var t = this.cls(e)
, i = new EnmoliParamter;
return this.pt(t, i.ayz(t, "showselfAnchorVisitorParameters"))
},
cls: function(e) {
var t = "";
for (var i in e)
t = t + i + "=" + e[i] + "&";
return t = t.substring(0, t.length - 1)
},
pt: function(e, t) {
var i = new EnmoliParamter;
return e += t,
i.az(i.ax(i.ak(i.aj(i.ai(i.ah(i.af(i.ae(i.ad(i.ac(i.aa(e, e + "01" + t), e + "escape" + t), e + "same"), e + "visitor"), "anchor"), e + "person"), e + "ax" + t), "ae" + t), e + "ax" + t), e + "inspect" + t), "af" + t)
},
bnu: function(e, t) {
for (var i = e.split("&"), o = 0; o < i.length; o++) {
var n = i[o].split("=");
2 == n.length && (t[n[0]] = encodeURIComponent($.trim(n[1])).toString())
}
},
bn: function(e, t) {
for (var i in e)
"object" == typeof e[i] ? t[i] = encodeURIComponent($.trim(JSON.stringify(e[i]))).toString() : t[i] = encodeURIComponent($.trim(e[i])).toString()
}
}
var enmoliSubmiter = new EnmoliSubmiter();
function sign(options, cryptoJSPath){
CryptoJS = require(cryptoJSPath);
return enmoliSubmiter.bsq(options);
}
module.exports = {
sign
};
// const options = {
// "accessToken": "pLXSC%252FXJ0asc1I21tVL5FYZhNJn2Zg6d7m94umCnpgL%252BuVm31GQvyw%253D%253D",
// "tku": "3000006",
// "c": "10138100100000",
// "_st1": "1728621076958"
// }
// const cryptoJSPath = './crypto-js.min.js'
// console.log(sign(options, cryptoJSPath))

33
src/javascript/laixiu.js Normal file
View File

@@ -0,0 +1,33 @@
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function calculateSign() {
const a = new Date().getTime();
const s = generateUUID().replace(/-/g, "");
const u = 'kk792f28d6ff1f34ec702c08626d454b39pro';
const input = "web" + s + a + u;
const hash = CryptoJS.MD5(input).toString();
return {
timestamp: a,
imei: s,
requestId: hash,
inputString: input
};
}
function sign(cryptoJSPath) {
CryptoJS = require(cryptoJSPath);
return calculateSign();
}
module.exports = {
sign
};

425
src/javascript/liveme.js Normal file
View File

@@ -0,0 +1,425 @@
/**
* @author Hmily
* @createTime 2024-10-10
*/
const id = 1;
const r = `${new Date().getTime()}${id}`
const Am = "LM6000101139961122666757";
const rl = "undefined"
function createRandom(length = 32) {
let result = "";
const characters = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
for (let i = 0; i < length; ++i) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters.charAt(randomIndex);
}
return result;
}
function createSignature(input = "4l4m5") {
let signature = "";
let number = 0;
for (let i = 0; i < input.length; ++i) {
const charCode = input.charCodeAt(i);
if (charCode >= 48 && charCode <= 57) {
number = number * 10 + (charCode - 48);
} else {
if (number !== 0) {
signature += createRandom(number);
number = 0;
}
signature += String.fromCharCode(charCode);
}
}
if (number !== 0) {
signature += createRandom(number);
}
return signature;
}
function oC(e) {
return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e
}
var Tm = {
exports: {}
}
, Sm = {
exports: {}
};
(function() {
var e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
, t = {
rotl: function(n, r) {
return n << r | n >>> 32 - r
},
rotr: function(n, r) {
return n << 32 - r | n >>> r
},
endian: function(n) {
if (n.constructor == Number)
return t.rotl(n, 8) & 16711935 | t.rotl(n, 24) & 4278255360;
for (var r = 0; r < n.length; r++)
n[r] = t.endian(n[r]);
return n
},
randomBytes: function(n) {
for (var r = []; n > 0; n--)
r.push(Math.floor(Math.random() * 256));
return r
},
bytesToWords: function(n) {
for (var r = [], s = 0, o = 0; s < n.length; s++,
o += 8)
r[o >>> 5] |= n[s] << 24 - o % 32;
return r
},
wordsToBytes: function(n) {
for (var r = [], s = 0; s < n.length * 32; s += 8)
r.push(n[s >>> 5] >>> 24 - s % 32 & 255);
return r
},
bytesToHex: function(n) {
for (var r = [], s = 0; s < n.length; s++)
r.push((n[s] >>> 4).toString(16)),
r.push((n[s] & 15).toString(16));
return r.join("")
},
hexToBytes: function(n) {
for (var r = [], s = 0; s < n.length; s += 2)
r.push(parseInt(n.substr(s, 2), 16));
return r
},
bytesToBase64: function(n) {
for (var r = [], s = 0; s < n.length; s += 3)
for (var o = n[s] << 16 | n[s + 1] << 8 | n[s + 2], i = 0; i < 4; i++)
s * 8 + i * 6 <= n.length * 8 ? r.push(e.charAt(o >>> 6 * (3 - i) & 63)) : r.push("=");
return r.join("")
},
base64ToBytes: function(n) {
n = n.replace(/[^A-Z0-9+\/]/ig, "");
for (var r = [], s = 0, o = 0; s < n.length; o = ++s % 4)
o != 0 && r.push((e.indexOf(n.charAt(s - 1)) & Math.pow(2, -2 * o + 8) - 1) << o * 2 | e.indexOf(n.charAt(s)) >>> 6 - o * 2);
return r
}
};
Sm.exports = t
}
)();
var iC = Sm.exports
, nl = {
utf8: {
stringToBytes: function(e) {
return nl.bin.stringToBytes(unescape(encodeURIComponent(e)))
},
bytesToString: function(e) {
return decodeURIComponent(escape(nl.bin.bytesToString(e)))
}
},
bin: {
stringToBytes: function(e) {
for (var t = [], n = 0; n < e.length; n++)
t.push(e.charCodeAt(n) & 255);
return t
},
bytesToString: function(e) {
for (var t = [], n = 0; n < e.length; n++)
t.push(String.fromCharCode(e[n]));
return t.join("")
}
}
}, sd = nl;
var aC = function(e) {
return e != null && (Cm(e) || lC(e) || !!e._isBuffer)
};
function Cm(e) {
return !!e.constructor && typeof e.constructor.isBuffer == "function" && e.constructor.isBuffer(e)
}
function lC(e) {
return typeof e.readFloatLE == "function" && typeof e.slice == "function" && Cm(e.slice(0, 0))
}
(function() {
var e = iC
, t = sd.utf8
, n = aC
, r = sd.bin
, s = function(o, i) {
o.constructor == String ? i && i.encoding === "binary" ? o = r.stringToBytes(o) : o = t.stringToBytes(o) : n(o) ? o = Array.prototype.slice.call(o, 0) : !Array.isArray(o) && o.constructor !== Uint8Array && (o = o.toString());
for (var a = e.bytesToWords(o), l = o.length * 8, c = 1732584193, u = -271733879, f = -1732584194, d = 271733878, m = 0; m < a.length; m++)
a[m] = (a[m] << 8 | a[m] >>> 24) & 16711935 | (a[m] << 24 | a[m] >>> 8) & 4278255360;
a[l >>> 5] |= 128 << l % 32,
a[(l + 64 >>> 9 << 4) + 14] = l;
for (var v = s._ff, w = s._gg, R = s._hh, y = s._ii, m = 0; m < a.length; m += 16) {
var b = c
, _ = u
, g = f
, C = d;
c = v(c, u, f, d, a[m + 0], 7, -680876936),
d = v(d, c, u, f, a[m + 1], 12, -389564586),
f = v(f, d, c, u, a[m + 2], 17, 606105819),
u = v(u, f, d, c, a[m + 3], 22, -1044525330),
c = v(c, u, f, d, a[m + 4], 7, -176418897),
d = v(d, c, u, f, a[m + 5], 12, 1200080426),
f = v(f, d, c, u, a[m + 6], 17, -1473231341),
u = v(u, f, d, c, a[m + 7], 22, -45705983),
c = v(c, u, f, d, a[m + 8], 7, 1770035416),
d = v(d, c, u, f, a[m + 9], 12, -1958414417),
f = v(f, d, c, u, a[m + 10], 17, -42063),
u = v(u, f, d, c, a[m + 11], 22, -1990404162),
c = v(c, u, f, d, a[m + 12], 7, 1804603682),
d = v(d, c, u, f, a[m + 13], 12, -40341101),
f = v(f, d, c, u, a[m + 14], 17, -1502002290),
u = v(u, f, d, c, a[m + 15], 22, 1236535329),
c = w(c, u, f, d, a[m + 1], 5, -165796510),
d = w(d, c, u, f, a[m + 6], 9, -1069501632),
f = w(f, d, c, u, a[m + 11], 14, 643717713),
u = w(u, f, d, c, a[m + 0], 20, -373897302),
c = w(c, u, f, d, a[m + 5], 5, -701558691),
d = w(d, c, u, f, a[m + 10], 9, 38016083),
f = w(f, d, c, u, a[m + 15], 14, -660478335),
u = w(u, f, d, c, a[m + 4], 20, -405537848),
c = w(c, u, f, d, a[m + 9], 5, 568446438),
d = w(d, c, u, f, a[m + 14], 9, -1019803690),
f = w(f, d, c, u, a[m + 3], 14, -187363961),
u = w(u, f, d, c, a[m + 8], 20, 1163531501),
c = w(c, u, f, d, a[m + 13], 5, -1444681467),
d = w(d, c, u, f, a[m + 2], 9, -51403784),
f = w(f, d, c, u, a[m + 7], 14, 1735328473),
u = w(u, f, d, c, a[m + 12], 20, -1926607734),
c = R(c, u, f, d, a[m + 5], 4, -378558),
d = R(d, c, u, f, a[m + 8], 11, -2022574463),
f = R(f, d, c, u, a[m + 11], 16, 1839030562),
u = R(u, f, d, c, a[m + 14], 23, -35309556),
c = R(c, u, f, d, a[m + 1], 4, -1530992060),
d = R(d, c, u, f, a[m + 4], 11, 1272893353),
f = R(f, d, c, u, a[m + 7], 16, -155497632),
u = R(u, f, d, c, a[m + 10], 23, -1094730640),
c = R(c, u, f, d, a[m + 13], 4, 681279174),
d = R(d, c, u, f, a[m + 0], 11, -358537222),
f = R(f, d, c, u, a[m + 3], 16, -722521979),
u = R(u, f, d, c, a[m + 6], 23, 76029189),
c = R(c, u, f, d, a[m + 9], 4, -640364487),
d = R(d, c, u, f, a[m + 12], 11, -421815835),
f = R(f, d, c, u, a[m + 15], 16, 530742520),
u = R(u, f, d, c, a[m + 2], 23, -995338651),
c = y(c, u, f, d, a[m + 0], 6, -198630844),
d = y(d, c, u, f, a[m + 7], 10, 1126891415),
f = y(f, d, c, u, a[m + 14], 15, -1416354905),
u = y(u, f, d, c, a[m + 5], 21, -57434055),
c = y(c, u, f, d, a[m + 12], 6, 1700485571),
d = y(d, c, u, f, a[m + 3], 10, -1894986606),
f = y(f, d, c, u, a[m + 10], 15, -1051523),
u = y(u, f, d, c, a[m + 1], 21, -2054922799),
c = y(c, u, f, d, a[m + 8], 6, 1873313359),
d = y(d, c, u, f, a[m + 15], 10, -30611744),
f = y(f, d, c, u, a[m + 6], 15, -1560198380),
u = y(u, f, d, c, a[m + 13], 21, 1309151649),
c = y(c, u, f, d, a[m + 4], 6, -145523070),
d = y(d, c, u, f, a[m + 11], 10, -1120210379),
f = y(f, d, c, u, a[m + 2], 15, 718787259),
u = y(u, f, d, c, a[m + 9], 21, -343485551),
c = c + b >>> 0,
u = u + _ >>> 0,
f = f + g >>> 0,
d = d + C >>> 0
}
return e.endian([c, u, f, d])
};
s._ff = function(o, i, a, l, c, u, f) {
var d = o + (i & a | ~i & l) + (c >>> 0) + f;
return (d << u | d >>> 32 - u) + i
}
,
s._gg = function(o, i, a, l, c, u, f) {
var d = o + (i & l | a & ~l) + (c >>> 0) + f;
return (d << u | d >>> 32 - u) + i
}
,
s._hh = function(o, i, a, l, c, u, f) {
var d = o + (i ^ a ^ l) + (c >>> 0) + f;
return (d << u | d >>> 32 - u) + i
}
,
s._ii = function(o, i, a, l, c, u, f) {
var d = o + (a ^ (i | ~l)) + (c >>> 0) + f;
return (d << u | d >>> 32 - u) + i
}
,
s._blocksize = 16,
s._digestsize = 16,
Tm.exports = function(o, i) {
if (o == null)
throw new Error("Illegal argument " + o);
var a = e.wordsToBytes(s(o, i));
return i && i.asBytes ? a : i && i.asString ? r.bytesToString(a) : e.bytesToHex(a)
}
}
)();
var cC = Tm.exports;
var t = {
utf8: {
stringToBytes: function(e) {
return nl.bin.stringToBytes(unescape(encodeURIComponent(e)))
},
bytesToString: function(e) {
return decodeURIComponent(escape(nl.bin.bytesToString(e)))
}
},
bin: {
stringToBytes: function(e) {
for (var t = [], n = 0; n < e.length; n++)
t.push(e.charCodeAt(n) & 255);
return t
},
bytesToString: function(e) {
for (var t = [], n = 0; n < e.length; n++)
t.push(String.fromCharCode(e[n]));
return t.join("")
}
}
};
const hC = (e, t, n=!1) => {
if (t.params) {
const o = {};
Object.keys(t.params).forEach(i => {
t.params[i] !== void 0 && t.params[i] !== null && (o[i] = t.params[i])
}
),
t.params = o
}
let r = {};
const s = t.method.toLowerCase();
if (s === "get")
t.params = Object.assign({}, e, t.params || {});
else if (s === "post")
if (typeof t.data == "string") {
let o;
t.data.split("&").forEach(i => {
o = i.split("="),
r[o[0]] = o[1]
}
),
r = Object.assign({}, e, r),
t.data = Object.keys(r).map(i => `${i}=${r[i]}`).join("&")
} else
r = Object.assign(r, e, t.data || {}),
t.data = r;
return n ? t : (r = Object.assign({}, t.params, r),
r)
}
const Rm = oC(cC);
const s = Rm(r);
pC = e => {
let t = Object.keys(e).sort().map(n => {
function r(s) {
return Array.isArray(s) ? s.join(",") : typeof s === "object" ? JSON.stringify(s) : s
}
return n + r(e[n])
}
).join("");
return t += Am + e.lm_s_ts + rl,
Rm(t)
}
// final encryption function
let CryptoJS = null;
lm_s_key = atob('ZGQ0NmRiYjQ0MmI2ZTRiYTgxN2Q2MzQ3ZDJkZGY0OTM=');
function requestSign(signParams, cryptoJSPath) {
let sKey = Object.keys(signParams).sort().map(key => {
function getValue(val) {
if (Array.isArray(val)) {
return val.join(',');
}
if (typeof val === 'object') {
return JSON.stringify(val);
}
return val;
}
return key + getValue(signParams[key]);
}).join('');
sKey += signParams.lm_s_id + signParams.lm_s_ts + lm_s_key;
console.log(`sKey: ${sKey}`);
CryptoJS = require(cryptoJSPath);
return CryptoJS.MD5(sKey).toString();
}
function sign(videoid, cryptoJSPath, platform='web'){
const vali = createSignature();
const data_e = {
lm_s_id: Am,
lm_s_ts: r,
lm_s_str: s,
lm_s_ver: 1,
h5: 1
};
/* data_e example value
const data_e = {
lm_s_id: Am,
lm_s_ts: "17284909009151",
lm_s_str: "88f9777231dc2d6ac462a1d7ebf5f54e",
lm_s_ver: 1,
h5: 1
};
*/
console.log("data_e:",data_e);
data_i = {
...data_e,
_time: new Date().valueOf(),
thirdchannel: 6,
videoid: videoid,
area: 'zh',
vali: vali
}
console.log("data_i:",data_i);
// fake lm_s_sign param value
let lm_s_sign = pC(data_i);
console.log(`fake lm_s_sign: ${lm_s_sign}`);
//finnal request params
/*
signParams = {
"alias": "liveme",
"tongdun_black_box": "iWPU21728483558afruvSVo6x0",
"os": "android",
"lm_s_id": "LM6000101139961122666757",
"lm_s_ts": "17284909009151",
"lm_s_str": "88f9777231dc2d6ac462a1d7ebf5f54e",
"lm_s_ver": 1,
"h5": 1,
"_time": 1728490664651,
"thirdchannel": 6,
"videoid": "17284844223282059697",
"area": "zh",
"vali": "zH8SlBwnCm4AZWp"
}#
//result: 4eaf71a1ec19b49b7267e4d16e007105
*/
signParams = {
"alias": "liveme",
"tongdun_black_box": "",
"os": platform,
...data_i
}
console.log("signParams: ", signParams);
lm_s_sign = requestSign(signParams, cryptoJSPath);
console.log(`\x1b[32mfinal lm_s_sign: \x1b[0m${lm_s_sign}\n`);
data = {
...signParams,
lm_s_sign
}
return data;
}
module.exports = {
sign
};

143
src/javascript/migu.js Normal file
View File

@@ -0,0 +1,143 @@
/**
* Function to get the ddCalcu parameter value
* @param {string} inputUrl - The original URL before encryption
* @returns {Promise<string>} - Returns the calculated ddCalcu value
*/
async function getDdCalcu(inputUrl) {
let wasmInstance = null;
let memory_p = null; // Uint8Array view
let memory_h = null; // Uint32Array view
// Fixed parameter
const f = 'PBTxuWiTEbUPPFcpyxs0ww==';
// Utility function: Convert string to UTF-8 in memory
function stringToUTF8(string, offset) {
const encoder = new TextEncoder();
const encoded = encoder.encode(string);
for (let i = 0; i < encoded.length; i++) {
memory_p[offset + i] = encoded[i];
}
memory_p[offset + encoded.length] = 0; // Null-terminate
}
// Utility function: Read UTF-8 string from memory address
function UTF8ToString(offset) {
let s = '';
let i = 0;
while (memory_p[offset + i]) {
s += String.fromCharCode(memory_p[offset + i]);
i++;
}
return s;
}
// WASM import function stubs
function a(e, t, r, n) {
let s = 0;
for (let i = 0; i < r; i++) {
const d = memory_h[t + 4 >> 2];
t += 8;
s += d;
}
memory_h[n >> 2] = s;
return 0;
}
function b() {}
function c() {}
// Step 1: Retrieve playerVersion
const settingsResp = await fetch('https://app-sc.miguvideo.com/common/v1/settings/H5_DetailPage');
const settingsData = await settingsResp.json();
const playerVersion = JSON.parse(settingsData.body.paramValue).playerVersion;
// Step 2: Load WASM module
const wasmUrl = `https://www.miguvideo.com/mgs/player/prd/${playerVersion}/dist/mgprtcl.wasm`;
const wasmResp = await fetch(wasmUrl);
if (!wasmResp.ok) throw new Error("Failed to download WASM");
const wasmBuffer = await wasmResp.arrayBuffer();
const importObject = {
a: { a, b, c }
};
const { instance } = await WebAssembly.instantiate(wasmBuffer, importObject);
wasmInstance = instance;
const memory = wasmInstance.exports.d;
memory_p = new Uint8Array(memory.buffer);
memory_h = new Uint32Array(memory.buffer);
const exports = {
CallInterface1: wasmInstance.exports.h,
CallInterface2: wasmInstance.exports.i,
CallInterface3: wasmInstance.exports.j,
CallInterface4: wasmInstance.exports.k,
CallInterface6: wasmInstance.exports.m,
CallInterface7: wasmInstance.exports.n,
CallInterface8: wasmInstance.exports.o,
CallInterface9: wasmInstance.exports.p,
CallInterface10: wasmInstance.exports.q,
CallInterface11: wasmInstance.exports.r,
CallInterface14: wasmInstance.exports.t,
malloc: wasmInstance.exports.u,
};
const parsedUrl = new URL(inputUrl);
const query = Object.fromEntries(parsedUrl.searchParams);
const o = query.userid || '';
const a_val = query.timestamp || '';
const s = query.ProgramID || '';
const u = query.Channel_ID || '';
const v = query.puData || '';
// Allocate memory
const d = exports.malloc(o.length + 1);
const h = exports.malloc(a_val.length + 1);
const y = exports.malloc(s.length + 1);
const m = exports.malloc(u.length + 1);
const g = exports.malloc(v.length + 1);
const b_val = exports.malloc(f.length + 1);
const E = exports.malloc(128);
const T = exports.malloc(128);
// Write data to memory
stringToUTF8(o, d);
stringToUTF8(a_val, h);
stringToUTF8(s, y);
stringToUTF8(u, m);
stringToUTF8(v, g);
stringToUTF8(f, b_val);
// Call interface functions
const S = exports.CallInterface6(); // Create context
exports.CallInterface1(S, y, s.length);
exports.CallInterface10(S, h, a_val.length);
exports.CallInterface9(S, d, o.length);
exports.CallInterface3(S, 0, 0);
exports.CallInterface11(S, 0, 0);
exports.CallInterface8(S, g, v.length);
exports.CallInterface2(S, m, u.length);
exports.CallInterface14(S, b_val, f.length, T, 128);
const w = UTF8ToString(T);
const I = exports.malloc(w.length + 1);
stringToUTF8(w, I);
exports.CallInterface7(S, I, w.length);
exports.CallInterface4(S, E, 128);
return UTF8ToString(E);
}
const url = process.argv[2];
getDdCalcu(url).then(result => {
console.log(result);
}).catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -0,0 +1,78 @@
function sign(e) {
function t(e, t) {
return e << t | e >>> 32 - t
}
function o(e, t) {
var o, n, r, i, a;
return r = 2147483648 & e,
i = 2147483648 & t,
a = (1073741823 & e) + (1073741823 & t),
(o = 1073741824 & e) & (n = 1073741824 & t) ? 2147483648 ^ a ^ r ^ i : o | n ? 1073741824 & a ? 3221225472 ^ a ^ r ^ i : 1073741824 ^ a ^ r ^ i : a ^ r ^ i
}
function n(e, n, r, i, a, s, u) {
return o(t(e = o(e, o(o(function(e, t, o) {
return e & t | ~e & o
}(n, r, i), a), u)), s), n)
}
function r(e, n, r, i, a, s, u) {
return o(t(e = o(e, o(o(function(e, t, o) {
return e & o | t & ~o
}(n, r, i), a), u)), s), n)
}
function i(e, n, r, i, a, s, u) {
return o(t(e = o(e, o(o(function(e, t, o) {
return e ^ t ^ o
}(n, r, i), a), u)), s), n)
}
function a(e, n, r, i, a, s, u) {
return o(t(e = o(e, o(o(function(e, t, o) {
return t ^ (e | ~o)
}(n, r, i), a), u)), s), n)
}
function s(e) {
var t, o = "", n = "";
for (t = 0; 3 >= t; t++)
o += (n = "0" + (e >>> 8 * t & 255).toString(16)).substr(n.length - 2, 2);
return o
}
var u, l, d, c, p, f, h, m, y, g;
for (g = function(e) {
for (var t = e.length, o = t + 8, n = 16 * ((o - o % 64) / 64 + 1), r = Array(n - 1), i = 0, a = 0; t > a; )
i = a % 4 * 8,
r[(a - a % 4) / 4] |= e.charCodeAt(a) << i,
a++;
return i = a % 4 * 8,
r[(a - a % 4) / 4] |= 128 << i,
r[n - 2] = t << 3,
r[n - 1] = t >>> 29,
r
}(e = function(e) {
var t = String.fromCharCode;
e = e.replace(/\r\n/g, "\n");
for (var o, n = "", r = 0; r < e.length; r++)
128 > (o = e.charCodeAt(r)) ? n += t(o) : o > 127 && 2048 > o ? (n += t(o >> 6 | 192),
n += t(63 & o | 128)) : (n += t(o >> 12 | 224),
n += t(o >> 6 & 63 | 128),
n += t(63 & o | 128));
return n
}(e)),
f = 1732584193,
h = 4023233417,
m = 2562383102,
y = 271733878,
u = 0; u < g.length; u += 16)
l = f,
d = h,
c = m,
p = y,
h = a(h = a(h = a(h = a(h = i(h = i(h = i(h = i(h = r(h = r(h = r(h = r(h = n(h = n(h = n(h = n(h, m = n(m, y = n(y, f = n(f, h, m, y, g[u + 0], 7, 3614090360), h, m, g[u + 1], 12, 3905402710), f, h, g[u + 2], 17, 606105819), y, f, g[u + 3], 22, 3250441966), m = n(m, y = n(y, f = n(f, h, m, y, g[u + 4], 7, 4118548399), h, m, g[u + 5], 12, 1200080426), f, h, g[u + 6], 17, 2821735955), y, f, g[u + 7], 22, 4249261313), m = n(m, y = n(y, f = n(f, h, m, y, g[u + 8], 7, 1770035416), h, m, g[u + 9], 12, 2336552879), f, h, g[u + 10], 17, 4294925233), y, f, g[u + 11], 22, 2304563134), m = n(m, y = n(y, f = n(f, h, m, y, g[u + 12], 7, 1804603682), h, m, g[u + 13], 12, 4254626195), f, h, g[u + 14], 17, 2792965006), y, f, g[u + 15], 22, 1236535329), m = r(m, y = r(y, f = r(f, h, m, y, g[u + 1], 5, 4129170786), h, m, g[u + 6], 9, 3225465664), f, h, g[u + 11], 14, 643717713), y, f, g[u + 0], 20, 3921069994), m = r(m, y = r(y, f = r(f, h, m, y, g[u + 5], 5, 3593408605), h, m, g[u + 10], 9, 38016083), f, h, g[u + 15], 14, 3634488961), y, f, g[u + 4], 20, 3889429448), m = r(m, y = r(y, f = r(f, h, m, y, g[u + 9], 5, 568446438), h, m, g[u + 14], 9, 3275163606), f, h, g[u + 3], 14, 4107603335), y, f, g[u + 8], 20, 1163531501), m = r(m, y = r(y, f = r(f, h, m, y, g[u + 13], 5, 2850285829), h, m, g[u + 2], 9, 4243563512), f, h, g[u + 7], 14, 1735328473), y, f, g[u + 12], 20, 2368359562), m = i(m, y = i(y, f = i(f, h, m, y, g[u + 5], 4, 4294588738), h, m, g[u + 8], 11, 2272392833), f, h, g[u + 11], 16, 1839030562), y, f, g[u + 14], 23, 4259657740), m = i(m, y = i(y, f = i(f, h, m, y, g[u + 1], 4, 2763975236), h, m, g[u + 4], 11, 1272893353), f, h, g[u + 7], 16, 4139469664), y, f, g[u + 10], 23, 3200236656), m = i(m, y = i(y, f = i(f, h, m, y, g[u + 13], 4, 681279174), h, m, g[u + 0], 11, 3936430074), f, h, g[u + 3], 16, 3572445317), y, f, g[u + 6], 23, 76029189), m = i(m, y = i(y, f = i(f, h, m, y, g[u + 9], 4, 3654602809), h, m, g[u + 12], 11, 3873151461), f, h, g[u + 15], 16, 530742520), y, f, g[u + 2], 23, 3299628645), m = a(m, y = a(y, f = a(f, h, m, y, g[u + 0], 6, 4096336452), h, m, g[u + 7], 10, 1126891415), f, h, g[u + 14], 15, 2878612391), y, f, g[u + 5], 21, 4237533241), m = a(m, y = a(y, f = a(f, h, m, y, g[u + 12], 6, 1700485571), h, m, g[u + 3], 10, 2399980690), f, h, g[u + 10], 15, 4293915773), y, f, g[u + 1], 21, 2240044497), m = a(m, y = a(y, f = a(f, h, m, y, g[u + 8], 6, 1873313359), h, m, g[u + 15], 10, 4264355552), f, h, g[u + 6], 15, 2734768916), y, f, g[u + 13], 21, 1309151649), m = a(m, y = a(y, f = a(f, h, m, y, g[u + 4], 6, 4149444226), h, m, g[u + 11], 10, 3174756917), f, h, g[u + 2], 15, 718787259), y, f, g[u + 9], 21, 3951481745),
f = o(f, l),
h = o(h, d),
m = o(m, c),
y = o(y, p);
return (s(f) + s(h) + s(m) + s(y)).toLowerCase()
}
// 正确sign值05748e8359cd3e6deaab02d15caafc11
// var sg =sign('5655b7041ca049730330701082886efd&1719411639403&12574478&{"componentKey":"wp_pc_shop_basic_info","params":"{\\"memberId\\":\\"b2b-22133374292418351a\\"}"}')
// console.log(sg)

564
src/javascript/x-bogus.js Normal file

File diff suppressed because one or more lines are too long

43
src/logger.py Normal file
View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
import os
import sys
from loguru import logger
logger.remove()
custom_format = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> - <level>{message}</level>"
logger.add(
sink=sys.stderr,
format=custom_format,
level="DEBUG",
colorize=True,
enqueue=True
)
script_path = os.path.split(os.path.realpath(sys.argv[0]))[0]
logger.add(
f"{script_path}/logs/streamget.log",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}",
filter=lambda i: i["level"].name != "INFO",
serialize=False,
enqueue=True,
retention=1,
rotation="300 KB",
encoding='utf-8'
)
logger.add(
f"{script_path}/logs/PlayURL.log",
level="INFO",
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {message}",
filter=lambda i: i["level"].name == "INFO",
serialize=False,
enqueue=True,
retention=1,
rotation="300 KB",
encoding='utf-8'
)

92
src/proxy.py Normal file
View File

@@ -0,0 +1,92 @@
import os
import sys
from enum import Enum, auto
from dataclasses import dataclass, field
from .utils import logger
class ProxyType(Enum):
HTTP = auto()
HTTPS = auto()
SOCKS = auto()
@dataclass(frozen=True)
class ProxyInfo:
ip: str = field(default="", repr=True)
port: str = field(default="", repr=True)
def __post_init__(self):
if (self.ip and not self.port) or (not self.ip and self.port):
raise ValueError("IP or port cannot be empty")
if (self.ip and self.port) and (not self.port.isdigit() or not (1 <= int(self.port) <= 65535)):
raise ValueError("Port must be a digit between 1 and 65535")
class ProxyDetector:
def __init__(self):
if sys.platform.startswith('win'):
import winreg
self.winreg = winreg
self.__path = r'Software\Microsoft\Windows\CurrentVersion\Internet Settings'
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as key_user:
self.__INTERNET_SETTINGS = winreg.OpenKeyEx(key_user, self.__path, 0, winreg.KEY_ALL_ACCESS)
else:
self.__is_windows = False
def get_proxy_info(self) -> ProxyInfo:
if sys.platform.startswith('win'):
ip, port = self._get_proxy_info_windows()
else:
ip, port = self._get_proxy_info_linux()
return ProxyInfo(ip, port)
def is_proxy_enabled(self) -> bool:
if sys.platform.startswith('win'):
return self._is_proxy_enabled_windows()
else:
return self._is_proxy_enabled_linux()
def _get_proxy_info_windows(self) -> tuple[str, str]:
ip, port = "", ""
if self._is_proxy_enabled_windows():
try:
ip_port = self.winreg.QueryValueEx(self.__INTERNET_SETTINGS, "ProxyServer")[0]
if ip_port:
ip, port = ip_port.split(":")
except FileNotFoundError as err:
logger.warning("No proxy information found: " + str(err))
except Exception as err:
logger.error("An error occurred: " + str(err))
else:
logger.debug("No proxy is enabled on the system")
return ip, port
def _is_proxy_enabled_windows(self) -> bool:
try:
if self.winreg.QueryValueEx(self.__INTERNET_SETTINGS, "ProxyEnable")[0] == 1:
return True
except FileNotFoundError as err:
print("No proxy information found: " + str(err))
except Exception as err:
print("An error occurred: " + str(err))
return False
@staticmethod
def _get_proxy_info_linux() -> tuple[str, str]:
proxies = {
'http': os.getenv('http_proxy'),
'https': os.getenv('https_proxy'),
'ftp': os.getenv('ftp_proxy')
}
ip = port = ""
for proto, proxy in proxies.items():
if proxy:
ip, port = proxy.split(':')
break
return ip, port
def _is_proxy_enabled_linux(self) -> bool:
proxies = self._get_proxy_info_linux()
return any(proxy != '' for proxy in proxies)

150
src/room.py Normal file
View File

@@ -0,0 +1,150 @@
# -*- encoding: utf-8 -*-
"""
Author: Hmily
GitHub:https://github.com/ihmily
Date: 2023-07-17 23:52:05
Update: 2025-02-04 04:57:00
Copyright (c) 2023 by Hmily, All Rights Reserved.
"""
import re
import urllib.parse
import execjs
import httpx
import urllib.request
from . import JS_SCRIPT_PATH, utils
no_proxy_handler = urllib.request.ProxyHandler({})
opener = urllib.request.build_opener(no_proxy_handler)
class UnsupportedUrlError(Exception):
pass
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': 's_v_web_id=verify_lk07kv74_QZYCUApD_xhiB_405x_Ax51_GYO9bUIyZQVf'
}
HEADERS_PC = {
'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',
'Cookie': 'sessionid=7494ae59ae06784454373ce25761e864; __ac_nonce=0670497840077ee4c9eb2; '
'__ac_signature=_02B4Z6wo00f012DZczQAAIDCJJBb3EjnINdg-XeAAL8-db; '
's_v_web_id=verify_m1ztgtjj_vuHnMLZD_iwZ9_4YO4_BdN1_7wLP3pyqXsf2; '
}
# X-bogus算法
async def get_xbogus(url: str, headers: dict | None = None) -> str:
if not headers or 'user-agent' not in (k.lower() for k in headers):
headers = HEADERS
query = urllib.parse.urlparse(url).query
xbogus = execjs.compile(open(f'{JS_SCRIPT_PATH}/x-bogus.js').read()).call(
'sign', query, headers.get("User-Agent", "user-agent"))
return xbogus
# 获取房间ID和用户secID
async def get_sec_user_id(url: str, proxy_addr: str | None = None, headers: dict | None = None) -> tuple | None:
if not headers or all(k.lower() not in ['user-agent', 'cookie'] for k in headers):
headers = HEADERS
try:
proxy_addr = utils.handle_proxy_addr(proxy_addr)
async with httpx.AsyncClient(proxy=proxy_addr, timeout=15) as client:
response = await client.get(url, headers=headers, follow_redirects=True)
redirect_url = response.url
if 'reflow/' in str(redirect_url):
match = re.search(r'sec_user_id=([\w_\-]+)&', str(redirect_url))
if match:
sec_user_id = match.group(1)
room_id = str(redirect_url).split('?')[0].rsplit('/', maxsplit=1)[1]
return room_id, sec_user_id
else:
raise RuntimeError("Could not find sec_user_id in the URL.")
else:
raise UnsupportedUrlError("The redirect URL does not contain 'reflow/'.")
except UnsupportedUrlError as e:
raise e
except Exception as e:
raise RuntimeError(f"An error occurred: {e}")
# 获取抖音号
async def get_unique_id(url: str, proxy_addr: str | None = None, headers: dict | None = None) -> str | None:
if not headers or all(k.lower() not in ['user-agent', 'cookie'] for k in headers):
headers = HEADERS
try:
proxy_addr = utils.handle_proxy_addr(proxy_addr)
async with httpx.AsyncClient(proxy=proxy_addr, timeout=15) as client:
response = await client.get(url, headers=headers, follow_redirects=True)
redirect_url = str(response.url)
if 'reflow/' in str(redirect_url):
raise UnsupportedUrlError("Unsupported URL")
sec_user_id = redirect_url.split('?')[0].rsplit('/', maxsplit=1)[1]
headers['Cookie'] = ('ttwid=1%7C4ejCkU2bKY76IySQENJwvGhg1IQZrgGEupSyTKKfuyk%7C1740470403%7Cbc9a'
'd2ee341f1a162f9e27f4641778030d1ae91e31f9df6553a8f2efa3bdb7b4; __ac_nonce=06'
'83e59f3009cc48fbab0; __ac_signature=_02B4Z6wo00f01mG6waQAAIDB9JUCzFb6.TZhmsU'
'AAPBf34; __ac_referer=__ac_blank')
user_page_response = await client.get(f'https://www.iesdouyin.com/share/user/{sec_user_id}',
headers=headers, follow_redirects=True)
matches = re.findall(r'unique_id":"(.*?)","verification_type', user_page_response.text)
if matches:
unique_id = matches[-1]
return unique_id
else:
raise RuntimeError("Could not find unique_id in the response.")
except UnsupportedUrlError as e:
raise e
except Exception as e:
raise RuntimeError(f"An error occurred: {e}")
# 获取直播间webID
async def get_live_room_id(room_id: str, sec_user_id: str, proxy_addr: str | None = None, params: dict | None = None,
headers: dict | None = None) -> str:
if not headers or all(k.lower() not in ['user-agent', 'cookie'] for k in headers):
headers = HEADERS
if not params:
params = {
"verifyFp": "verify_lk07kv74_QZYCUApD_xhiB_405x_Ax51_GYO9bUIyZQVf",
"type_id": "0",
"live_id": "1",
"room_id": room_id,
"sec_user_id": sec_user_id,
"app_id": "1128",
"msToken": "wrqzbEaTlsxt52-vxyZo_mIoL0RjNi1ZdDe7gzEGMUTVh_HvmbLLkQrA_1HKVOa2C6gkxb6IiY6TY2z8enAkPEwGq--gM"
"-me3Yudck2ailla5Q4osnYIHxd9dI4WtQ==",
}
api = f'https://webcast.amemv.com/webcast/room/reflow/info/?{urllib.parse.urlencode(params)}'
xbogus = await get_xbogus(api)
api = api + "&X-Bogus=" + xbogus
try:
proxy_addr = utils.handle_proxy_addr(proxy_addr)
async with httpx.AsyncClient(proxy=proxy_addr,
timeout=15) as client:
response = await client.get(api, headers=headers)
response.raise_for_status()
json_data = response.json()
return json_data['data']['room']['owner']['web_rid']
except httpx.HTTPStatusError as e:
print(f"HTTP status error occurred: {e.response.status_code}")
raise
except Exception as e:
print(f"An exception occurred during get_live_room_id: {e}")
raise
if __name__ == '__main__':
room_url = "https://v.douyin.com/iQLgKSj/"
_room_id, sec_uid = get_sec_user_id(room_url)
web_rid = get_live_room_id(_room_id, sec_uid)
print("return web_rid:", web_rid)

3253
src/spider.py Normal file

File diff suppressed because it is too large Load Diff

446
src/stream.py Normal file
View File

@@ -0,0 +1,446 @@
# -*- encoding: utf-8 -*-
"""
Author: Hmily
GitHub: https://github.com/ihmily
Date: 2023-07-15 23:15:00
Update: 2025-02-06 02:28:00
Copyright (c) 2023-2025 by Hmily, All Rights Reserved.
Function: Get live stream data.
"""
import base64
import hashlib
import json
import time
import random
import re
from operator import itemgetter
import urllib.parse
import urllib.request
from .utils import trace_error_decorator
from .spider import (
get_douyu_stream_data, get_bilibili_stream_data
)
from .http_clients.async_http import get_response_status
QUALITY_MAPPING = {"OD": 0, "BD": 0, "UHD": 1, "HD": 2, "SD": 3, "LD": 4}
def get_quality_index(quality) -> tuple:
if not quality:
return list(QUALITY_MAPPING.items())[0]
quality_str = str(quality).upper()
if quality_str.isdigit():
quality_int = int(quality_str[0])
quality_str = list(QUALITY_MAPPING.keys())[quality_int]
return quality_str, QUALITY_MAPPING.get(quality_str, 0)
@trace_error_decorator
async def get_douyin_stream_url(json_data: dict, video_quality: str, proxy_addr: str) -> dict:
anchor_name = json_data.get('anchor_name')
result = {
"anchor_name": anchor_name,
"is_live": False,
}
status = json_data.get("status", 4)
if status == 2:
stream_url = json_data['stream_url']
flv_url_dict = stream_url['flv_pull_url']
flv_url_list: list = list(flv_url_dict.values())
m3u8_url_dict = stream_url['hls_pull_url_map']
m3u8_url_list: list = list(m3u8_url_dict.values())
while len(flv_url_list) < 5:
flv_url_list.append(flv_url_list[-1])
m3u8_url_list.append(m3u8_url_list[-1])
video_quality, quality_index = get_quality_index(video_quality)
m3u8_url = m3u8_url_list[quality_index]
flv_url = flv_url_list[quality_index]
ok = await get_response_status(url=m3u8_url, proxy_addr=proxy_addr)
if not ok:
index = quality_index + 1 if quality_index < 4 else quality_index - 1
m3u8_url = m3u8_url_list[index]
flv_url = flv_url_list[index]
result |= {
'is_live': True,
'title': json_data['title'],
'quality': video_quality,
'm3u8_url': m3u8_url,
'flv_url': flv_url,
'record_url': m3u8_url or flv_url,
}
return result
@trace_error_decorator
async def get_tiktok_stream_url(json_data: dict, video_quality: str, proxy_addr: str) -> dict:
if not json_data:
return {"anchor_name": None, "is_live": False}
def get_video_quality_url(stream, q_key) -> list:
play_list = []
for key in stream:
url_info = stream[key]['main']
sdk_params = url_info['sdk_params']
sdk_params = json.loads(sdk_params)
vbitrate = int(sdk_params['vbitrate'])
v_codec = sdk_params.get('VCodec', '')
play_url = ''
if url_info.get(q_key):
if url_info[q_key].endswith(".flv") or url_info[q_key].endswith(".m3u8"):
play_url = url_info[q_key] + '?codec=' + v_codec
else:
play_url = url_info[q_key] + '&codec=' + v_codec
resolution = sdk_params['resolution']
if vbitrate != 0 and resolution:
width, height = map(int, resolution.split('x'))
play_list.append({'url': play_url, 'vbitrate': vbitrate, 'resolution': (width, height)})
play_list.sort(key=itemgetter('vbitrate'), reverse=True)
play_list.sort(key=lambda x: (-x['vbitrate'], -x['resolution'][0], -x['resolution'][1]))
return play_list
live_room = json_data['LiveRoom']['liveRoomUserInfo']
user = live_room['user']
anchor_name = f"{user['nickname']}-{user['uniqueId']}"
status = user.get("status", 4)
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if status == 2:
stream_data = live_room['liveRoom']['streamData']['pull_data']['stream_data']
stream_data = json.loads(stream_data).get('data', {})
flv_url_list = get_video_quality_url(stream_data, 'flv')
m3u8_url_list = get_video_quality_url(stream_data, 'hls')
while len(flv_url_list) < 5:
flv_url_list.append(flv_url_list[-1])
while len(m3u8_url_list) < 5:
m3u8_url_list.append(m3u8_url_list[-1])
video_quality, quality_index = get_quality_index(video_quality)
flv_dict: dict = flv_url_list[quality_index]
m3u8_dict: dict = m3u8_url_list[quality_index]
check_url = m3u8_dict.get('url') or flv_dict.get('url')
ok = await get_response_status(url=check_url, proxy_addr=proxy_addr, http2=False)
if not ok:
index = quality_index + 1 if quality_index < 4 else quality_index - 1
flv_dict: dict = flv_url_list[index]
m3u8_dict: dict = m3u8_url_list[index]
flv_url = flv_dict['url']
m3u8_url = m3u8_dict['url']
result |= {
'is_live': True,
'title': live_room['liveRoom']['title'],
'quality': video_quality,
'm3u8_url': m3u8_url,
'flv_url': flv_url,
'record_url': m3u8_url or flv_url,
}
return result
@trace_error_decorator
async def get_kuaishou_stream_url(json_data: dict, video_quality: str) -> dict:
if json_data['type'] == 1 and not json_data["is_live"]:
return json_data
live_status = json_data['is_live']
result = {
"type": 2,
"anchor_name": json_data['anchor_name'],
"is_live": live_status,
}
if live_status:
quality_mapping_bit = {'OD': 99999, 'BD': 4000, 'UHD': 2000, 'HD': 1000, 'SD': 800, 'LD': 600}
if video_quality in QUALITY_MAPPING:
quality, quality_index = get_quality_index(video_quality)
if 'm3u8_url_list' in json_data:
m3u8_url_list = json_data['m3u8_url_list'][::-1]
while len(m3u8_url_list) < 5:
m3u8_url_list.append(m3u8_url_list[-1])
m3u8_url = m3u8_url_list[quality_index]['url']
result['m3u8_url'] = m3u8_url
if 'flv_url_list' in json_data:
if 'bitrate' in json_data['flv_url_list'][0]:
flv_url_list = json_data['flv_url_list']
flv_url_list = sorted(flv_url_list, key=lambda x: x['bitrate'], reverse=True)
quality_str = str(video_quality).upper()
if quality_str.isdigit():
video_quality, quality_index_bitrate_value = list(quality_mapping_bit.items())[int(quality_str)]
else:
quality_index_bitrate_value = quality_mapping_bit.get(quality_str, 99999)
video_quality = quality_str
quality_index = next(
(i for i, x in enumerate(flv_url_list) if x['bitrate'] <= quality_index_bitrate_value), None)
if quality_index is None:
quality_index = len(flv_url_list) - 1
flv_url = flv_url_list[quality_index]['url']
result['flv_url'] = flv_url
result['record_url'] = flv_url
else:
flv_url_list = json_data['flv_url_list'][::-1]
while len(flv_url_list) < 5:
flv_url_list.append(flv_url_list[-1])
flv_url = flv_url_list[quality_index]['url']
result |= {'flv_url': flv_url, 'record_url': flv_url}
result['is_live'] = True
result['quality'] = video_quality
return result
@trace_error_decorator
async def get_huya_stream_url(json_data: dict, video_quality: str) -> dict:
game_live_info = json_data['data'][0]['gameLiveInfo']
live_title = game_live_info['introduction']
stream_info_list = json_data['data'][0]['gameStreamInfoList']
anchor_name = game_live_info.get('nick', '')
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if stream_info_list:
select_cdn = stream_info_list[0]
flv_url = select_cdn.get('sFlvUrl')
stream_name = select_cdn.get('sStreamName')
flv_url_suffix = select_cdn.get('sFlvUrlSuffix')
hls_url = select_cdn.get('sHlsUrl')
hls_url_suffix = select_cdn.get('sHlsUrlSuffix')
flv_anti_code = select_cdn.get('sFlvAntiCode')
def get_anti_code(old_anti_code: str) -> str:
# js地址https://hd.huya.com/cdn_libs/mobile/hysdk-m-202402211431.js
params_t = 100
sdk_version = 2403051612
# sdk_id是13位数毫秒级时间戳
t13 = int(time.time()) * 1000
sdk_sid = t13
# 计算uuid和uid参数值
init_uuid = (int(t13 % 10 ** 10 * 1000) + int(1000 * random.random())) % 4294967295 # 直接初始化
uid = random.randint(1400000000000, 1400009999999) # 经过测试uid也可以使用init_uuid代替
seq_id = uid + sdk_sid # 移动端请求的直播流地址中包含seqId参数
# 计算ws_time参数值(16进制) 可以是当前毫秒时间戳当然也可以直接使用url_query['wsTime'][0]
# 原始最大误差不得慢240000毫秒
target_unix_time = (t13 + 110624) // 1000
ws_time = f"{target_unix_time:x}".lower()
# fm参数值是经过url编码然后base64编码得到的解码结果类似 DWq8BcJ3h6DJt6TY_$0_$1_$2_$3
# 具体细节在上面js中查看大概在32657行代码开始有base64混淆代码请自行替换
url_query = urllib.parse.parse_qs(old_anti_code)
ws_secret_pf = base64.b64decode(urllib.parse.unquote(url_query['fm'][0]).encode()).decode().split("_")[0]
ws_secret_hash = hashlib.md5(f'{seq_id}|{url_query["ctype"][0]}|{params_t}'.encode()).hexdigest()
ws_secret = f'{ws_secret_pf}_{uid}_{stream_name}_{ws_secret_hash}_{ws_time}'
ws_secret_md5 = hashlib.md5(ws_secret.encode()).hexdigest()
anti_code = (
f'wsSecret={ws_secret_md5}&wsTime={ws_time}&seqid={seq_id}&ctype={url_query["ctype"][0]}&ver=1'
f'&fs={url_query["fs"][0]}&uuid={init_uuid}&u={uid}&t={params_t}&sv={sdk_version}'
f'&sdk_sid={sdk_sid}&codec=264'
)
return anti_code
new_anti_code = get_anti_code(flv_anti_code)
flv_url = f'{flv_url}/{stream_name}.{flv_url_suffix}?{new_anti_code}&ratio='
m3u8_url = f'{hls_url}/{stream_name}.{hls_url_suffix}?{new_anti_code}&ratio='
quality_list = flv_anti_code.split('&exsphd=')
if len(quality_list) > 1 and video_quality not in ["OD", "BD"]:
pattern = r"(?<=264_)\d+"
quality_list = list(re.findall(pattern, quality_list[1]))[::-1]
while len(quality_list) < 5:
quality_list.append(quality_list[-1])
video_quality_options = {
"UHD": quality_list[0],
"HD": quality_list[1],
"SD": quality_list[2],
"LD": quality_list[3]
}
if video_quality not in video_quality_options:
raise ValueError(
f"Invalid video quality. Available options are: {', '.join(video_quality_options.keys())}")
flv_url = flv_url + str(video_quality_options[video_quality])
m3u8_url = m3u8_url + str(video_quality_options[video_quality])
result |= {
'is_live': True,
'title': live_title,
'quality': video_quality,
'm3u8_url': m3u8_url,
'flv_url': flv_url,
'record_url': flv_url or m3u8_url
}
return result
@trace_error_decorator
async def get_douyu_stream_url(json_data: dict, video_quality: str, cookies: str, proxy_addr: str) -> dict:
if not json_data["is_live"]:
return json_data
video_quality_options = {
"OD": '0',
"BD": '0',
"UHD": '3',
"HD": '2',
"SD": '1',
"LD": '1'
}
rid = str(json_data["room_id"])
json_data.pop("room_id")
rate = video_quality_options.get(video_quality, '0')
flv_data = await get_douyu_stream_data(rid, rate, cookies=cookies, proxy_addr=proxy_addr)
rtmp_url = flv_data['data'].get('rtmp_url')
rtmp_live = flv_data['data'].get('rtmp_live')
if rtmp_live:
flv_url = f'{rtmp_url}/{rtmp_live}'
json_data |= {'quality': video_quality, 'flv_url': flv_url, 'record_url': flv_url}
return json_data
@trace_error_decorator
async def get_yy_stream_url(json_data: dict) -> dict:
anchor_name = json_data.get('anchor_name', '')
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if 'avp_info_res' in json_data:
stream_line_addr = json_data['avp_info_res']['stream_line_addr']
cdn_info = list(stream_line_addr.values())[0]
flv_url = cdn_info['cdn_info']['url']
result |= {
'is_live': True,
'title': json_data['title'],
'quality': 'OD',
'flv_url': flv_url,
'record_url': flv_url
}
return result
@trace_error_decorator
async def get_bilibili_stream_url(json_data: dict, video_quality: str, proxy_addr: str, cookies: str) -> dict:
anchor_name = json_data["anchor_name"]
if not json_data["live_status"]:
return {
"anchor_name": anchor_name,
"is_live": False
}
room_url = json_data['room_url']
video_quality_options = {
"OD": '10000',
"BD": '400',
"UHD": '250',
"HD": '150',
"SD": '80',
"LD": '80'
}
select_quality = video_quality_options[video_quality]
play_url = await get_bilibili_stream_data(
room_url, qn=select_quality, platform='web', proxy_addr=proxy_addr, cookies=cookies)
return {
'anchor_name': json_data['anchor_name'],
'is_live': True,
'title': json_data['title'],
'quality': video_quality,
'record_url': play_url
}
@trace_error_decorator
async def get_netease_stream_url(json_data: dict, video_quality: str) -> dict:
if not json_data['is_live']:
return json_data
m3u8_url = json_data['m3u8_url']
flv_url = None
if json_data.get('stream_list'):
stream_list = json_data['stream_list']['resolution']
order = ['blueray', 'ultra', 'high', 'standard']
sorted_keys = [key for key in order if key in stream_list]
while len(sorted_keys) < 5:
sorted_keys.append(sorted_keys[-1])
video_quality, quality_index = get_quality_index(video_quality)
selected_quality = sorted_keys[quality_index]
flv_url_list = stream_list[selected_quality]['cdn']
selected_cdn = list(flv_url_list.keys())[0]
flv_url = flv_url_list[selected_cdn]
return {
"is_live": True,
"anchor_name": json_data['anchor_name'],
"title": json_data['title'],
'quality': video_quality,
"m3u8_url": m3u8_url,
"flv_url": flv_url,
"record_url": flv_url or m3u8_url
}
async def get_stream_url(json_data: dict, video_quality: str, url_type: str = 'm3u8', spec: bool = False,
hls_extra_key: str | int = None, flv_extra_key: str | int = None) -> dict:
if not json_data['is_live']:
return json_data
play_url_list = json_data['play_url_list']
while len(play_url_list) < 5:
play_url_list.append(play_url_list[-1])
video_quality, selected_quality = get_quality_index(video_quality)
data = {
"anchor_name": json_data['anchor_name'],
"is_live": True
}
def get_url(key):
play_url = play_url_list[selected_quality]
return play_url[key] if key else play_url
if url_type == 'all':
m3u8_url = get_url(hls_extra_key)
flv_url = get_url(flv_extra_key)
data |= {
"m3u8_url": json_data['m3u8_url'] if spec else m3u8_url,
"flv_url": json_data['flv_url'] if spec else flv_url,
"record_url": m3u8_url
}
elif url_type == 'm3u8':
m3u8_url = get_url(hls_extra_key)
data |= {"m3u8_url": json_data['m3u8_url'] if spec else m3u8_url, "record_url": m3u8_url}
else:
flv_url = get_url(flv_extra_key)
data |= {"flv_url": flv_url, "record_url": flv_url}
data['title'] = json_data.get('title')
data['quality'] = video_quality
return data

206
src/utils.py Normal file
View File

@@ -0,0 +1,206 @@
# -*- coding: utf-8 -*-
import json
import os
import random
import shutil
import string
from pathlib import Path
import functools
import hashlib
import re
import traceback
from typing import Any
from urllib.parse import parse_qs, urlparse
from collections import OrderedDict
import execjs
from .logger import logger
import configparser
OptionalStr = str | None
OptionalDict = dict | None
class Color:
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
RESET = "\033[0m"
@staticmethod
def print_colored(text, color):
print(f"{color}{text}{Color.RESET}")
def trace_error_decorator(func: callable) -> callable:
@functools.wraps(func)
def wrapper(*args: list, **kwargs: dict) -> Any:
try:
return func(*args, **kwargs)
except execjs.ProgramError:
logger.warning('Failed to execute JS code. Please check if the Node.js environment')
except Exception as e:
error_line = traceback.extract_tb(e.__traceback__)[-1].lineno
error_info = f"message: type: {type(e).__name__}, {str(e)} in function {func.__name__} at line: {error_line}"
logger.error(error_info)
return []
return wrapper
def check_md5(file_path: str | Path) -> str:
with open(file_path, 'rb') as fp:
file_md5 = hashlib.md5(fp.read()).hexdigest()
return file_md5
def dict_to_cookie_str(cookies_dict: dict) -> str:
cookie_str = '; '.join([f"{key}={value}" for key, value in cookies_dict.items()])
return cookie_str
def read_config_value(file_path: str | Path, section: str, key: str) -> str | None:
config = configparser.ConfigParser()
try:
config.read(file_path, encoding='utf-8-sig')
except Exception as e:
print(f"Error occurred while reading the configuration file: {e}")
return None
if section in config:
if key in config[section]:
return config[section][key]
else:
print(f"Key [{key}] does not exist in section [{section}].")
else:
print(f"Section [{section}] does not exist in the file.")
return None
def update_config(file_path: str | Path, section: str, key: str, new_value: str) -> None:
config = configparser.ConfigParser()
try:
config.read(file_path, encoding='utf-8-sig')
except Exception as e:
print(f"An error occurred while reading the configuration file: {e}")
return
if section not in config:
print(f"Section [{section}] does not exist in the file.")
return
# 转义%字符
escaped_value = new_value.replace('%', '%%')
config[section][key] = escaped_value
try:
with open(file_path, 'w', encoding='utf-8-sig') as configfile:
config.write(configfile)
print(f"The value of {key} under [{section}] in the configuration file has been updated.")
except Exception as e:
print(f"Error occurred while writing to the configuration file: {e}")
def get_file_paths(directory: str) -> list:
file_paths = []
for root, dirs, files in os.walk(directory):
for file in files:
file_paths.append(os.path.join(root, file))
return file_paths
def remove_emojis(text: str, replace_text: str = '') -> str:
emoji_pattern = re.compile(
"["
"\U0001F1E0-\U0001F1FF" # flags (iOS)
"\U0001F300-\U0001F5FF" # symbols & pictographs
"\U0001F600-\U0001F64F" # emoticons
"\U0001F680-\U0001F6FF" # transport & map symbols
"\U0001F700-\U0001F77F" # alchemical symbols
"\U0001F780-\U0001F7FF" # Geometric Shapes Extended
"\U0001F800-\U0001F8FF" # Supplemental Arrows-C
"\U0001F900-\U0001F9FF" # Supplemental Symbols and Pictographs
"\U0001FA00-\U0001FA6F" # Chess Symbols
"\U0001FA70-\U0001FAFF" # Symbols and Pictographs Extended-A
"\U00002702-\U000027B0" # Dingbats
"]+",
flags=re.UNICODE
)
return emoji_pattern.sub(replace_text, text)
def remove_duplicate_lines(file_path: str | Path) -> None:
unique_lines = OrderedDict()
text_encoding = 'utf-8-sig'
with open(file_path, 'r', encoding=text_encoding) as input_file:
for line in input_file:
unique_lines[line.strip()] = None
with open(file_path, 'w', encoding=text_encoding) as output_file:
for line in unique_lines:
output_file.write(line + '\n')
def check_disk_capacity(file_path: str | Path, show: bool = False) -> float:
absolute_path = os.path.abspath(file_path)
directory = os.path.dirname(absolute_path)
disk_usage = shutil.disk_usage(directory)
disk_root = Path(directory).anchor
free_space_gb = disk_usage.free / (1024 ** 3)
if show:
print(f"{disk_root} Total: {disk_usage.total / (1024 ** 3):.2f} GB "
f"Used: {disk_usage.used / (1024 ** 3):.2f} GB "
f"Free: {free_space_gb:.2f} GB\n")
return free_space_gb
def handle_proxy_addr(proxy_addr):
if proxy_addr:
if not proxy_addr.startswith('http'):
proxy_addr = 'http://' + proxy_addr
else:
proxy_addr = None
return proxy_addr
def generate_random_string(length: int) -> str:
characters = string.ascii_uppercase + string.digits
random_string = ''.join(random.choices(characters, k=length))
return random_string
def jsonp_to_json(jsonp_str: str) -> OptionalDict:
pattern = r'(\w+)\((.*)\);?$'
match = re.search(pattern, jsonp_str)
if match:
_, json_str = match.groups()
json_obj = json.loads(json_str)
return json_obj
else:
raise Exception("No JSON data found in JSONP response.")
def replace_url(file_path: str | Path, old: str, new: str) -> None:
with open(file_path, 'r', encoding='utf-8-sig') as f:
content = f.read()
if old in content:
with open(file_path, 'w', encoding='utf-8-sig') as f:
f.write(content.replace(old, new))
def get_query_params(url: str, param_name: OptionalStr) -> dict | list[str]:
parsed_url = urlparse(url)
query_params = parse_qs(parsed_url.query)
if param_name is None:
return query_params
else:
values = query_params.get(param_name, [])
return values