def probe(self): ret = self.__session.get( 'https://member.bilibili.com/preupload?r=probe', timeout=5).json() logger.info(f"线路:{ret['lines']}") data, auto_os = None, None min_cost = 0 if ret['probe'].get('get'): method = 'get' else: method = 'post' data = bytes(int(1024 * 0.1 * 1024)) for line in ret['lines']: start = time.perf_counter() test = self.__session.request(method, f"https:{line['probe_url']}", data=data, timeout=30) cost = time.perf_counter() - start print(line['query'], cost) if test.status_code != 200: return if not min_cost or min_cost > cost: auto_os = line min_cost = cost auto_os['cost'] = min_cost return auto_os
async def kodo(self, file, total_size, ret, chunk_size=4194304, tasks=3): filename = file.name bili_filename = ret['bili_filename'] key = ret['key'] endpoint = f"https:{ret['endpoint']}" token = ret['uptoken'] fetch_url = ret['fetch_url'] fetch_headers = ret['fetch_headers'] url = f'{endpoint}/mkblk' headers = { 'Authorization': f"UpToken {token}", } # 开始上传 parts = [] # 分块信息 chunks = math.ceil(total_size / chunk_size) # 获取分块数量 async def upload_chunk(session, chunks_data, params): async with session.post(f'{url}/{len(chunks_data)}', data=chunks_data, headers=headers) as response: end = time.perf_counter() - start ctx = await response.json() parts.append({"index": params['chunk'], "ctx": ctx['ctx']}) sys.stdout.write( f"\r{params['end'] / 1000 / 1000 / end:.2f}MB/s " f"=> {params['partNumber'] / chunks:.1%}") start = time.perf_counter() await self._upload({}, file, chunk_size, upload_chunk, tasks=tasks) cost = time.perf_counter() - start logger.info( f'{filename} uploaded >> {total_size / 1000 / 1000 / cost:.2f}MB/s' ) parts.sort(key=lambda x: x['index']) self.__session.post( f"{endpoint}/mkfile/{total_size}/key/{base64.urlsafe_b64encode(key.encode()).decode()}", data=','.join(map(lambda x: x['ctx'], parts)), headers=headers, timeout=10) r = self.__session.post(f"https:{fetch_url}", headers=fetch_headers, timeout=5).json() if r["OK"] != 1: raise Exception(r) return { "title": splitext(filename)[0], "filename": bili_filename, "desc": "" }
def submit_client(self): logger.info('使用客户端api端提交') if not self.access_token: self.login_by_password(**config['user']['account']) self.store() while True: ret = self.__session.post( f'http://member.bilibili.com/x/vu/client/add?access_key={self.access_token}', timeout=5, json=asdict(self.video)).json() if ret['code'] == -101: logger.info(f'刷新token{ret}') self.login_by_password(**config['user']['account']) self.store() continue return ret
def upload(self, file_list): video = Data() with BiliBili(video) as bili: bili.login(self.persistence_path, self.user) for file in file_list: video_part = bili.upload_file(file, self.lines, self.threads) # 上传视频 video.videos.append(video_part) # 添加已经上传的视频 video.title = self.data["format_title"] video.desc = self.desc + ',喜欢点赞!' video.copyright = self.copyright if self.copyright == 2: video.source = self.data["url"] # 添加转载地址说明 # 设置视频分区,默认为174 生活,其他分区 video.tid = self.tid video.set_tag(self.tags) if self.cover_path: video.cover = bili.cover_up(self.cover_path).replace( 'http:', '') ret = bili.submit(self.submit_api) # 提交视频 logger.info(f"上传成功: {ret}") return file_list
def submit(self, submit_api=None): if not self.video.title: self.video.title = self.video.videos[0]["title"] self.__session.get('https://member.bilibili.com/x/geetest/pre/add', timeout=5) if submit_api is None: myinfo = self.__session.get( 'https://member.bilibili.com/x/web/archive/pre?lang=cn', timeout=15).json()['data']['myinfo'] total_info = self.__session.get( 'https://member.bilibili.com/x/web/index/stat', timeout=15).json() if total_info.get('data') is None: logger.error(total_info) myinfo['total_info'] = total_info.get('data') if myinfo['level'] > 3 and myinfo[ 'total_info'] and myinfo['total_info']['total_fans'] > 100: user_weight = 2 else: user_weight = 1 logger.info(f'用户权重: {user_weight}') submit_api = 'web' if user_weight == 2 else 'client' ret = None if submit_api == 'web': ret = self.submit_web() if ret["code"] == 21138: logger.info(f'改用客户端接口提交{ret}') submit_api = 'client' if submit_api == 'client': ret = self.submit_client() if not ret: raise Exception(f'不存在的选项:{submit_api}') if ret["code"] == 0: return ret else: raise Exception(ret)
def submit_web(self): logger.info('使用网页端api提交') return self.__session.post( f'https://member.bilibili.com/x/vu/web/add?csrf={self.csrf}', timeout=5, json=asdict(self.video)).json()
async def upos(self, file, total_size, ret, tasks=3): filename = file.name chunk_size = ret['chunk_size'] auth = ret["auth"] endpoint = ret["endpoint"] biz_id = ret["biz_id"] upos_uri = ret["upos_uri"] url = f"https:{endpoint}/{upos_uri.replace('upos://', '')}" # 视频上传路径 headers = {"X-Upos-Auth": auth} # 向上传地址申请上传,得到上传id等信息 upload_id = self.__session.post(f'{url}?uploads&output=json', timeout=30, headers=headers).json()["upload_id"] # 开始上传 parts = [] # 分块信息 chunks = math.ceil(total_size / chunk_size) # 获取分块数量 async def upload_chunk(session, chunks_data, params): async with session.put(url, params=params, raise_for_status=True, data=chunks_data, headers=headers): end = time.perf_counter() - start parts.append({ "partNumber": params['chunk'] + 1, "eTag": "etag" }) sys.stdout.write( f"\r{params['end'] / 1000 / 1000 / end:.2f}MB/s " f"=> {params['partNumber'] / chunks:.1%}") start = time.perf_counter() await self._upload( { 'uploadId': upload_id, 'chunks': chunks, 'total': total_size }, file, chunk_size, upload_chunk, tasks=tasks) cost = time.perf_counter() - start p = { 'name': filename, 'uploadId': upload_id, 'biz_id': biz_id, 'output': 'json', 'profile': 'ugcupos/bup' } r = self.__session.post(url, params=p, json={ "parts": parts }, headers=headers, timeout=15).json() logger.info( f'{filename} uploaded >> {total_size / 1000 / 1000 / cost:.2f}MB/s. {r}' ) if r.get('OK') != 1: raise Exception(r) return { "title": splitext(filename)[0], "filename": splitext(basename(upos_uri))[0], "desc": "" }
def upload_file(self, filepath: str, lines='AUTO', tasks=3): if not self._auto_os: self._auto_os = self.probe() if lines == 'kodo': self._auto_os = { "os": "kodo", "query": "bucket=bvcupcdnkodobm&probe_version=20200810", "probe_url": "//up-na0.qbox.me/crossdomain.xml" } if lines == 'bda2': self._auto_os = { "os": "upos", "query": "upcdn=bda2&probe_version=20200810", "probe_url": "//upos-sz-upcdnbda2.bilivideo.com/OK" } if lines == 'ws': self._auto_os = { "os": "upos", "query": "upcdn=ws&probe_version=20200810", "probe_url": "//upos-sz-upcdnws.bilivideo.com/OK" } if lines == 'qn': self._auto_os = { "os": "upos", "query": "upcdn=qn&probe_version=20200810", "probe_url": "//upos-sz-upcdnqn.bilivideo.com/OK" } logger.info( f"线路选择 => {self._auto_os['os']}: {self._auto_os['query']}. time: {self._auto_os.get('cost')}" ) if self._auto_os['os'] == 'upos': upload = self.upos elif self._auto_os['os'] == 'kodo': upload = self.kodo elif self._auto_os['os'] == "gcs": raise NotImplementedError('gcs') elif self._auto_os['os'] == "bos": raise NotImplementedError('bos') else: logger.error(f"NoSearch:{self._auto_os['os']}") raise NotImplementedError(self._auto_os['os']) total_size = os.path.getsize(filepath) with open(filepath, 'rb') as f: query = { 'name': f.name, 'size': int(total_size), 'r': self._auto_os['os'], 'profile': 'ugcupos/bup' if 'upos' == self._auto_os['os'] else "ugcupos/bupfetch", 'ssl': 0, 'version': '2.8.12', 'build': 2081200, } self.__session.headers.update({ 'authority': 'member.bilibili.com', 'sec-ch-ua': '"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"', 'sec-ch-ua-mobile': '?0', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36', 'accept': '*/*', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://member.bilibili.com/video/upload.html', 'accept-language': 'zh-CN,zh;q=0.9', 'cookie': f'buvid3={self.buvid3}; SESSDATA={self.sessdata}; bili_jct={self.csrf}' }) res = self.__session.get( f"https://member.bilibili.com/preupload?{self._auto_os['query']}", params=query, timeout=5) if res.status_code != 200: raise Exception(res) else: print(res) return asyncio.run(upload(f, total_size, res.json(), tasks=tasks))