def upload(self, file_list): video = Data() with BiliBili(video) as bili: bili.login(self.persistence_path) for file in file_list: video_part = bili.upload_file(file) # 上传视频 video.videos.append(video_part) # 添加已经上传的视频 video.title = self.data["format_title"] video.desc = '''这个自动录制上传的小程序开源在Github:http://t.cn/RgapTpf(或者在Github搜索ForgQi) 交流群:837362626''' video.source = self.data["url"] # 添加转载地址说明 # 设置视频分区,默认为174 生活,其他分区 tid = engine.config['streamers'][self.principal].get('tid') if tid: video.tid = tid tags = engine.config['streamers'][self.principal].get( 'tags', ['星际争霸2', '电子竞技']) if tags: video.set_tag(tags) img_path = engine.config['streamers'][self.principal].get( 'cover_path') if img_path: video.cover = bili.cover_up(img_path).replace('http:', '') ret = bili.submit() # 提交视频 logger.info(f"上传成功: {ret}") self.remove_filelist(file_list)
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
def upload_file(self, filepath: str): """上传本地视频文件,返回视频信息dict b站目前支持4种上传线路upos, kodo, gcs, bos kodo: {"os":"kodo","query": "bucket=bvcupcdnkodobm&probe_version=20200810", "probe_url":"//up-na0.qbox.me/crossdomain.xml"} gcs: {"os":"gcs","query":"bucket=bvcupcdngcsus&probe_version=20200810", "probe_url":"//storage.googleapis.com/bvcupcdngcsus/OK"}, bos: {"os":"bos","query":"bucket=bvcupcdnboshb&probe_version=20200810", "probe_url":"??"} upos: {"os":"upos","query":"upcdn=ws&probe_version=20200810","probe_url":"//upos-sz-upcdnws.bilivideo.com/OK"} {"os":"upos","query":"upcdn=qn&probe_version=20200810","probe_url":"//upos-sz-upcdnqn.bilivideo.com/OK"} {"os":"upos","query":"upcdn=bda2&probe_version=20200810","probe_url":"//upos-sz-upcdnbda2.bilivideo.com/OK"} """ if not self._auto_os: self._auto_os = self.probe() # self._auto_os = {"os": "kodo", "query": "bucket=bvcupcdnkodobm&probe_version=20200810", # "probe_url": "//up-na0.qbox.me/crossdomain.xml"} logger.info( f"自动线路选择{self._auto_os['os']}: {self._auto_os['query']}. time: {self._auto_os.get('cost')}" ) profile = 'ugcupos/bup' if 'upos' == self._auto_os[ 'os'] else "ugcupos/bupfetch" query = f"r={self._auto_os['os']}&profile={quote(profile, safe='')}" \ f"&ssl=0&version=2.8.9&build=2080900&{self._auto_os['query']}" if self._auto_os['os'] == 'upos': return self.upos(filepath, query) elif self._auto_os['os'] == 'kodo': return self.kodo(filepath, query) 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']}")
def add_videos(self, videopath): formate_title = self.data["format_title"] WebDriverWait(self.driver, 20).until( ec.presence_of_element_located((By.NAME, 'buploader'))) upload = self.driver.find_element_by_name('buploader') # logger.info(driver.title) upload.send_keys(videopath) # send_keys logger.info('开始上传' + formate_title) time.sleep(2) button = r'//*[@class="new-feature-guide-v2-container"]/div/div/div/div/div[1]' if self.is_element_exist(self.driver, button): sb = self.driver.find_element_by_xpath(button) sb.click() sb.click() sb.click() logger.debug('点击') while True: try: info = self.driver.find_elements_by_class_name(r'item-upload-info') for t in info: if t.text != '': print(t.text) time.sleep(10) text = self.driver.find_elements_by_xpath(r'//*[@class="item-upload-info"]/span') aggregate = set() for s in text: if s.text != '': aggregate.add(s.text) print(s.text) if len(aggregate) == 1 and ('Upload complete' in aggregate or '上传完成' in aggregate): break except selenium.common.exceptions.StaleElementReferenceException: logger.exception("selenium.common.exceptions.StaleElementReferenceException") logger.info('上传%s个数%s' % (formate_title, len(info)))
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 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 = '''这个自动录制上传的小程序开源在Github:http://t.cn/RgapTpf(或者在Github搜索ForgQi) 交流群:837362626''' 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() # 提交视频 logger.info(f"上传成功: {ret}") self.remove_filelist(file_list)
def upload(self, file_list): filename = self.persistence_path videopath = self.assemble_videopath(file_list) # service_log_path = "{}/chromedriver.log".format('/home') options = webdriver.ChromeOptions() options.add_argument('headless') self.driver = webdriver.Chrome(executable_path=engine.config.get('chromedriver_path'), chrome_options=options) # service_log_path=service_log_path) try: self.driver.get("https://www.bilibili.com") # driver.delete_all_cookies() if os.path.isfile(filename): with open(filename) as f: new_cookie = json.load(f) for cookie in new_cookie: if isinstance(cookie.get("expiry"), float): cookie["expiry"] = int(cookie["expiry"]) self.driver.add_cookie(cookie) self.driver.get("https://member.bilibili.com/video/upload.html") # print(driver.title) self.add_videos(videopath) # js = "var q=document.getElementsByClassName('content-header-right')[0].scrollIntoView();" # driver.execute_script(js) cookie = self.driver.get_cookies() with open(filename, "w") as f: json.dump(cookie, f) self.add_information() self.driver.find_element_by_xpath('//*[@class="upload-v2-container"]/div[2]/div[3]/div[5]/span[1]').click() # screen_shot = driver.save_screenshot('bin/1.png') # print('截图') time.sleep(3) upload_success = self.driver.find_element_by_xpath(r'//*[@id="app"]/div/div[3]/h3').text if upload_success == '': self.driver.save_screenshot('err.png') logger.info('稿件提交失败,截图记录') return else: logger.info(upload_success) # logger.info('%s提交完成!' % title_) self.remove_filelist(file_list) except selenium.common.exceptions.NoSuchElementException: logger.exception('发生错误') # except selenium.common.exceptions.TimeoutException: # logger.exception('超时') except selenium.common.exceptions.TimeoutException: self.login(filename) finally: self.driver.quit() logger.info('浏览器驱动退出')
def login(self, filename): logger.info('准备更新cookie') # screen_shot = driver.save_screenshot('bin/1.png') WebDriverWait(self.driver, 10).until( ec.presence_of_element_located((By.XPATH, r'//*[@id="login-username"]'))) username = self.driver.find_element_by_xpath(r'//*[@id="login-username"]') username.send_keys(engine.config['user']['account']['username']) password = self.driver.find_element_by_xpath('//*[@id="login-passwd"]') password.send_keys(engine.config['user']['account']['password']) self.driver.find_element_by_class_name("btn-login").click() # logger.info('第四步') # try: cracker = slider_cracker(self.driver) cracker.crack() # except: # logger.exception('出错') time.sleep(5) if self.driver.title == '投稿 - 哔哩哔哩弹幕视频网 - ( ゜- ゜)つロ 乾杯~ - bilibili': cookie = self.driver.get_cookies() print(cookie) with open(filename, "w") as f: json.dump(cookie, f) logger.info('更新cookie成功') else: logger.info('更新cookie失败')
def submit(self): 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) myinfo = self.__session.get( 'https://member.bilibili.com/x/web/archive/pre?lang=cn', timeout=15).json()['data']['myinfo'] myinfo['total_info'] = self.__session.get( 'https://member.bilibili.com/x/web/index/stat', timeout=15).json()['data'] user_weight = 2 if myinfo['level'] > 3 \ and myinfo['total_info'] and myinfo['total_info']['total_fans'] > 100 else 1 if user_weight == 2: logger.info(f'用户权重: {user_weight} => 网页端分p数量不受限制使用网页端api提交') ret = self.__session.post( f'https://member.bilibili.com/x/vu/web/add?csrf={self.__bili_jct}', timeout=5, json=asdict(self.video)).json() if ret["code"] == 0: return ret elif ret["code"] == 21138: logger.info(f'改用客户端接口提交{ret}') else: raise Exception(ret) logger.info(f'用户权重: {user_weight} => 网页端分p数量受到限制使用客户端api端提交') if not self.access_token: self.login_by_password(**engine.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(**engine.config['user']['account']) self.store() continue break if ret["code"] == 0: return ret else: raise Exception(ret)
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=5, 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): """上传本地视频文件,返回视频信息dict b站目前支持4种上传线路upos, kodo, gcs, bos gcs: {"os":"gcs","query":"bucket=bvcupcdngcsus&probe_version=20200810", "probe_url":"//storage.googleapis.com/bvcupcdngcsus/OK"}, bos: {"os":"bos","query":"bucket=bvcupcdnboshb&probe_version=20200810", "probe_url":"??"} """ 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 = { '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, 'name': f.name, 'size': total_size, } ret = self.__session.get( f"https://member.bilibili.com/preupload?{self._auto_os['query']}", params=query, timeout=5) return asyncio.run(upload(f, total_size, ret.json(), tasks=tasks))
def upos(self, filepath, query): total = os.path.getsize(filepath) with open(filepath, 'rb') as f: name = f.name ret = self.__session.get( f'https://member.bilibili.com/preupload?name={quote(name)}&size={total}&{query}', timeout=5).json() 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://', '')}" # 视频上传路径 # 向上传地址申请上传,得到上传id等信息 upload_id = self.__session.post(f'{url}?uploads&output=json', timeout=5, headers={ "X-Upos-Auth": auth }).json()["upload_id"] # 开始上传 parts = [] # 分块信息 chunks = math.ceil(total / chunk_size) # 获取分块数量 start = time.perf_counter() for i in range(chunks): chunks_data = f.read(chunk_size) # 一次读取一个分块大小 uploaded = i * chunk_size + len(chunks_data) self.__session.put( f'{url}?partNumber={i + 1}&uploadId={upload_id}&chunk={i}&chunks={chunks}' f'&size={len(chunks_data)}&start={i * chunk_size}' f'&end={uploaded}&total={total}', timeout=30, data=chunks_data, headers={"X-Upos-Auth": auth}) parts.append({ "partNumber": i + 1, "eTag": "etag" }) # 添加分块信息,partNumber从1开始 # 输出上传进度 cost = time.perf_counter() - start percent = (i + 1) / chunks sys.stdout.write( f"\r{uploaded / 1000 / 1000 / cost:.2f}MB/s => {percent:.1%}" ) logger.info(f'{name} uploaded >> {total / 1000 / 1000 / cost:.2f}MB/s') r = self.__session.post( f'{url}?output=json&name={quote(name)}&profile=ugcupos%2Fbup&uploadId={upload_id}&biz_id={biz_id}', json={ "parts": parts }, headers={ "X-Upos-Auth": auth }, timeout=15).json() if r["OK"] != 1: raise Exception(r) return { "title": splitext(name)[0], "filename": splitext(basename(upos_uri))[0], "desc": "" }