├── .gitattributes ├── requirements.txt ├── README.md ├── LICENSE ├── main.py ├── api.py ├── const.py ├── .gitignore └── utils.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengzonefire/shareLink2bdLink/HEAD/requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shareLink2bdLink 2 | 从百度分享链接生成秒传链接的后端程序 3 | 4 | 代码写的比较简易, 仅供参考, 其他的平台自行实现 5 | 6 | # run 7 | ``` 8 | pip install -r requirements.txt 9 | python main.py 10 | ``` 11 | 12 | * 运行前先使用cookie插件: [Cookie Editor](https://cookie-editor.cgagnier.ca/#download) 导出百度网盘cookie并粘贴到 baidu_cookie.txt 内 13 | 14 | # protocol 15 | 后端默认监听 localhost:9001 (可自行在代码中修改) 16 | 17 | http协议发送json数据 { 'sharelink': 'https://pan.baidu.com/s/xxx, 'pw': 'xxxx' } 到后端 18 | 19 | 后端返回的json数据格式为: { 'errno':错误码, 'msg':错误信息, 'list':[ { 'path':文件路径, 'bdlink':秒传链接, 'errno':错误码, 'msg':错误信息 } ] } 20 | 21 | * errno=0为成功, 此时不会有'msg'字段 22 | 23 | # preview 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 mengzonefire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: mengzonefire 3 | Date: 2023-01-08 01:13:00 4 | LastEditTime: 2023-04-25 21:33:41 5 | LastEditors: mengzonefire 6 | Description: 主函数入口, 创建http服务 7 | """ 8 | 9 | import sys 10 | import const 11 | import traceback 12 | import argparse 13 | from utils import inital, readCookie, write_log 14 | from api import messageHandler 15 | from http.server import ThreadingHTTPServer 16 | 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("-w", "--www", action="store_true") 19 | parser.add_argument("-p", "--port", dest="port", type=int) 20 | args = parser.parse_args() 21 | 22 | 23 | def main(): 24 | print(f"{const.title} by {const.author} {const.version} {const.date}") 25 | inital() 26 | if not readCookie(): 27 | sys.exit() 28 | PORT = args.port or 9001 29 | HOST = "0.0.0.0" if args.www else "localhost" 30 | print("http服务运行在 {}:{}".format(HOST, PORT)) 31 | server = ThreadingHTTPServer((HOST, PORT), messageHandler) 32 | server.serve_forever() 33 | 34 | 35 | if __name__ == "__main__": 36 | try: 37 | main() 38 | except Exception: 39 | traceback.print_exc() # 打印报错信息 40 | write_log( 41 | "msg: {}\nerror_log: {}".format(const.lastMsg, traceback.format_exc()) 42 | ) # 报错信息写入log 43 | raise 44 | -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: mengzonefire 3 | Date: 2023-04-25 19:55:50 4 | LastEditTime: 2023-04-25 21:33:34 5 | LastEditors: mengzonefire 6 | Description: 配置后端对应api的调用 7 | """ 8 | 9 | from json import loads, dumps 10 | from http.server import BaseHTTPRequestHandler 11 | from utils import bdpanHelper 12 | import const 13 | 14 | 15 | class messageHandler(BaseHTTPRequestHandler): 16 | def do_POST(self): 17 | self.send_response(200) 18 | self.send_header("Content-type", "application/x-www-form-urlencoded") 19 | self.send_header("Access-Control-Allow-Origin", "*") 20 | self.send_header("Access-Control-Allow-Methods", "*") 21 | self.send_header("Access-Control-Allow-Headers", "Authorization, Content-Type") 22 | self.end_headers() 23 | req_datas = self.rfile.read(int(self.headers["content-length"])) # 加上限制读取的报文长度 24 | message = req_datas.decode() 25 | const.lastMsg = message 26 | jsonData = loads(message) 27 | print(jsonData) 28 | if "sharelink" in jsonData and "pw" in jsonData: 29 | respon = bdpanHelper( 30 | jsonData["sharelink"], jsonData["pw"] 31 | ).getBdlink() # 单文件分享 32 | if respon["errno"]: 33 | msg = const.default_msg 34 | if respon["errno"] in const.errno_msg: 35 | msg = const.errno_msg[respon["errno"]] 36 | respon["msg"] = msg 37 | self.wfile.write(dumps(respon).encode("utf-8")) 38 | else: 39 | self.wfile.write(dumps({"errno": 114514, "msg": "接口参数错误!"}).encode("utf-8")) 40 | -------------------------------------------------------------------------------- /const.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: mengzonefire 3 | Date: 2023-01-05 14:50:05 4 | LastEditTime: 2023-04-25 21:33:25 5 | LastEditors: mengzonefire 6 | Description: 存放全局常量 7 | """ 8 | 9 | import re 10 | 11 | author = "mengzonefire" 12 | title = "度盘分享链转秒传后端" 13 | version = "v1.0.3" 14 | date = "23.4.25" 15 | 16 | ua_web = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" 17 | ua_dl = "netdisk;" 18 | tpl_url = "https://pan.baidu.com/share/tplconfig?fields=sign,timestamp&channel=chunlei&web=1&app_id=250528&clienttype=0&surl={surl}&logid={logid}" 19 | sharedownload_url = "https://pan.baidu.com/api/sharedownload?channel=chunlei&clienttype=12&web=1&app_id=250528&sign={sign}×tamp={timestamp}" 20 | verify_url = "http://pan.baidu.com/share/verify?app_id=250528&channel=chunlei&clienttype=0&surl={surl}&web=1&bdstoken={bdstoken}" 21 | create_url = "https://pan.baidu.com/api/create" 22 | sharelist_url = "https://pan.baidu.com/share/list?showempty=0&num=10000&channel=chunlei&web=1&app_id=250528&clienttype=0&dir={dir}&logid={logid}&shareid={shareid}&uk={uk}&page={page}" 23 | p_surl = re.compile(r"(s\/1|surl=)([a-zA-Z0-9_-]+)") 24 | p_share_uk = re.compile(r'share_uk:"(\d+)"') 25 | p_shareid = re.compile(r'shareid:"(\d+)"') 26 | p_fileList = re.compile(r'"file_list":\[{(.+?)}\],') 27 | p_bdstoken = re.compile(r"bdstoken:'([a-z\d]{32})'") 28 | p_md5 = re.compile(r"'content-md5': '([\da-f]{32})'", flags=re.IGNORECASE) # 忽略大小写 29 | errno_msg = { 30 | -9: "提取码错误", 31 | 114: "链接格式错误", 32 | 514: "share_uk和shareid获取失败", 33 | 119: "文件已被和谐", 34 | 1919: "链接已失效", 35 | 810: "账号cookie已失效", 36 | 115: "fileList获取失败", 37 | 116: "dlink获取失败, 请尝试更换cookie", 38 | 996: "文件md5获取失败, 请下载后本地生成", 39 | } 40 | default_msg = "未知错误" 41 | cookie_file = "baidu_cookie.txt" 42 | issue_domain = "issuecdn.baidupcs.com" # 用于检测文件是否被和谐 43 | testPath = "/apps/生成秒传测试文件.mengzonefire" 44 | lastMsg = "" # 用于存储服务崩溃时接受的最后一条请求数据 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | .vscode 3 | .history 4 | baidu_cookie.txt 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # poetry 103 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 104 | # This is especially recommended for binary packages to ensure reproducibility, and is more 105 | # commonly ignored for libraries. 106 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 107 | #poetry.lock 108 | 109 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 110 | __pypackages__/ 111 | 112 | # Celery stuff 113 | celerybeat-schedule 114 | celerybeat.pid 115 | 116 | # SageMath parsed files 117 | *.sage.py 118 | 119 | # Environments 120 | .env 121 | .venv 122 | env/ 123 | venv/ 124 | ENV/ 125 | env.bak/ 126 | venv.bak/ 127 | 128 | # Spyder project settings 129 | .spyderproject 130 | .spyproject 131 | 132 | # Rope project settings 133 | .ropeproject 134 | 135 | # mkdocs documentation 136 | /site 137 | 138 | # mypy 139 | .mypy_cache/ 140 | .dmypy.json 141 | dmypy.json 142 | 143 | # Pyre type checker 144 | .pyre/ 145 | 146 | # pytype static type analyzer 147 | .pytype/ 148 | 149 | # Cython debug symbols 150 | cython_debug/ 151 | 152 | # PyCharm 153 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 154 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 155 | # and can be added to the global gitignore or merged into this file. For a more nuclear 156 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 157 | #.idea/ 158 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: mengzonefire 3 | Date: 2023-01-05 14:49:01 4 | LastEditTime: 2023-04-25 21:36:11 5 | LastEditors: mengzonefire 6 | Description: 存放工具函数 7 | """ 8 | 9 | import os 10 | import re 11 | import sys 12 | import base64 13 | import json 14 | import copy 15 | import time 16 | import requests 17 | from urllib.parse import quote, unquote 18 | from const import * 19 | 20 | s = requests.Session() 21 | baidu_cookies_dict = dict() 22 | 23 | """ 24 | description: 初始化步骤 25 | """ 26 | 27 | 28 | def inital(): 29 | exe_path, _ = os.path.split(sys.argv[0]) 30 | if exe_path: 31 | os.chdir(exe_path) # 切换工作目录到可执行文件所在目录 32 | 33 | 34 | def write_log(err): 35 | now_time = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) 36 | with open("./{}.log".format(now_time), "a") as f: 37 | f.write(err) 38 | f.close() 39 | 40 | 41 | def readCookie(): 42 | global logid 43 | if not os.path.isfile(cookie_file): 44 | print( 45 | f"cookie文件{cookie_file}不存在, 请使用浏览器Cookie Editor插件导出度盘cookie, 在程序所在目录创建{cookie_file}并复制进去" 46 | ) 47 | return False 48 | with open(cookie_file, "r") as f: 49 | try: 50 | cookie_json = json.loads(f.read()) 51 | except: 52 | print("cookie格式错误, 请使用浏览器cookie插件导出度盘cookie") 53 | return False 54 | for cookie in cookie_json: 55 | baidu_cookies_dict[cookie["name"]] = cookie["value"] 56 | logid = str( 57 | base64.b64encode(unquote(baidu_cookies_dict["BAIDUID"]).encode("utf-8")), 58 | "utf-8", 59 | ) 60 | return True 61 | 62 | 63 | # def outputCookie(): 64 | # cookie_text = "" 65 | # for key in baidu_cookies_dict: 66 | # cookie_text += f'{key}={baidu_cookies_dict[key]}; ' 67 | # with open('baidu_cookie_output.txt', 'w') as f: 68 | # f.write(cookie_text) 69 | 70 | 71 | """ 72 | description: 解密已加密的md5 73 | param {str} encryptMd5 (加密) 74 | return {str} (解密) 75 | """ 76 | 77 | 78 | def decryptMd5(encryptMd5): 79 | if re.compile(r"[a-f\d]").match(encryptMd5[9]): 80 | return encryptMd5 81 | key = "{:x}".format(ord(encryptMd5[9]) - ord("g")) 82 | key2 = encryptMd5[0:9] + key + encryptMd5[10:] 83 | key3 = "" 84 | for i in range(len(key2)): 85 | key3 += "{:x}".format(int(key2[i], 16) ^ (15 & i)) 86 | md5 = key3[8:16] + key3[0:8] + key3[24:32] + key3[16:24] 87 | return md5 88 | 89 | 90 | """ 91 | description: 从接口元数据解析出正确文件路径 92 | param {*} fileInfo 接口获取的文件/目录数据 93 | return {str} path 文件路径 94 | """ 95 | 96 | 97 | def parseFilePath(fileInfo): 98 | path = str 99 | if "app_id" in fileInfo: 100 | path = fileInfo["path"] if fileInfo["isdir"] else fileInfo["server_filename"] 101 | else: 102 | path = fileInfo["path"] 103 | if path[0] != "/": 104 | path = "/" + path 105 | return path.replace("\u200b", "") 106 | 107 | 108 | class bdpanHelper: 109 | cookie: dict 110 | bdstoken: str 111 | logid: str 112 | extra: str 113 | shareid: int 114 | share_uk: int 115 | url: str 116 | pwd: str 117 | surl: str 118 | 119 | def __init__(self, url: str, pwd: str): 120 | self.url = url 121 | self.pwd = pwd 122 | self.cookie = copy.deepcopy(baidu_cookies_dict) 123 | 124 | def getBdlink(self): 125 | fileInfoList = [] 126 | outputInfoList = [] # [{path:, errno:, bdlink?:,}] 127 | respon = self.verify() 128 | 129 | if 0 == respon["errno"]: 130 | self.cookie["BDCLND"] = respon["randsk"] 131 | self.extra = f'{{"sekey":"{unquote(self.cookie["BDCLND"])}"}}' 132 | respon = self.getFileList() 133 | if 0 == respon["errno"]: 134 | fileInfoList = respon["data"] 135 | else: 136 | return respon 137 | else: 138 | return respon 139 | 140 | for fileInfo in fileInfoList: 141 | if fileInfo["md5"]: 142 | fileInfo["md5"] = decryptMd5(fileInfo["md5"]) 143 | result = self.checkMd5(fileInfo)["errno"] 144 | if 0 == result: 145 | outputInfoList.append( 146 | { 147 | "path": fileInfo["path"], 148 | "bdlink": f"{fileInfo['md5']}#{fileInfo['size']}#{fileInfo['path'][1:]}", 149 | "errno": 0, 150 | } 151 | ) 152 | elif 404 == result: 153 | outputInfoList.append(self.getMd5FromDlink(fileInfo)) 154 | else: 155 | outputInfoList.append({"path": fileInfo["path"], "errno": result}) 156 | 157 | for outputInfo in outputInfoList: 158 | if outputInfo["errno"]: 159 | msg = default_msg 160 | if outputInfo["errno"] in errno_msg: 161 | msg = errno_msg[respon["errno"]] 162 | outputInfo["msg"] = msg 163 | 164 | return {"errno": 0, "list": outputInfoList} 165 | 166 | """ 167 | description: 递归扫描目录下的文件 168 | param {*} dirInfo 单条目录数据, 需包含path 169 | return {*} fileInfoList 文件数据列表 170 | """ 171 | 172 | def scanFile(self, path, page): 173 | fileInfoList = [] 174 | respon = s.get( 175 | sharelist_url.format( 176 | dir=quote(path), 177 | logid=logid, 178 | shareid=self.shareid, 179 | uk=self.share_uk, 180 | page=page, 181 | ), 182 | cookies=self.cookie, 183 | ).text 184 | respon = json.loads(respon) 185 | if 0 == respon["errno"]: 186 | if not len(respon["list"]): 187 | return fileInfoList 188 | else: 189 | for file in respon["list"]: 190 | if file["isdir"]: 191 | fileInfoList += self.scanFile(parseFilePath(file), 1) 192 | else: 193 | fileInfoList += [ 194 | { 195 | "md5": file["md5"].lower(), 196 | "size": file["size"], 197 | "path": parseFilePath(file), 198 | "fs_id": file["fs_id"], 199 | } 200 | ] 201 | fileInfoList += self.scanFile(path, page + 1) 202 | else: 203 | fileInfoList.append({"path": path, "errno": respon["errno"]}) 204 | return fileInfoList 205 | 206 | """ 207 | description: 从分享链接递归读取文件列表数据 208 | param {*} info 接口参数数据dic 209 | return {List} 文件数据列表, 包含md5, size, path, fs_id 210 | """ 211 | 212 | def getFileList(self): 213 | fileInfoList = [] 214 | respon = s.get(self.url, cookies=self.cookie).text 215 | fileList = p_fileList.findall(respon) 216 | if len(fileList): 217 | fileList = json.loads(f"[{{{fileList[0]}}}]") 218 | else: 219 | return {"errno": 115} 220 | for file in fileList: 221 | if file["isdir"]: 222 | fileInfoList += self.scanFile(parseFilePath(file), 1) 223 | else: 224 | fileInfoList += [ 225 | { 226 | "md5": file["md5"].lower(), 227 | "size": file["size"], 228 | "path": parseFilePath(file), 229 | "fs_id": file["fs_id"], 230 | } 231 | ] 232 | return {"errno": 0, "data": fileInfoList} 233 | 234 | """ 235 | description: 验证链接, 获取randsk(密钥)和share_uk,shareid 236 | param {str} url 分享链接 237 | param {str} pwd 提取码 238 | return {*} info 接口参数数据dic 239 | """ 240 | 241 | def verify(self): 242 | result = p_surl.findall(self.url) 243 | if len(result): 244 | self.surl = result[0][1] 245 | else: 246 | return {"errno": 114} 247 | respon = s.get(self.url, cookies=self.cookie).text 248 | if "百度网盘-链接不存在" in respon: 249 | return {"errno": 1919} 250 | _share_uk = p_share_uk.findall(respon) 251 | _shareid = p_shareid.findall(respon) 252 | _bdstoken = p_bdstoken.findall(respon) 253 | if len(_share_uk) and len(_shareid): 254 | self.share_uk = int(_share_uk[0]) 255 | self.shareid = int(_shareid[0]) 256 | else: 257 | return {"errno": 514} 258 | if len(_bdstoken): 259 | self.bdstoken = _bdstoken[0] 260 | else: 261 | return {"errno": 810} 262 | respon = s.post( 263 | verify_url.format(surl=self.surl, bdstoken=self.bdstoken), 264 | headers={ 265 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 266 | "User-Agent": ua_web, 267 | "Referer": self.url, 268 | }, 269 | data={"pwd": self.pwd, "vcode": "", "vcode_str": ""}, 270 | ).text 271 | respon = json.loads(respon) 272 | if 0 == respon["errno"]: 273 | return {"errno": 0, "randsk": respon["randsk"]} 274 | else: 275 | return {"errno": respon["errno"]} 276 | 277 | """ 278 | description: 从dlink(文件下载直链)获取文件md5 279 | param {*} fileInfo 单条文件数据, 需包含md5, size, path, file_id 280 | return {*} 修正md5后的输出文件数据 281 | """ 282 | 283 | def getMd5FromDlink(self, fileInfo): 284 | dlink: str 285 | sign: str 286 | timestamp: int 287 | respon = s.get( 288 | tpl_url.format(surl="1" + self.surl, logid=logid), cookies=self.cookie 289 | ).text 290 | respon = json.loads(respon) 291 | if 0 == respon["errno"]: 292 | sign = respon["data"]["sign"] 293 | timestamp = respon["data"]["timestamp"] 294 | else: 295 | return {"path": fileInfo["path"], "errno": respon["errno"]} 296 | data = { 297 | "extra": self.extra, 298 | "logid": logid, 299 | "fid_list": json.dumps([fileInfo["fs_id"]]), 300 | "primaryid": self.shareid, 301 | "uk": self.share_uk, 302 | "product": "share", 303 | "encrypt": 0, 304 | } 305 | respon = s.post( 306 | sharedownload_url.format(sign=sign, timestamp=timestamp), 307 | cookies=self.cookie, 308 | data=data, 309 | ).text 310 | respon = json.loads(respon) 311 | if 0 == respon["errno"]: 312 | if "list" in respon and len(respon["list"]): 313 | dlink = respon["list"][0]["dlink"] 314 | else: 315 | return {"path": fileInfo["path"], "errno": 116} 316 | else: 317 | return {"path": fileInfo["path"], "errno": respon["errno"]} 318 | respon = s.get(dlink, headers={"Range": "bytes=0-1", "User-Agent": ua_dl}) 319 | if issue_domain in respon.url: 320 | return {"path": fileInfo["path"], "errno": 119} 321 | responHeader = str(respon.headers) 322 | md5 = p_md5.findall(responHeader) 323 | if len(md5): 324 | fileInfo["md5"] = md5[0].lower() 325 | else: 326 | return {"path": fileInfo["path"], "errno": 996} 327 | return { 328 | "path": fileInfo["path"], 329 | "bdlink": f"{fileInfo['md5']}#{fileInfo['size']}#{fileInfo['path']}", 330 | "errno": 0, 331 | } 332 | 333 | """ 334 | description: 验证秒传信息的有效性 335 | param {*} fileInfo 单条文件数据, 需包含md5, size 336 | return {'errno': int} 验证结果 errno=0为验证通过 337 | """ 338 | 339 | def checkMd5(self, fileInfo): 340 | if not fileInfo["md5"]: 341 | return {"errno": 404} 342 | data = { 343 | "block_list": json.dumps([fileInfo["md5"]]), 344 | "size": fileInfo["size"], 345 | "path": testPath, 346 | "isdir": 0, 347 | "rtype": 3, 348 | "is_revision": 1, 349 | } 350 | respon = s.post( 351 | create_url + f"?bdstoken={self.bdstoken}", 352 | cookies=baidu_cookies_dict, 353 | data=data, 354 | ).text 355 | respon = json.loads(respon) 356 | if 0 == respon["errno"]: 357 | return {"errno": 0} 358 | elif 31190 == respon["errno"]: 359 | return {"errno": 404} 360 | else: 361 | return {"errno": respon["errno"]} 362 | 363 | 364 | if __name__ == "__main__": 365 | readCookie() 366 | respon = bdpanHelper( 367 | "https://pan.baidu.com/s/1eShrmBBtKqQCP3zHjIOxkw?pwd=ais4", "ais4" 368 | ).getBdlink() # 模块测试 369 | if 0 == respon["errno"]: 370 | print(respon) 371 | else: 372 | msg = default_msg 373 | if respon["errno"] in errno_msg: 374 | msg = errno_msg[respon["errno"]] 375 | print(f"错误码: {respon['errno']}, 提示: {msg}") 376 | --------------------------------------------------------------------------------