Example #1
0
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
Example #2
0
    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))
Example #3
0
    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")
Example #4
0
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
Example #5
0
 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()
Example #6
0
    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))
Example #7
0
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
Example #8
0
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()
Example #9
0
 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
Example #10
0
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()
Example #11
0
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)
    )
Example #12
0
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)
Example #13
0
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()