mirror of
https://github.com/Evil0ctal/Douyin_TikTok_Download_API.git
synced 2025-04-23 05:24:26 +08:00
560 lines
14 KiB
Python
560 lines
14 KiB
Python
"""
|
|
Original Author:
|
|
This file is from https://github.com/JoeanAmier/TikTokDownloader
|
|
And is licensed under the GNU General Public License v3.0
|
|
If you use this code, please keep this license and the original author information.
|
|
|
|
Modified by:
|
|
And this file is now a part of the https://github.com/Evil0ctal/Douyin_TikTok_Download_API open-source project.
|
|
This project is licensed under the Apache License 2.0, and the original author information is kept.
|
|
|
|
Purpose:
|
|
This file is used to generate the `a_bogus` parameter for the Douyin Web API.
|
|
|
|
Changes Made:
|
|
1. Changed the ua_code to compatible with the current config file User-Agent string in https://github.com/Evil0ctal/Douyin_TikTok_Download_API/blob/main/crawlers/douyin/web/config.yaml
|
|
"""
|
|
|
|
from random import randint
|
|
from random import random
|
|
from re import compile
|
|
from time import time
|
|
from urllib.parse import urlencode, quote
|
|
|
|
|
|
class ABogus:
|
|
__filter = compile(r'%([0-9A-F]{2})')
|
|
__arguments = [0, 1, 14]
|
|
__end_string = "cus"
|
|
__version = [1, 0, 1, 5]
|
|
__env = [
|
|
49,
|
|
53,
|
|
51,
|
|
54,
|
|
124,
|
|
55,
|
|
52,
|
|
50,
|
|
124,
|
|
49,
|
|
53,
|
|
51,
|
|
54,
|
|
124,
|
|
56,
|
|
54,
|
|
52,
|
|
124,
|
|
48,
|
|
124,
|
|
48,
|
|
124,
|
|
48,
|
|
124,
|
|
48,
|
|
124,
|
|
49,
|
|
53,
|
|
51,
|
|
54,
|
|
124,
|
|
56,
|
|
54,
|
|
52,
|
|
124,
|
|
49,
|
|
53,
|
|
51,
|
|
54,
|
|
124,
|
|
56,
|
|
54,
|
|
52,
|
|
124,
|
|
49,
|
|
53,
|
|
51,
|
|
54,
|
|
124,
|
|
55,
|
|
52,
|
|
50,
|
|
124,
|
|
50,
|
|
52,
|
|
124,
|
|
50,
|
|
52,
|
|
124,
|
|
87,
|
|
105,
|
|
110,
|
|
51,
|
|
50]
|
|
__reg = [
|
|
1937774191,
|
|
1226093241,
|
|
388252375,
|
|
3666478592,
|
|
2842636476,
|
|
372324522,
|
|
3817729613,
|
|
2969243214,
|
|
]
|
|
__str = {
|
|
"s0": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
|
"s1": "Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=",
|
|
"s2": "Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=",
|
|
"s3": "ckdp1h4ZKsUB80/Mfvw36XIgR25+WQAlEi7NLboqYTOPuzmFjJnryx9HVGDaStCe",
|
|
"s4": "Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe"}
|
|
|
|
def __init__(self, ):
|
|
self.chunk = []
|
|
self.size = 0
|
|
self.reg = self.__reg[:]
|
|
|
|
@classmethod
|
|
def list_1(cls, random_num=None, a=170, b=85, c=45, ) -> list:
|
|
return cls.random_list(
|
|
random_num,
|
|
a,
|
|
b,
|
|
1,
|
|
2,
|
|
5,
|
|
c & a,
|
|
)
|
|
|
|
@classmethod
|
|
def list_2(cls, random_num=None, a=170, b=85, ) -> list:
|
|
return cls.random_list(
|
|
random_num,
|
|
a,
|
|
b,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
)
|
|
|
|
@classmethod
|
|
def list_3(cls, random_num=None, a=170, b=85, ) -> list:
|
|
return cls.random_list(
|
|
random_num,
|
|
a,
|
|
b,
|
|
1,
|
|
0,
|
|
5,
|
|
0,
|
|
)
|
|
|
|
@staticmethod
|
|
def random_list(
|
|
a: float = None,
|
|
b=170,
|
|
c=85,
|
|
d=0,
|
|
e=0,
|
|
f=0,
|
|
g=0,
|
|
) -> list:
|
|
r = a or (random() * 10000)
|
|
v = [
|
|
r,
|
|
int(r) & 255,
|
|
int(r) >> 8,
|
|
]
|
|
s = v[1] & b | d
|
|
v.append(s)
|
|
s = v[1] & c | e
|
|
v.append(s)
|
|
s = v[2] & b | f
|
|
v.append(s)
|
|
s = v[2] & c | g
|
|
v.append(s)
|
|
return v[-4:]
|
|
|
|
@staticmethod
|
|
def from_char_code(*args):
|
|
return "".join(chr(code) for code in args)
|
|
|
|
@classmethod
|
|
def generate_string_1(
|
|
cls,
|
|
random_num_1=None,
|
|
random_num_2=None,
|
|
random_num_3=None,
|
|
):
|
|
return cls.from_char_code(*cls.list_1(random_num_1)) + cls.from_char_code(
|
|
*cls.list_2(random_num_2)) + cls.from_char_code(*cls.list_3(random_num_3))
|
|
|
|
def generate_string_2(
|
|
self,
|
|
url_params: str,
|
|
user_agent: str,
|
|
start_time=0,
|
|
end_time=0,
|
|
) -> str:
|
|
a = self.generate_string_2_list(
|
|
url_params,
|
|
user_agent,
|
|
start_time,
|
|
end_time,
|
|
)
|
|
e = self.end_check_num(a)
|
|
a.extend(self.__env)
|
|
a.append(e)
|
|
return self.rc4_encrypt(self.from_char_code(*a), "y")
|
|
|
|
def generate_string_2_list(
|
|
self,
|
|
url_params: str,
|
|
user_agent: str,
|
|
start_time=0,
|
|
end_time=0,
|
|
) -> list:
|
|
start_time = start_time or int(time() * 1000)
|
|
end_time = end_time or (start_time + randint(4, 8))
|
|
params_array = self.sum(self.sum(url_params))
|
|
# TODO: 需要编写一个函数来生成ua_code 2024年6月13日17:13:08
|
|
# Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
|
|
ua_code = [76, 98, 15, 131, 97, 245, 224, 133, 122, 199, 241, 166, 79, 34, 90, 191, 128, 126, 122, 98, 66, 11, 14, 40, 49, 110, 110, 173, 67, 96, 138, 252]
|
|
return self.list_4(
|
|
(end_time >> 24) & 255,
|
|
params_array[21],
|
|
ua_code[23],
|
|
(end_time >> 16) & 255,
|
|
params_array[22],
|
|
ua_code[24],
|
|
(end_time >> 8) & 255,
|
|
(end_time >> 0) & 255,
|
|
(start_time >> 24) & 255,
|
|
(start_time >> 16) & 255,
|
|
(start_time >> 8) & 255,
|
|
(start_time >> 0) & 255,
|
|
)
|
|
|
|
@staticmethod
|
|
def reg_to_array(a):
|
|
o = [0] * 32
|
|
for i in range(8):
|
|
c = a[i]
|
|
o[4 * i + 3] = (255 & c)
|
|
c >>= 8
|
|
o[4 * i + 2] = (255 & c)
|
|
c >>= 8
|
|
o[4 * i + 1] = (255 & c)
|
|
c >>= 8
|
|
o[4 * i] = (255 & c)
|
|
|
|
return o
|
|
|
|
def compress(self, a):
|
|
f = self.generate_f(a)
|
|
i = self.reg[:]
|
|
for o in range(64):
|
|
c = self.de(i[0], 12) + i[4] + self.de(self.pe(o), o)
|
|
c = (c & 0xFFFFFFFF)
|
|
c = self.de(c, 7)
|
|
s = (c ^ self.de(i[0], 12)) & 0xFFFFFFFF
|
|
|
|
u = self.he(o, i[0], i[1], i[2])
|
|
u = (u + i[3] + s + f[o + 68]) & 0xFFFFFFFF
|
|
|
|
b = self.ve(o, i[4], i[5], i[6])
|
|
b = (b + i[7] + c + f[o]) & 0xFFFFFFFF
|
|
|
|
i[3] = i[2]
|
|
i[2] = self.de(i[1], 9)
|
|
i[1] = i[0]
|
|
i[0] = u
|
|
|
|
i[7] = i[6]
|
|
i[6] = self.de(i[5], 19)
|
|
i[5] = i[4]
|
|
i[4] = (b ^ self.de(b, 9) ^ self.de(b, 17)) & 0xFFFFFFFF
|
|
|
|
for l in range(8):
|
|
self.reg[l] = (self.reg[l] ^ i[l]) & 0xFFFFFFFF
|
|
|
|
@classmethod
|
|
def generate_f(cls, e):
|
|
r = [0] * 132
|
|
|
|
for t in range(16):
|
|
r[t] = (e[4 * t] << 24) | (e[4 * t + 1] <<
|
|
16) | (e[4 * t + 2] << 8) | e[4 * t + 3]
|
|
r[t] &= 0xFFFFFFFF
|
|
|
|
for n in range(16, 68):
|
|
a = r[n - 16] ^ r[n - 9] ^ cls.de(r[n - 3], 15)
|
|
a = a ^ cls.de(a, 15) ^ cls.de(a, 23)
|
|
r[n] = (a ^ cls.de(r[n - 13], 7) ^ r[n - 6]) & 0xFFFFFFFF
|
|
|
|
for n in range(68, 132):
|
|
r[n] = (r[n - 68] ^ r[n - 64]) & 0xFFFFFFFF
|
|
|
|
return r
|
|
|
|
@staticmethod
|
|
def pad_array(arr, length=60):
|
|
while len(arr) < length:
|
|
arr.append(0)
|
|
return arr
|
|
|
|
def fill(self, length=60):
|
|
size = 8 * self.size
|
|
self.chunk.append(128)
|
|
self.chunk = self.pad_array(self.chunk, length)
|
|
for i in range(4):
|
|
self.chunk.append((size >> 8 * (3 - i)) & 255)
|
|
|
|
@staticmethod
|
|
def list_4(
|
|
a: int,
|
|
b: int,
|
|
c: int,
|
|
d: int,
|
|
e: int,
|
|
f: int,
|
|
g: int,
|
|
h: int,
|
|
i: int,
|
|
j: int,
|
|
k: int,
|
|
m: int,
|
|
) -> list:
|
|
return [
|
|
44,
|
|
a,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
24,
|
|
b,
|
|
58,
|
|
0,
|
|
c,
|
|
d,
|
|
0,
|
|
24,
|
|
97,
|
|
1,
|
|
0,
|
|
239,
|
|
e,
|
|
51,
|
|
f,
|
|
g,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
h,
|
|
0,
|
|
0,
|
|
14,
|
|
i,
|
|
j,
|
|
0,
|
|
k,
|
|
m,
|
|
3,
|
|
399,
|
|
1,
|
|
399,
|
|
1,
|
|
64,
|
|
0,
|
|
0,
|
|
0]
|
|
|
|
@staticmethod
|
|
def end_check_num(a: list):
|
|
r = 0
|
|
for i in a:
|
|
r ^= i
|
|
return r
|
|
|
|
@classmethod
|
|
def decode_string(cls, url_string, ):
|
|
decoded = cls.__filter.sub(cls.replace_func, url_string)
|
|
return decoded
|
|
|
|
@staticmethod
|
|
def replace_func(match):
|
|
return chr(int(match.group(1), 16))
|
|
|
|
@staticmethod
|
|
def de(e, r):
|
|
r %= 32
|
|
return ((e << r) & 0xFFFFFFFF) | (e >> (32 - r))
|
|
|
|
@staticmethod
|
|
def pe(e):
|
|
return 2043430169 if 0 <= e < 16 else 2055708042
|
|
|
|
@staticmethod
|
|
def he(e, r, t, n):
|
|
if 0 <= e < 16:
|
|
return (r ^ t ^ n) & 0xFFFFFFFF
|
|
elif 16 <= e < 64:
|
|
return (r & t | r & n | t & n) & 0xFFFFFFFF
|
|
raise ValueError
|
|
|
|
@staticmethod
|
|
def ve(e, r, t, n):
|
|
if 0 <= e < 16:
|
|
return (r ^ t ^ n) & 0xFFFFFFFF
|
|
elif 16 <= e < 64:
|
|
return (r & t | ~r & n) & 0xFFFFFFFF
|
|
raise ValueError
|
|
|
|
@staticmethod
|
|
def convert_to_char_code(a):
|
|
d = []
|
|
for i in a:
|
|
d.append(ord(i))
|
|
return d
|
|
|
|
@staticmethod
|
|
def split_array(arr, chunk_size=64):
|
|
result = []
|
|
for i in range(0, len(arr), chunk_size):
|
|
result.append(arr[i:i + chunk_size])
|
|
return result
|
|
|
|
@staticmethod
|
|
def char_code_at(s):
|
|
return [ord(char) for char in s]
|
|
|
|
def write(self, e, ):
|
|
if isinstance(e, str):
|
|
e = self.decode_string(e + self.__end_string)
|
|
e = self.char_code_at(e)
|
|
self.size = len(e)
|
|
if len(e) <= 64:
|
|
self.chunk = e
|
|
else:
|
|
chunks = self.split_array(e, 64)
|
|
for i in chunks[:-1]:
|
|
self.compress(i)
|
|
self.chunk = chunks[-1]
|
|
|
|
def reset(self, ):
|
|
self.chunk = []
|
|
self.size = 0
|
|
self.reg = self.__reg[:]
|
|
|
|
def sum(self, e, length=60):
|
|
self.reset()
|
|
self.write(e)
|
|
self.fill(length)
|
|
self.compress(self.chunk)
|
|
a = self.reg_to_array(self.reg)
|
|
self.reset()
|
|
return a
|
|
|
|
@classmethod
|
|
def generate_result_unit(cls, n, s):
|
|
r = ""
|
|
for i, j in zip(range(18, -1, -6), (16515072, 258048, 4032, 63)):
|
|
r += cls.__str[s][(n & j) >> i]
|
|
return r
|
|
|
|
@classmethod
|
|
def generate_result_end(cls, s, e="s4"):
|
|
r = ""
|
|
b = ord(s[120]) << 16
|
|
r += cls.__str[e][(b & 16515072) >> 18]
|
|
r += cls.__str[e][(b & 258048) >> 12]
|
|
r += "=="
|
|
return r
|
|
|
|
@classmethod
|
|
def generate_result(cls, s, n, e="s4"):
|
|
r = ""
|
|
for i in range(n):
|
|
b = ((ord(s[i * 3]) << 16) | (ord(s[i * 3 + 1]))
|
|
<< 8) | ord(s[i * 3 + 2])
|
|
r += cls.generate_result_unit(b, e)
|
|
return r
|
|
|
|
@classmethod
|
|
def generate_args_code(cls):
|
|
a = []
|
|
for j in range(24, -1, -8):
|
|
a.append(cls.__arguments[0] >> j)
|
|
a.append(cls.__arguments[1] / 256)
|
|
a.append(cls.__arguments[1] % 256)
|
|
a.append(cls.__arguments[1] >> 24)
|
|
a.append(cls.__arguments[1] >> 16)
|
|
for j in range(24, -1, -8):
|
|
a.append(cls.__arguments[2] >> j)
|
|
return [int(i) & 255 for i in a]
|
|
|
|
@staticmethod
|
|
def rc4_encrypt(plaintext, key):
|
|
s = list(range(256))
|
|
j = 0
|
|
|
|
# Key Scheduling Algorithm (KSA)
|
|
for i in range(256):
|
|
j = (j + s[i] + ord(key[i % len(key)])) % 256
|
|
s[i], s[j] = s[j], s[i]
|
|
|
|
i = 0
|
|
j = 0
|
|
cipher = []
|
|
|
|
# Pseudo-Random Generation Algorithm (PRGA)
|
|
for k in range(len(plaintext)):
|
|
i = (i + 1) % 256
|
|
j = (j + s[i]) % 256
|
|
s[i], s[j] = s[j], s[i]
|
|
t = (s[i] + s[j]) % 256
|
|
cipher.append(chr(s[t] ^ ord(plaintext[k])))
|
|
|
|
return ''.join(cipher)
|
|
|
|
def get_value(self,
|
|
url_params: dict,
|
|
user_agent: str,
|
|
start_time=0,
|
|
end_time=0,
|
|
random_num_1=None,
|
|
random_num_2=None,
|
|
random_num_3=None,
|
|
) -> str:
|
|
string_1 = self.generate_string_1(
|
|
random_num_1,
|
|
random_num_2,
|
|
random_num_3,
|
|
)
|
|
string_2 = self.generate_string_2(
|
|
urlencode(url_params),
|
|
user_agent,
|
|
start_time,
|
|
end_time,
|
|
)
|
|
string = string_1 + string_2
|
|
return self.generate_result(
|
|
string, 40, "s4") + self.generate_result_end(string, "s4")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
bogus = ABogus()
|
|
USERAGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
|
|
url_str = "https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_online=true&engine_name=Gecko&os_name=Windows&os_version=10&platform=PC&screen_width=1920&screen_height=1080&browser_version=124.0&engine_version=122.0.0.0&cpu_core_num=12&device_memory=8&aweme_id=7345492945006595379"
|
|
# 将url参数转换为字典
|
|
url_params = dict([param.split("=") for param in url_str.split("?")[1].split("&")])
|
|
print(f"URL参数: {url_params}")
|
|
a_bogus = bogus.get_value(url_params, USERAGENT)
|
|
# 使用url编码a_bogus
|
|
a_bogus = quote(a_bogus, safe='')
|
|
print(a_bogus)
|
|
print(USERAGENT)
|