219 lines
7.6 KiB
Python
219 lines
7.6 KiB
Python
"""
|
|
所有和网络请求相关函数。
|
|
|
|
:author: assassing
|
|
:contact: https://github.com/hxz393
|
|
:copyright: Copyright 2024, hxz393. 保留所有权利。
|
|
"""
|
|
import time
|
|
from typing import Union, List, Any
|
|
|
|
import requests
|
|
from retrying import retry
|
|
|
|
from src.constants import HEADERS, BASE_URL
|
|
|
|
|
|
class Network:
|
|
"""
|
|
网络请求相关类。
|
|
注意,此类函数用 retry 来处理网络问题,达到重试次数则由主函数来捕获异常,中断运行。只要请求能返回数据,都可以正确处理。
|
|
请求地址没有取全参数(params),只留关键参数。
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""
|
|
初始化会话、请求头和 bdstoken。
|
|
"""
|
|
self.s = requests.Session()
|
|
self.headers = HEADERS
|
|
self.bdstoken = ''
|
|
# 忽略证书验证警告
|
|
requests.packages.urllib3.disable_warnings()
|
|
|
|
@retry(stop_max_attempt_number=3, wait_random_min=1000, wait_random_max=2000)
|
|
def get_bdstoken(self) -> Union[str, int]:
|
|
"""
|
|
获取 bdstoken,用于创建、转存等操作,是所有其他请求的先决条件。
|
|
获取到的 token 在整个会话中通用。
|
|
|
|
:return: 获取成功返回 bdstoken,获取失败返回错误代码
|
|
"""
|
|
url = f'{BASE_URL}/api/gettemplatevariable'
|
|
params = {
|
|
'clienttype': '0',
|
|
'app_id': '38824127',
|
|
'web': '1',
|
|
'fields': '["bdstoken","token","uk","isdocuser","servertime"]'
|
|
}
|
|
|
|
r = self.s.get(url=url, params=params, headers=self.headers, timeout=10, allow_redirects=False, verify=False)
|
|
if r.json()['errno'] != 0:
|
|
return r.json()['errno']
|
|
|
|
return r.json()['result']['bdstoken']
|
|
|
|
@retry(stop_max_attempt_number=3, wait_random_min=1000, wait_random_max=2000)
|
|
def get_dir_list(self, folder_name: str) -> Union[List[Any], int]:
|
|
"""
|
|
获取指定目录下的文件或目录列表。
|
|
用于创建目录前,检查目录是否已存在;
|
|
用于批量分享时,生成任务列表。
|
|
|
|
:param folder_name: 指定要获取列表的目录名
|
|
:return: 获取成功时返回文件列表,获取失败时返回错误代码
|
|
"""
|
|
url = f'{BASE_URL}/api/list'
|
|
params = {
|
|
'order': 'time',
|
|
'desc': '1',
|
|
'showempty': '0',
|
|
'web': '1',
|
|
'page': '1',
|
|
'num': '1000',
|
|
'dir': folder_name,
|
|
'bdstoken': self.bdstoken
|
|
}
|
|
|
|
r = self.s.get(url=url, params=params, headers=self.headers, timeout=15, allow_redirects=False, verify=False)
|
|
if r.json()['errno'] != 0:
|
|
return r.json()['errno']
|
|
|
|
return r.json()['list']
|
|
|
|
@retry(stop_max_attempt_number=3, wait_random_min=1000, wait_random_max=2000)
|
|
def create_dir(self, folder_name: str) -> int:
|
|
"""
|
|
新建指定目录。
|
|
用于批量转存前,建立缺失的目标目录。
|
|
|
|
:param folder_name: 指定要建立的目录名
|
|
:return: 获取请求返回的代码,成功时返回 0
|
|
"""
|
|
url = f'{BASE_URL}/api/create'
|
|
params = {
|
|
'a': 'commit',
|
|
'bdstoken': self.bdstoken
|
|
}
|
|
data = {
|
|
'path': folder_name,
|
|
'isdir': '1',
|
|
'block_list': '[]',
|
|
}
|
|
|
|
r = self.s.post(url=url, params=params, headers=self.headers, data=data, timeout=15, allow_redirects=False, verify=False)
|
|
|
|
return r.json()['errno']
|
|
|
|
@retry(stop_max_attempt_number=3, wait_random_min=1000, wait_random_max=2000)
|
|
def verify_pass_code(self, link_url: str, pass_code: str) -> Union[str, int]:
|
|
"""
|
|
验证提取码是否正确。
|
|
如果正确,则会返回转存所必须的 randsk,也就是 bdclnd 参数。
|
|
|
|
:param link_url: 网盘地址
|
|
:param pass_code: 提取码
|
|
:return: 成功时返回 randsk 字符串,失败时返回错误代码
|
|
"""
|
|
url = f'{BASE_URL}/share/verify'
|
|
params = {
|
|
# 可放心用暴力切片
|
|
'surl': link_url[25:48],
|
|
'bdstoken': self.bdstoken,
|
|
# 当前时间的毫秒级时间戳
|
|
't': str(int(round(time.time() * 1000))),
|
|
# 下面是不明所以的固定参数
|
|
'channel': 'chunlei',
|
|
'web': '1',
|
|
'clienttype': '0'
|
|
}
|
|
data = {
|
|
'pwd': pass_code,
|
|
# 并没有发现下面两个参数的用途
|
|
'vcode': '',
|
|
'vcode_str': ''
|
|
}
|
|
|
|
r = self.s.post(url=url, params=params, headers=self.headers, data=data, timeout=10, allow_redirects=False, verify=False)
|
|
if r.json()['errno'] != 0:
|
|
return r.json()['errno']
|
|
|
|
return r.json()['randsk']
|
|
|
|
@retry(stop_max_attempt_number=3, wait_random_min=1000, wait_random_max=2000)
|
|
def get_transfer_params(self, url: str) -> str:
|
|
"""
|
|
更新 bdclnd 到 cookie 后,再次请求网盘链接,获取响应内容。
|
|
请求需要允许跳转。
|
|
请求不再需要提取码。
|
|
|
|
:param url: 网盘地址
|
|
:return: 返回原始请求内容,丢给 parse_response 函数取处理
|
|
"""
|
|
r = self.s.get(url=url, headers=self.headers, timeout=15, verify=False)
|
|
|
|
return r.content.decode("utf-8")
|
|
|
|
@retry(stop_max_attempt_number=5, wait_random_min=1000, wait_random_max=2000)
|
|
def transfer_file(self, params_list: List[str], folder_name: str) -> int:
|
|
"""
|
|
转存百度网盘文件。
|
|
|
|
:param params_list: 带有 shareid、share_uk 和 fs_id 的列表
|
|
:param folder_name: 转存目标目录
|
|
:return: 返回转存请求结果代码
|
|
"""
|
|
url = f'{BASE_URL}/share/transfer'
|
|
params = {
|
|
# shareid 是文件 id
|
|
'shareid': params_list[0],
|
|
# share_uk 猜是分享者的 id
|
|
'from': params_list[1],
|
|
'bdstoken': self.bdstoken,
|
|
'channel': 'chunlei',
|
|
'web': '1',
|
|
'clienttype': '0'
|
|
}
|
|
data = {
|
|
# 针对一个分享链接带有多个分享文件的情况,转换一下列表格式
|
|
'fsidlist': f"[{','.join(params_list[2])}]",
|
|
# 目标目录为空,则直接等于根目录 '/'
|
|
'path': f'/{folder_name}'
|
|
}
|
|
|
|
r = self.s.post(url=url, params=params, headers=self.headers, data=data, timeout=30, allow_redirects=False, verify=False)
|
|
|
|
return r.json()['errno']
|
|
|
|
@retry(stop_max_attempt_number=3, wait_random_min=1000, wait_random_max=2000)
|
|
def create_share(self, fs_id: int, expiry: str, password: str) -> Union[str, int]:
|
|
"""
|
|
生成百度网盘分享链接。
|
|
|
|
:param fs_id: 文件或目录独一无二的 id
|
|
:param expiry: 自定义失效时长
|
|
:param password: 自定义提取码
|
|
:return: 成功时返回生成的分享链接,失败时返回错误代码
|
|
"""
|
|
url = f'{BASE_URL}/share/set'
|
|
params = {
|
|
'channel': 'chunlei',
|
|
'bdstoken': self.bdstoken,
|
|
'clienttype': '0',
|
|
'app_id': '250528',
|
|
'web': '1'
|
|
}
|
|
data = {
|
|
'period': expiry,
|
|
'pwd': password,
|
|
'eflag_disable': 'true',
|
|
'channel_list': '[]',
|
|
'schannel': '4',
|
|
'fid_list': f'[{fs_id}]'
|
|
}
|
|
|
|
r = self.s.post(url=url, params=params, headers=self.headers, data=data, timeout=15, allow_redirects=False, verify=False)
|
|
if r.json()['errno'] != 0:
|
|
return r.json()['errno']
|
|
|
|
return r.json()['link'] |