def make_progress() -> Progress: _time = 0.0 def fake_time(): nonlocal _time try: return _time finally: _time += 1 console = Console( file=io.StringIO(), force_terminal=True, color_system="truecolor", width=80, legacy_windows=False, ) progress = Progress(console=console, get_time=fake_time, auto_refresh=False) task1 = progress.add_task("foo") task2 = progress.add_task("bar", total=30) progress.advance(task2, 16) task3 = progress.add_task("baz", visible=False) task4 = progress.add_task("egg") progress.remove_task(task4) task4 = progress.add_task("foo2", completed=50, start=False) progress.stop_task(task4) progress.start_task(task4) progress.update( task4, total=200, advance=50, completed=200, visible=True, refresh=True ) progress.stop_task(task4) return progress
def run(self, args): # Create a progress bar for the download progress = Progress( TextColumn("[bold cyan]{task.fields[filename]}", justify="right"), BarColumn(bar_width=None), "[progress.percentage]{task.percentage:>3.1f}%", "•", DownloadColumn(), "•", TransferSpeedColumn(), "•", TimeRemainingColumn(), ) if not args.destination: args.destination = f"./{os.path.basename(args.source)}" else: access = pwncat.victim.access(args.destination) if Access.DIRECTORY in access: args.destination = os.path.join(args.destination, os.path.basename(args.source)) elif Access.PARENT_EXIST not in access: console.log( f"[cyan]{args.destination}[/cyan]: no such file or directory" ) return try: length = os.path.getsize(args.source) started = time.time() with progress: task_id = progress.add_task("upload", filename=args.destination, total=length, start=False) with open(args.source, "rb") as source: with pwncat.victim.open(args.destination, "wb", length=length) as destination: progress.start_task(task_id) copyfileobj( source, destination, lambda count: progress.update(task_id, advance=count), ) elapsed = time.time() - started console.log(f"uploaded [cyan]{human_readable_size(length)}[/cyan] " f"in [green]{human_readable_delta(elapsed)}[/green]") except (FileNotFoundError, PermissionError, IsADirectoryError) as exc: self.parser.error(str(exc))
def format_markdown(path): import re from rich.progress import Progress from .. import user_root rt_path = dir_char.join(os.path.abspath(path).split(dir_char)[:-1]) + dir_char img_dict = {} with open(path, 'r') as fp: ct = fp.read() aims = re.findall('!\[.*?]\((.*?)\)', ct, re.M) + re.findall('<img.*?src="(.*?)".*?>', ct, re.M) progress = Progress(console=qs_default_console) pid = progress.add_task(' Upload' if user_lang != 'zh' else ' 上传', total=len(aims)) progress.start() progress.start_task(pid) for aim in aims: if aim.startswith('http'): # Uploaded qs_default_console.print(qs_warning_string, aim, 'is not a local file' if user_lang != 'zh' else '非本地文件') progress.advance(pid, 1) continue raw_path = aim aim = aim.replace('~', user_root) aim = aim if aim.startswith(dir_char) else get_path(rt_path, aim) if aim not in img_dict: qs_default_console.print(qs_info_string, 'Start uploading:' if user_lang != 'zh' else '正在上传:', aim) res_dict = post_img(aim) if not res_dict: res_table.add_row(aim.split(dir_char)[-1], 'No File' if user_lang != 'zh' else '无文件', '') img_dict[aim] = False else: try: res_table.add_row( aim.split(dir_char)[-1], str(res_dict['code']), res_dict['msg'] if res_dict['code'] != 200 else ( res_dict['data']['url'] if res_dict['data']['url'] else plt_type + ' failed') ) img_dict[aim] = res_dict['data']['url'] if res_dict['code'] == 200 else False except Exception: qs_default_console.print(qs_error_string, res_dict) res_table.add_row(aim.split(dir_char)[-1], str(res_dict['code']), res_dict['msg']) img_dict[aim] = False if img_dict[aim]: qs_default_console.print(qs_info_string, 'replacing img:' if user_lang != 'zh' else '替换路径', f'"{raw_path}" with "{img_dict[aim]}"') ct = ct.replace(raw_path, img_dict[aim]) progress.advance(pid, 1) progress.stop() with open(path, 'w') as fp: fp.write(ct) qs_default_console.print(res_table, justify="center")
def _download(url: str, root: str): os.makedirs(root, exist_ok=True) filename = os.path.basename(url) download_target = os.path.join(root, filename) if os.path.isfile(download_target): return download_target if os.path.exists(download_target) and not os.path.isfile(download_target): raise FileExistsError(f'{download_target} exists and is not a regular file') from rich.progress import ( DownloadColumn, Progress, TextColumn, TimeRemainingColumn, TransferSpeedColumn, ) progress = Progress( TextColumn("[bold blue]{task.fields[filename]}", justify="right"), "[progress.percentage]{task.percentage:>3.1f}%", "•", DownloadColumn(), "•", TransferSpeedColumn(), "•", TimeRemainingColumn(), ) with progress: task = progress.add_task('download', filename=url, start=False) with urllib.request.urlopen(url) as source, open( download_target, 'wb' ) as output: progress.update(task, total=int(source.info().get('Content-Length'))) progress.start_task(task) while True: buffer = source.read(8192) if not buffer: break output.write(buffer) progress.update(task, advance=len(buffer)) return download_target
async def advance_progress(self, data: Dict, progress: Progress) -> None: """advances the progress bar for the given tasks by the current simulation progress""" simulation_id = data["data"]["simulation_id"] sim_progress = data["data"]["progress"] progress_task = self.tasks.get(int(simulation_id)) if not progress_task.task.started: progress.start_task(progress_task.task.id) # progress.console.print(f">>>>>>>>>> {sim_progress}") if int(sim_progress) <= progress_task.task.percentage: return advance_by = int(sim_progress) - int(progress_task.task.percentage) progress.update(progress_task.task.id, advance=advance_by) # auto_refresh is False so do this manually progress.refresh()
def run(self, manager: "pwncat.manager.Manager", args): # Create a progress bar for the download progress = Progress( TextColumn("[bold cyan]{task.fields[filename]}", justify="right"), BarColumn(bar_width=None), "[progress.percentage]{task.percentage:>3.1f}%", "•", DownloadColumn(), "•", TransferSpeedColumn(), "•", TimeRemainingColumn(), ) if not args.destination: args.destination = f"./{os.path.basename(args.source)}" try: length = os.path.getsize(args.source) started = time.time() with progress: task_id = progress.add_task("upload", filename=args.destination, total=length, start=False) with open(args.source, "rb") as source: with manager.target.platform.open(args.destination, "wb") as destination: progress.start_task(task_id) copyfileobj( source, destination, lambda count: progress.update(task_id, advance=count), ) progress.update(task_id, filename="draining buffers...") progress.stop_task(task_id) progress.start_task(task_id) progress.update(task_id, filename=args.destination) elapsed = time.time() - started console.log(f"uploaded [cyan]{human_readable_size(length)}[/cyan] " f"in [green]{human_readable_delta(elapsed)}[/green]") except (FileNotFoundError, PermissionError, IsADirectoryError) as exc: self.parser.error(str(exc))
def make_progress() -> Progress: console = Console(file=io.StringIO(), force_terminal=True) progress = Progress(console=console) task1 = progress.add_task("foo") task2 = progress.add_task("bar", 30) progress.advance(task2, 16) task3 = progress.add_task("baz", visible=False) task4 = progress.add_task("egg") progress.remove_task(task4) task4 = progress.add_task("foo2", completed=50, start=False) progress.start_task(task4) progress.update( task4, total=200, advance=50, completed=200, visible=True, refresh=True ) return progress
class Run: def __init__(self, all_users: bool): self.all_users = all_users self.progress = Progress( '[progress.description]{task.description}', BarColumn(), '{task.completed}/{task.total}', ) @logger.catch def signin_loop(self): with self.progress: if self.all_users: users = AccountsManager.get_all_accounts() else: users = AccountsManager.get_active_accounts() if not users: sys.exit( 'No active users found. Please configure your users first.' ) task_id = self.progress.add_task('signin_check', total=len(users)) self.progress.start_task(task_id) user: Account for user in users: self.progress.update(task_id, description=f'{user.name}: sign-in...') ct8_user = CT8(user.name, user.password) result = ct8_user.sign_in_request() ct8_user.update_expiration_date() self.progress.console.print(result) self.progress.advance(task_id) self.progress.update(task_id, description=':100: Completed!') @logger.catch def main(self): self.signin_loop()
async def update_status(self, data: Dict, progress: Progress): status = data["data"]["status"] simulation_id = data["data"]["simulation_id"] progress_task = self.tasks.get(int(simulation_id)) if self.is_live_finished(status, progress_task): status = "finished" status_txt = status color = self.STATUS_COLORS.get(status) if color: status_txt = f"[bold {color}]{status_txt}[/bold {color}]" update_kwargs = {"status": status_txt} advance_by = 100 if status in ("finished", "ended", "postprocessing") else None if advance_by and not progress_task.task.started: progress.start_task(progress_task.task.id) update_kwargs.update({"advance": advance_by}) progress.update(progress_task.task.id, **update_kwargs) # auto_refresh is False so do this manually progress.refresh() progress_task.status = status
class Downloader: def __init__(self, args: CmdArgs): self.logger = logging.getLogger('downloader') self.args = args self.exit = False # <---来自命令行的设置---> self.max_concurrent_downloads = 1 # <---进度条---> self.progress = Progress( TextColumn("[bold blue]{task.fields[name]}", justify="right"), BarColumn(bar_width=None), "[progress.percentage]{task.percentage:>3.2f}%", "•", DownloadColumn(binary_units=True), "•", TransferSpeedColumn(), "•", TimeRemainingColumn(), ) self.terminate = False signal.signal(signal.SIGINT, self.stop) signal.signal(signal.SIGTERM, self.stop) def stop(self, signum: int, frame): self.terminate = True def get_conn(self): ''' connector在一个ClientSession使用后可能就会关闭 若需要再次使用则需要重新生成 ''' return TCPConnector( ttl_dns_cache=300, limit_per_host=self.args.limit_per_host, limit=500, force_close=not self.args.disable_force_close, enable_cleanup_closed=not self.args.disable_force_close) def daemon(self): ''' 一直循环调度下载和更新进度 ''' if self.args.repl is False: self.download_stream() return while self.exit: break def download_stream(self): extractor = Extractor(self.args) streams = extractor.fetch_metadata(self.args.URI[0]) loop = get_event_loop() loop.run_until_complete(self.download_all_segments(loop, streams)) loop.close() def get_selected_index(self, length: int) -> list: selected = [] try: text = input('请输入要下载流的序号:').strip() except EOFError: click.secho('未选择流,退出') return [] if text == '': return [index for index in range(length + 1)] elif text.isdigit(): return [int(text)] elif '-' in text and len(text.split('-')) == 2: start, end = text.split('-') if start.strip().isdigit() and end.strip().isdigit(): return [ index for index in range(int(start.strip()), int(end.strip()) + 1) ] elif text.replace(' ', '').isdigit(): for index in text.split(' '): if index.strip().isdigit(): if int(index.strip()) <= length: selected.append(int(index)) return selected elif text.replace(',', '').replace(' ', '').isdigit(): for index in text.split(','): if index.strip().isdigit(): if int(index.strip()) <= length: selected.append(int(index)) return selected return selected async def download_all_segments(self, loop: AbstractEventLoop, streams: List[Stream]): if streams is None: return if len(streams) == 0: return for index, stream in enumerate(streams): stream.show_info(index) if self.args.select is True: selected = self.get_selected_index(len(streams)) else: selected = [index for index in range(len(streams) + 1)] all_results = [] for index, stream in enumerate(streams): if self.terminate is True: break if index not in selected: continue click.secho(f'{stream.get_name()} download start.') stream.dump_segments() max_failed = 5 while max_failed > 0: results = await self.do_with_progress(loop, stream) all_results.append(results) count_none, count_true, count_false = 0, 0, 0 for _, flag in results.items(): if flag is True: count_true += 1 elif flag is False: count_false += 1 else: count_none += 1 # 出现False则说明无法下载 if count_false > 0: break # False 0 出现None则说明需要继续下载 否则合并 if count_none > 0: max_failed -= 1 continue else: # if stream.stream_type == 'text': # # mpd中text类型 一般是字幕直链 跳过合并 # pass if self.args.disable_auto_concat is False: stream.concat(self.args) break return all_results def get_left_segments(self, stream: Stream): completed = 0 _left_segments = [] for segment in stream.segments: segment_path = segment.get_path() if segment_path.exists() is True: # 文件落盘 说明下载一定成功了 if segment_path.stat().st_size == 0: segment_path.unlink() else: completed += segment_path.stat().st_size continue _left_segments.append(segment) return completed, _left_segments def init_progress(self, stream: Stream, completed: int): stream_id = self.progress.add_task("download", name=stream.get_name(), start=False) # TaskID if completed > 0: if stream.filesize > 0: total = stream.filesize else: total = completed stream.filesize = total self.progress.update(stream_id, completed=completed, total=total) else: if stream.filesize > 0: total = stream.filesize else: total = 0 stream.filesize = total self.progress.update(stream_id, total=total) return stream_id async def do_with_progress(self, loop: AbstractEventLoop, stream: Stream): ''' 下载过程输出进度 并合理处理异常 ''' results = {} # type: Dict[bool] tasks = set() # type: Set[Task] def _done_callback(_future: Future) -> None: nonlocal results if _future.exception() is None: segment, status, flag = _future.result() if flag is None: pass # print('下载过程中出现已知异常 需重新下载\n') elif flag is False: # 某几类已知异常 如状态码不对 返回头没有文件大小 视为无法下载 主动退出 cancel_all_task() if status in ['STATUS_CODE_ERROR', 'NO_CONTENT_LENGTH']: print(f'无法下载的m3u8 {status} 退出其他下载任务\n') elif status == 'EXIT': pass else: print(f'出现未知status -> {status} 退出其他下载任务\n') results[segment] = flag else: # 出现未知异常 强制退出全部task print('出现未知异常 强制退出全部task\n') cancel_all_task() results['未知segment'] = False def cancel_all_task() -> None: for task in tasks: task.remove_done_callback(_done_callback) for task in filter(lambda task: not task.done(), tasks): task.cancel() # limit_per_host 根据不同网站和网络状况调整 如果与目标地址连接性较好 那么设置小一点比较好 completed, _left = self.get_left_segments(stream) if len(_left) == 0: return results # 没有需要下载的则尝试合并 返回False则说明需要继续下载完整 with self.progress: stream_id = self.init_progress(stream, completed) client = ClientSession( connector=self.get_conn()) # type: ClientSession for segment in _left: task = loop.create_task( self.download(client, stream_id, stream, segment)) task.add_done_callback(_done_callback) tasks.add(task) # 阻塞并等待运行完成 finished, unfinished = await asyncio.wait(tasks) # 关闭ClientSession await client.close() return results async def download(self, client: ClientSession, stream_id: TaskID, stream: Stream, segment: Segment): proxy, headers = self.args.proxy, self.args.headers status, flag = 'EXIT', True try: async with client.get( segment.url, proxy=proxy, headers=headers) as resp: # type: ClientResponse if resp.status == 405: status = 'STATUS_CODE_ERROR' flag = False elif resp.headers.get('Content-length') is None: status = 'NO_CONTENT_LENGTH' flag = False else: stream.filesize += int(resp.headers["Content-length"]) self.progress.update(stream_id, total=stream.filesize) self.progress.start_task(stream_id) while self.terminate is False: data = await resp.content.read(512) if not data: break segment.content.append(data) self.progress.update(stream_id, advance=len(data)) except TimeoutError: return segment, 'TimeoutError', None except client_exceptions.ClientConnectorError: return segment, 'ClientConnectorError', None except client_exceptions.ClientPayloadError: return segment, 'ClientPayloadError', None except client_exceptions.ClientOSError: return segment, 'ClientOSError', None except CancelledError: return segment, 'EXIT', False except Exception as e: self.logger.error(f'! -> {segment.url}', exc_info=e) return segment, status, False if self.terminate: return segment, 'EXIT', False if flag is False: return segment, status, False return segment, 'SUCCESS', await self.decrypt(segment) async def decrypt(self, segment: Segment) -> bool: ''' 解密部分 ''' if segment.is_encrypt() and segment.is_supported_encryption(): cipher = CommonAES(segment.xkey.key, binascii.a2b_hex(segment.xkey.iv)) return cipher.decrypt(segment) else: return segment.dump()
from rich.panel import Panel from rich.progress import Progress JOBS = [100, 150, 25, 70, 110, 90] progress = Progress(auto_refresh=False) master_task = progress.add_task("overall", total=sum(JOBS)) jobs_task = progress.add_task("jobs") progress.console.print( Panel( "[bold blue]A demonstration of progress with a current task and overall progress.", padding=1, ) ) with progress: for job_no, job in enumerate(JOBS): progress.log(f"Starting job #{job_no}") sleep(0.2) progress.reset(jobs_task, total=job, description=f"job [bold yellow]#{job_no}") progress.start_task(jobs_task) for wait in progress.track(range(job), task_id=jobs_task): sleep(0.01) progress.advance(master_task, job) progress.log(f"Job #{job_no} is complete") progress.log( Panel(":sparkle: All done! :sparkle:", border_style="green", padding=1) )
class DeviantArtDownloader: def __init__(self, client_id, client_secret): self.api = Api(client_id, client_secret) self.progress = Progress( BarColumn(bar_width=None), "[progress.percentage]{task.percentage:>3.1f}%", DownloadColumn(), TransferSpeedColumn(), "[bold blue]{task.fields[filename]}", ) self.all_t = self.progress.add_task('All', filename='All', start=0) self.total_length = 0; def download_worker(self, task_id, url, path): with open(path, 'wb') as f, GET(url, stream=True) as rq: length = int(rq.headers.get('Content-Length', 0)) self.progress.start_task(task_id) self.progress.update(task_id, total=length) self.total_length += length self.progress.update(self.all_t, total=self.total_length) for chunk in rq.iter_content(chunk_size=4096): f.write(chunk) self.progress.update(task_id, advance=len(chunk)) self.progress.update(self.all_t, advance=len(chunk)) return task_id def search_content(self, tag, max_items=-1): n_items = 0 offset = 0 while True: data = self.api.browse('tags', tag=tag, offset=offset) for item in data['results']: yield item n_items += 1 if n_items > max_items and max_items > 0: return if not data['has_more']: break offset = data['next_offset'] @staticmethod def _make_filename(item): src = item.content['src'] ext = splitext(urlparse(src).path)[1] return splitpath(item.url)[1] + ext def download(self, tag, out_dir='.', max_items=-1, max_workers=8, list_path=None): if not exists(out_dir): mkdir(out_dir) with self.progress, ThreadPoolExecutor(max_workers=max_workers) as pool: self.progress.start_task(self.all_t) futures = [] for item in self.search_content(tag, max_items): if list_path: with open(list_path, 'a') as flist: flist.write(item.url + '\n') if not item.content: continue filename = join(out_dir, self._make_filename(item)) task_id = self.progress.add_task( 'download', filename=item.title, start=0) url = item.content['src'] f = pool.submit(self.download_worker, task_id, url, filename) futures.append(f) while len(futures) >= max_workers: for f in futures: if f.done(): futures.remove(f) self.progress.remove_task(f.result()) sleep(0.1)
class Downloader: def __init__(self): self.exit = False # <---来自命令行的设置---> self.max_concurrent_downloads = 1 # <---进度条---> self.progress = Progress( TextColumn("[bold blue]{task.fields[name]}", justify="right"), BarColumn(bar_width=None), "[progress.percentage]{task.percentage:>3.2f}%", "•", DownloadColumn(), "•", TransferSpeedColumn(), "•", TimeRemainingColumn(), ) def daemon(self, args: Namespace): ''' 一直循环调度下载和更新进度 ''' if args.repl is False: self.download_one_stream(args) # click.secho('Download end.') return while self.exit: break def download_one_stream(self, args: Namespace): extractor = Extractor(args) streams = extractor.fetch_metadata(args.URI[0]) loop = get_event_loop() loop.run_until_complete(self.download_all_segments(loop, streams)) loop.close() async def download_all_segments(self, loop: AbstractEventLoop, streams: List[Stream]): for index, stream in enumerate(streams): stream.show_info(index) all_results = [] for stream in streams: click.secho(f'{stream.name} download start.') max_failed = 5 while max_failed > 0: results = await self.do_with_progress(loop, stream) all_results.append(results) count_none, count_true, count_false = 0, 0, 0 for _, flag in results.items(): if flag is True: count_true += 1 elif flag is False: count_false += 1 else: count_none += 1 # 出现False则说明无法下载 if count_false > 0: break # False 0 出现None则说明需要继续下载 否则合并 if count_none > 0: max_failed -= 1 continue else: stream.concat() break return all_results def get_left_segments(self, stream: Stream): completed = 0 _left_segments = [] for segment in stream.segments: segment_path = segment.get_path() if segment_path.exists() is True: # 文件落盘 说明下载一定成功了 if segment_path.stat().st_size == 0: segment_path.unlink() else: completed += segment_path.stat().st_size continue _left_segments.append(segment) return completed, _left_segments def init_progress(self, stream: Stream, completed: int): stream_id = self.progress.add_task("download", name=stream.name, start=False) # TaskID if completed > 0: if stream.filesize > 0: total = stream.filesize else: total = completed stream.filesize = total self.progress.update(stream_id, completed=completed, total=total) else: if stream.filesize > 0: total = stream.filesize else: total = 0 stream.filesize = total self.progress.update(stream_id, total=total) return stream_id async def do_with_progress(self, loop: AbstractEventLoop, stream: Stream): ''' 下载过程输出进度 并合理处理异常 ''' results = {} # type: Dict[bool] tasks = set() # type: Set[Task] def _done_callback(_future: Future) -> None: nonlocal results if _future.exception() is None: segment, status, flag = _future.result() if flag is None: print('下载过程中出现已知异常 需重新下载\n') elif flag is False: # 某几类已知异常 如状态码不对 返回头没有文件大小 视为无法下载 主动退出 if status in ['STATUS_CODE_ERROR', 'NO_CONTENT_LENGTH']: # print('无法下载的m3u8 退出其他下载任务\n') cancel_all_task() else: print(f'出现未知status -> {status} 退出其他下载任务\n') cancel_all_task() results[segment] = flag else: # 出现未知异常 强制退出全部task print('出现未知异常 强制退出全部task\n') cancel_all_task() results[segment] = False def cancel_all_task() -> None: for task in tasks: task.remove_done_callback(_done_callback) for task in filter(lambda task: not task.done(), tasks): task.cancel() completed, _left = self.get_left_segments(stream) if len(_left) == 0: return results # 没有需要下载的则尝试合并 返回False则说明需要继续下载完整 with self.progress: stream_id = self.init_progress(stream, completed) connector = TCPConnector(ttl_dns_cache=300, limit_per_host=4, limit=100, force_close=True, enable_cleanup_closed=True) for segment in _left: task = loop.create_task( self.download(connector, stream_id, stream, segment)) task.add_done_callback(_done_callback) tasks.add(task) finished, unfinished = await asyncio.wait(tasks) # 阻塞并等待运行完成 return results async def download(self, connector: TCPConnector, stream_id: TaskID, stream: Stream, segment: Segment): status, flag = 'EXIT', True try: async with request('GET', segment.url, connector=connector, headers=segment.headers) as response: if response.status == 405: status = 'STATUS_CODE_ERROR' flag = False elif response.headers.get('Content-length') is None: status = 'NO_CONTENT_LENGTH' flag = False else: stream.filesize += int(response.headers["Content-length"]) self.progress.update(stream_id, total=stream.filesize) self.progress.start_task(stream_id) while True: data = await response.content.read(512) if not data: break segment.content.append(data) self.progress.update(stream_id, advance=len(data)) except ClientConnectorError: return segment, 'ClientConnectorError', None except ClientPayloadError: return segment, 'ClientPayloadError', None except ConnectionResetError: return segment, 'ConnectionResetError', None except Exception: # print(e, f'{status}\n') return segment, status, False if flag is False: return segment, status, False return segment, 'SUCCESS', await self.decrypt(segment) async def decrypt(self, segment: Segment) -> bool: ''' 解密部分 ''' if segment.is_encrypt(): cipher = CommonAES(segment.xkey.key, binascii.a2b_hex(segment.xkey.iv)) return cipher.decrypt(segment) else: return segment.dump()