Пример #1
0
async def test1():
    client = PixivClient(env=True).start()
    api = AppPixivAPI(client=client)
    api.login(refresh_token=_TOKEN)
    # await api.login(_USERNAME, _PASSWORD)
    await client.close()
    print("test1 - finished")
Пример #2
0
 def __init__(self, account: int, a_token: str, r_token: str,
              recent_update: list, save_path: str, logger_,
              proxy: Optional[str]):
     """
     Pixiv对象,储存个人账号信息进行更新、下载(不支持gif),支持代理(SOCKS5),支持自定义logger
     :param account: qq账号id
     :param a_token: Pixiv账号access_token
     :param r_token: Pixiv账号refresh_token
     :param recent_update: 最近更新列表
     :param save_path: 图片存储路径
     :param logger_: 日志对象
     :param proxy: 代理链接
     """
     if proxy:
         self.api = Api(proxy=proxy)
     else:
         self.api = Api()
     self.skip_page = "ugoira"
     # self.save_path = "./function/pixiv_img"
     self.save_path = save_path
     self.account = account
     self.a_token = a_token
     self.r_token = r_token
     self.recent_update = recent_update
     self.proxy = proxy
     self.login_command()
     self.logger = logger_
Пример #3
0
 async def init_appapi(aapi: AppPixivAPI):
     try:
         await aapi.login(username=self.Config.pixiv_username, password=self.Config.pixiv_password)
     except:
         logger.exception("Pixiv 登陆失败")
         return False
     logger.info("成功登录 Pixiv")
     aapi.set_accept_language("zh-CN")
     return True
Пример #4
0
async def _test_async_illust_detail(num):
    async with PixivClient() as client:
        aapi = AppPixivAPI(client=client)
        papi = PixivAPI(client=client)
        await aapi.login(_USERNAME, _PASSWORD)
        await papi.login(_USERNAME, _PASSWORD)
        tasks = [asyncio.ensure_future(illust_detail(aapi, i)) for i in range(num)]
        await asyncio.wait(tasks)
Пример #5
0
async def get_by_ranking(mode="day", num=3):
    async with PixivClient(proxy=proxy, timeout=20) as client:
        aapi = AppPixivAPI(client=client, proxy=proxy)
        await aapi.login(refresh_token=pixiv_config.pixiv_token)
        illusts = await aapi.illust_ranking(mode)
        illusts = illusts["illusts"]
        random.shuffle(illusts)
        return illusts[0:num]
Пример #6
0
async def _test_async_me_following_works(num):
    async with PixivClient() as client:
        aapi = AppPixivAPI(client=client)
        papi = PixivAPI(client=client)
        await aapi.login(_USERNAME, _PASSWORD)
        await papi.login(_USERNAME, _PASSWORD)
        tasks = [asyncio.ensure_future(me_following_works(papi, i)) for i in range(num)]
        await asyncio.wait(tasks)
Пример #7
0
 def __init__(self, client: NanoClient, pixiv_client: PixivClient()):
     self.name = "Pixiv"
     self.client = client
     self.aapi = AppPixivAPI(client=pixiv_client.start())
     self.day_pool = {}
     self.day_male_pool = {}
     self.day_female_pool = {}
     self.base_url = 'https://www.pixiv.net/en/artworks/'
     asyncio.ensure_future(self.load_pools_async())
Пример #8
0
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # forces reauth() to trigger if any method is called:
        self.last_auth = datetime.datetime.fromtimestamp(0)
        self.username = ""
        self.password = ""

        self.aapi = AppPixivAPI(**kwargs)
        self.papi = PixivAPI(**kwargs)
Пример #9
0
async def _test_async_ranking(num):
    async with PixivClient() as client:
        aapi = AppPixivAPI(client=client)
        papi = PixivAPI(client=client)
        await papi.login(refresh_token=_TOKEN)
        await aapi.login(refresh_token=_TOKEN)
        # await aapi.login(_USERNAME, _PASSWORD)
        # await papi.login(_USERNAME, _PASSWORD)
        tasks = [asyncio.ensure_future(ranking(papi, i)) for i in range(num)]
        await asyncio.wait(tasks)
Пример #10
0
class PixivParser:
    status = False
    aapi = AppPixivAPI()
    id_regex = r"[1-9]\d*"

    class Config(BaseConfig, config_file="push_config.json"):
        pixiv_refresh_token: str = str()

        @classmethod
        def _check(cls, _attr_name: str, _attr_value: Any) -> Tuple[str, Any]:
            if _attr_name == "pixiv_refresh_token" and _attr_value == "":
                logger.exception(f"非法的 refresh_token。如果您不知道这是什么,请查看文档")
            else:
                return _attr_name, _attr_value

    @classmethod
    def send(self, url: str, classification: str, bot: Bot, target):
        def origin_link_button(_id: int) -> InlineKeyboardMarkup:
            return InlineKeyboardMarkup([[
                InlineKeyboardButton(
                    text="原链接", url=f"https://www.pixiv.net/artworks/{_id}")
            ]])

        illust_id = int(re.search(pattern=self.id_regex, string=url).group(0))

        try:
            illust = Illust(illust_id, self.Config.pixiv_refresh_token)
        except IllustInitError:
            return

        illust.download()

        images = illust.get_downloaded_images()

        if len(images) > 1:
            bot.send_media_group(
                chat_id=target,
                media=[InputMediaPhoto(BytesIO(image)) for image in images])
            bot.send_message(text=str(illust),
                             chat_id=target,
                             reply_markup=origin_link_button(illust_id),
                             disable_web_page_preview=True,
                             parse_mode=ParseMode.HTML)
        else:
            print(str(illust))
            bot.send_photo(photo=BytesIO(images[0]),
                           chat_id=target,
                           caption=str(illust),
                           reply_markup=origin_link_button(illust_id),
                           parse_mode=ParseMode.HTML)

        logger.info(f"Pixiv: 成功推送 {illust.id}")
Пример #11
0
async def get_by_search(keyword, num=3):
    async with PixivClient(proxy=proxy, timeout=20) as client:
        aapi = AppPixivAPI(client=client, proxy=proxy)
        await aapi.login(refresh_token=pixiv_config.pixiv_token)
        illusts = await aapi.search_illust(keyword)
        illusts = illusts["illusts"]
        illusts = sorted(illusts,
                         key=lambda i: i["total_bookmarks"],
                         reverse=True)
        if len(illusts) > num * 3:
            illusts = illusts[0:int(len(illusts) / 2)]
        random.shuffle(illusts)
        return illusts[0:min(num, len(illusts))]
Пример #12
0
async def to_msg(illusts) -> Message:
    msg = Message()
    async with PixivClient(proxy=proxy, timeout=20) as client:
        aapi = AppPixivAPI(client=client, proxy=proxy)
        await aapi.login(refresh_token=pixiv_config.pixiv_token)
        for illust in illusts:
            try:
                url: str = illust["image_urls"]["large"]
                url = url.replace("_webp", "").replace("i.pximg.net",
                                                       "i.pixiv.re")
                async with httpx.AsyncClient() as client:
                    resp = await client.get(url, timeout=20)
                    result = resp.content
                if result:
                    msg.append("{} ({})".format(illust["title"], illust["id"]))
                    msg.append(MessageSegment.image(result))
            except Exception as e:
                logger.warning(f"Error downloading image: {e}")
        return msg
Пример #13
0
async def test_pixivpy_async_app_api():
    client = PixivClient(env=True)
    aapi = AppPixivAPI(client=client.start())

    # conn = aiohttp.TCPConnector(limit_per_host=30)
    # session = aiohttp.ClientSession(
    #     connector=conn,
    #     timeout=aiohttp.ClientTimeout(total=10),
    #     trust_env=True,
    # )
    # client.client = session

    username, password = os.environ['PIXIV_USERNAME'], os.environ['PIXIV_PASS']
    
    # For App Pixiv API
    await aapi.login(username, password)

    res = await aapi.illust_ranking("day")
    illusts = res.get("illusts")
    print(len(illusts), illusts[0])

    await client.close()
Пример #14
0
async def get_by_id(work_id):
    async with PixivClient(proxy=proxy, timeout=20) as client:
        aapi = AppPixivAPI(client=client, proxy=proxy)
        await aapi.login(refresh_token=pixiv_config.pixiv_token)
        illust = await aapi.illust_detail(work_id)
        return illust
Пример #15
0
class PixivParser:
    status = False
    aapi = AppPixivAPI()
    id_regex = r"[1-9]\d*"

    class Config(BaseConfig, config_file="push_config.json"):
        pixiv_username: str = str()
        pixiv_password: str = str()
        download_path: Optional[str] = str()

        @classmethod
        def _check(cls, _attr_name: str, _attr_value: Any) -> Tuple[str, Any]:
            if _attr_name == "download_path" and _attr_value == "":
                logger.info(f"未提供下载路径, 使用 {os.path.dirname(__file__)}/PixivDownload")
                try:
                    os.mkdir(f"{os.path.dirname(__file__)}/PixivDownload")
                except FileExistsError:
                    pass
                except BaseException as exc:
                    logger.exception("下载路径不可用")
                    #sys.exit(1)
                    raise exc
                return _attr_name, os.path.dirname(__file__) + "/PixivDownload"
            else:
                return _attr_name, _attr_value

    @classmethod
    @run_async
    def send(self, url: str, classification: str, bot: Bot, target):
        async def init_appapi(aapi: AppPixivAPI):
            try:
                await aapi.login(username=self.Config.pixiv_username, password=self.Config.pixiv_password)
            except:
                logger.exception("Pixiv 登陆失败")
                return False
            logger.info("成功登录 Pixiv")
            aapi.set_accept_language("zh-CN")
            return True

        def origin_link(_id: int):
            return InlineKeyboardMarkup([[InlineKeyboardButton(text="原链接", url=f"https://www.pixiv.net/artworks/{_id}")]])

        def parse_tags_text(tags: list) -> str:
            text = str()
            for tag in tags:
                if tag.translated_name:
                    translated_name = f"({tag.translated_name})"
                else:
                    translated_name = ""
                text = text + \
                    f"<a href=\"https://www.pixiv.net/tags/{tag.name}/artworks\">{tag.name}{translated_name}</a> "
            return text

        async def download_single_pic(url: str, _id: int, size: str, page: int, aapi: AppPixivAPI):
            url_basename = os.path.basename(url)
            extension = os.path.splitext(url_basename)[1]
            name = f"{_id}_p{page}_{size}{extension}"
            try:
                os.mkdir(f"{self.Config.download_path}/{_id}")
            except FileExistsError:
                pass
            await aapi.download(url, path=self.Config.download_path + "/" + str(_id), name=name)
            logger.info(f"成功下载 {name}")

        def download_pic(urls: list, _id: int, size: str, aapi: AppPixivAPI):
            page = 0
            loop = asyncio.new_event_loop()
            tasks = list()
            for url in urls:
                tasks.append(download_single_pic(url, _id, size, page, aapi))
                # download_single_pic(url, _id, size, page, aapi)
                page = page + 1
            loop.run_until_complete(asyncio.gather(*tasks, loop=loop))
            loop.close()
            logger.info(f"成功下载 {_id} 全部图片")

        async def parse_illust_info_msg(illust_id: int, aapi: AppPixivAPI):
            json_result = await aapi.illust_detail(illust_id)
            info = json_result.illust
            caption = str()
            if info.caption != "":
                soup = BeautifulSoup(info.caption, "html.parser")
                caption = "\n" + soup.get_text()
            msg_text = f"<b>标题:</b>{info.title}\n<b>作者:</b><a href=\"https://www.pixiv.net/users/{info.user.id}\">{info.user.name}</a>{caption}\n<b>标签:</b>{parse_tags_text(info.tags)}{classification}"
            logger.info(msg_text)

            if info.page_count == 1:
                illust_urls = [info.image_urls.large]
            else:
                illust_urls = [page.image_urls.large for page in info.meta_pages]
            return illust_urls, illust_id, msg_text

        loop = asyncio.new_event_loop()
        login_result = loop.run_until_complete(init_appapi(self.aapi))
        loop.close()
        if not login_result:
            return

        illust_id = int(re.search(pattern=self.id_regex, string=url).group(0))

        loop = asyncio.new_event_loop()
        illust_urls, illust_id, msg_text = loop.run_until_complete(
            parse_illust_info_msg(illust_id, self.aapi))
        loop.close()
        download_pic(illust_urls, illust_id, "large", self.aapi)
        file_dirs = [self.Config.download_path+f"/{illust_id}/" +
                     filename for filename in os.listdir(self.Config.download_path+f"/{illust_id}")]
        if len(file_dirs) == 1:
            bot.send_photo(chat_id=target,
                           photo=open(file_dirs[0], 'rb'),
                           caption=msg_text,
                           reply_markup=origin_link(illust_id),
                           parse_mode=ParseMode.HTML)
        else:
            tmp_sub_file_group = list()
            tmp_size = 0
            sub_file_groups = list()
            for file_dir in file_dirs:
                if tmp_size + os.path.getsize(file_dir) <= 5242880 and len(tmp_sub_file_group) + 1 <= 10:
                    tmp_sub_file_group.append(InputMediaPhoto(media=open(file_dir, 'rb'),
                                                              caption=msg_text,
                                                              parse_mode=ParseMode.HTML))
                else:
                    sub_file_groups.append(tmp_sub_file_group)
                    tmp_sub_file_group = [InputMediaPhoto(media=open(file_dir, 'rb'),
                                                          caption=msg_text,
                                                          parse_mode=ParseMode.HTML)]
                    tmp_size = os.path.getsize(file_dir)
            sub_file_groups.append(tmp_sub_file_group)
            for sub_file_group in sub_file_groups:
                bot.send_media_group(chat_id=target, media=sub_file_group)
            bot.send_message(chat_id=target,
                          text=msg_text,
                          reply_markup=origin_link(illust_id),
                          disable_web_page_preview=True,
                          parse_mode=ParseMode.HTML)
def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(_main(AppPixivAPI()))
Пример #17
0
class Illust:

    aapi = AppPixivAPI(env=True)

    def __init__(self, illust_id: int, refresh_token:str):
        async def init_appapi() -> bool:
            try:
                await self.aapi.login(refresh_token=refresh_token)
            except:
                return False
            self.aapi.set_accept_language("zh-CN")
            return True

        async def get_info() -> bool:
            try:
                json_result = await self.aapi.illust_detail(self.id)
            except:
                return False

            info = json_result.illust

            self.caption = str()
            if info.caption != "":
                soup = BeautifulSoup(info.caption, "html.parser")
                self.caption = "\n" + soup.get_text()

            self.title = info.title
            self.author = (info.user.name, info.user.id)

            self.tags = [(tag.name, tag.translated_name) for tag in info.tags]

            if info.page_count == 1:
                self.urls = [info.image_urls.large]
                self.original_urls = [info.meta_single_page.original_image_url]
            else:
                self.urls = [
                    page.image_urls.large for page in info.meta_pages]
                self.original_urls = [
                    page.image_urls.original for page in info.meta_pages]

            return True

        loop = asyncio.new_event_loop()
        login_result = loop.run_until_complete(init_appapi())
        loop.close()
        if not login_result:
            logger.exception("Pixiv 登录失败")
            raise LoginError
        logger.info("Pixiv 登录成功")

        self.id: int = illust_id

        self.__images: list = list()

        loop = asyncio.new_event_loop()
        get_info_result = loop.run_until_complete(get_info())
        loop.close()
        if not get_info_result:
            logger.exception("插画信息获取失败")
            raise GetInfoError

    def __str__(self):
        tags_text = str()
        for tag in self.tags:
            tags_text = tags_text + \
                f"<a href=\"https://www.pixiv.net/tags/{tag[0]}/artworks\">{tag[0]}</a>"
            if tag[1]:
                tags_text = tags_text + f" ({tag[1]})"
            tags_text = tags_text+", "

        return f"<b>标题:</b>{self.title}\n<b>作者:</b><a href=\"https://www.pixiv.net/users/{self.author[1]}\">{self.author[0]}</a>\n<b>简介:</b>{self.caption}\n<b>标签:</b>{tags_text}"

    async def __download_single_image(self, url: str, size_hint: str, page_hint: int):
        try:
            content, type = await self.aapi.down(url, "https://app-api.pixiv.net/")
        except:
            logger.exception(f"{self.id} {size_hint} 第 {page_hint} 张下载错误")
            raise DownloadError

        if type is not None and type.find("image") != -1:
            self.__images.append((page_hint, content))
        else:
            logger.exception(f"{self.id} {size_hint} 第 {page_hint} 张下载错误")
            raise DownloadError

        logger.info(f"成功下载 {self.id} {size_hint} 第 {page_hint} 张")

    def __download_images(self, original: bool = False):
        page = 0
        loop = asyncio.new_event_loop()

        if not original:
            urls = self.urls
            size_hint = "large"
        else:
            urls = self.original_urls
            size_hint = "original"

        tasks = list()
        for url in urls:
            tasks.append(self.__download_single_image(url, size_hint, page))
            page = page + 1

        try:
            loop.run_until_complete(asyncio.gather(*tasks, loop=loop))
        except DownloadError:
            pass
        finally:
            loop.close()

        logger.info(f"成功下载 {self.id} 全部 {size_hint} 图片")

    def download(self):
        self.__download_images()

    def download_original(self):
        self.__download_images(True)

    def get_downloaded_images(self):
        if not len(self.__images):
            return None

        self.__images.sort(key=lambda elem: elem[0])

        return [elem[1] for elem in self.__images[:9]]
Пример #18
0
class Pixiv:
    def __init__(self, account: int, a_token: str, r_token: str,
                 recent_update: list, save_path: str, logger_,
                 proxy: Optional[str]):
        """
        Pixiv对象,储存个人账号信息进行更新、下载(不支持gif),支持代理(SOCKS5),支持自定义logger
        :param account: qq账号id
        :param a_token: Pixiv账号access_token
        :param r_token: Pixiv账号refresh_token
        :param recent_update: 最近更新列表
        :param save_path: 图片存储路径
        :param logger_: 日志对象
        :param proxy: 代理链接
        """
        if proxy:
            self.api = Api(proxy=proxy)
        else:
            self.api = Api()
        self.skip_page = "ugoira"
        # self.save_path = "./function/pixiv_img"
        self.save_path = save_path
        self.account = account
        self.a_token = a_token
        self.r_token = r_token
        self.recent_update = recent_update
        self.proxy = proxy
        self.login_command()
        self.logger = logger_

    def login_command(self):
        """
        使用内置的token设置登录pix
        :return:
        """
        self.api.set_auth(self.a_token, self.r_token)

    async def refresh_token(self):
        """
        依据内置的refresh_token进行token刷新,同时进行token登录设置
        :return:
        """
        a, r = await Pix.reset_token(self.account, self.r_token)
        self.a_token = a
        self.r_token = r
        self.logger.info(
            "{}的重置Token:'access_token - {}' 'refresh_token - {}'".format(
                str(self.account), a, r))
        self.login_command()
        self.logger.info("{}的Token 写入文件成功".format(str(self.account)))

    async def _get_new_follow_art(self, retry: bool = False):
        """
        获取关注列表的新图,列表结果已进行比对筛选去除已更新
        :param retry: 当token失效自动刷新登录,无需手动设置
        :return: 图片列表[[pid,page]...]
        """
        try:
            self.logger.info("向api请求中")
            json_all = await self.api.illust_follow()
        except BaseException as e:
            self.logger.error(e)
        else:
            if "error" in json_all and retry is False:
                self.logger.info("{}的Token失效".format(str(self.account)))
                await self.refresh_token()
                return await self._get_new_follow_art(retry=True)
            self.logger.info("Token有效,获得数据中")
            real_pid_list = []
            json_res = json_all["illusts"]
            last_save_pid_list = self.recent_update
            art_list = []
            nums = 0
            for img in json_res:
                pid = str(img['id'])
                if img['type'] == self.skip_page or pid in last_save_pid_list:
                    pass
                else:
                    meta_single_page = img['meta_single_page']
                    meta_pages = img['meta_pages']
                    if meta_single_page:
                        art_list.append(
                            [pid, meta_single_page['original_image_url']])
                    elif meta_pages:
                        pages = []
                        for page in meta_pages:
                            pages.append(page['image_urls']["original"])
                        art_list.append([pid, pages])
                    nums += 1
                real_pid_list.append(pid)
                if nums >= 20:
                    break
            self.logger.info("json数据获取结束")
            return art_list, real_pid_list

    @classmethod
    def _resolve_art_list(cls, art_list: list):
        """
        根据结果对url列表进行分离整合
        :param art_list: Pix_url_list
        :return: pid列表, 单个pid包含图片数列表, 纯url列表
        """
        pid_list = []
        pages_count_list = []
        url_list = []
        for art in art_list:
            pid_list.append(art[0])
            pages_info = art[1]
            if isinstance(pages_info, str):
                pages_info = [pages_info]
            pages_count_list.append(len(pages_info))
            url_list.extend(pages_info)

        return pid_list, pages_count_list, url_list

    async def _url_to_path(self, urls: List[str]) -> \
            Tuple[List[str], List[List[Union[int, str]]], List[List[Union[int, BytesIO]]]]:
        """
        根据url列表,转化为储存路径列表,并对url进行反向代理链接替换
        :param urls: url_list
        :return: paths: Tuple[List[str],
                url_list: List[List[Union[int, str]]], downloaded_img: List[List[Optional[int, BytesIO]]]]
        """
        if isinstance(urls, str):
            urls = [urls]
        paths = []
        num_to_urls = list(zip([i for i in range(len(urls))], urls))
        downloaded_img = []
        url_list = []
        for url in num_to_urls:
            path = os.path.join(self.save_path, url[1].split("/")[-1])
            if os.path.exists(path):
                async with aiofile.async_open(path, "rb") as f:
                    downloaded_img.append([url[0], BytesIO(await f.read())])
            else:
                url_list.append(
                    [url[0], url[1].replace("i.pximg.net", "i.pixiv.cat")])
                # url_list.append([url[0], url[1]])
                paths.append(path)
        return paths, url_list, downloaded_img

    @classmethod
    async def _downloader(cls, download_url: List[List[Union[int, str]]], logger, proxy: Optional[str] = None) \
            -> List[List[Union[int, BytesIO]]]:
        """
        下载所有图片链接, 返回二进制数据列表
        :param download_url: url_list
        :return: bytes_data_list
        """
        down = []
        index_date = [i[0] for i in download_url]
        n = 0
        if proxy:
            proxy_info_list = proxy.split(":")
            port = int(proxy_info_list[-1])
            ip = proxy_info_list[1].split("/")[-1]
            # hander = {
            #     "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4471.0 Safari/537.36 Edg/91.0.864.1",
            #     "Referer": "https://www.pixiv.net/"
            # }
            connector = ProxyConnector(proxy_type=ProxyType.SOCKS5,
                                       host=ip,
                                       port=port)
            async with aiohttp.ClientSession(connector=connector) as session:
                for url in download_url:
                    logger.info(f"请求url:{url}")
                    img = await cls._aiohttp_down(session, url)
                    down.append([index_date[n], img])
                    n += 1
        else:
            async with aiohttp.ClientSession() as session:
                for url in download_url:
                    img = await cls._aiohttp_down(session, url)
                    down.append([index_date[n], img])
                    n += 1
        return down

    @classmethod
    async def _aiohttp_down(cls,
                            session: aiohttp.ClientSession,
                            url: list[Union[int, str]],
                            re: int = 0):
        async with session.get(url[1]) as response:
            if response.status == 200:
                img = BytesIO(await response.read())
                return img
            else:
                if re > 3:
                    raise ConnectionError
                else:
                    return await cls._aiohttp_down(session, url, re + 1)

    @classmethod
    def _zip_img(cls, downloaded: List[List[Union[int, BytesIO]]], now_download: List[List[Union[int, BytesIO]]]) -> \
            List[BytesIO]:
        """
        合并
        :param downloaded:
        :param now_download:
        :return:
        """
        size_img = len(downloaded) + len(now_download)
        res_list = [BytesIO()] * size_img
        for i in downloaded:
            res_list[i[0]] = i[1]
        for i in now_download:
            res_list[i[0]] = i[1]
        return res_list

    async def _save_from_downloader(self, pages_path: list,
                                    img_bytes: List[List[Union[int,
                                                               BytesIO]]]):
        """
        下载至对应路径
        :param pages_path: list
        :param img_bytes: List[BytesIO]
        :return:
        """
        img_len = len(pages_path)
        for i in range(img_len):
            async with aiofile.async_open(pages_path[i], "wb") as f:
                await f.write(img_bytes[i][1].getvalue())
        self.logger.info("{}张图片保存成功".format(str(img_len)))

    def _creat_msg_chain(self, pid_list: List[str], pages_num: List[int],
                         img_bytes: List[BytesIO]):
        """
        进行消息链构造
        :param pid_list: List[str]
        :param pages_num: List[int]
        :param img_bytes: List[BytesIO]
        :return: List[List[MessageChain], ...]
        """
        page_count = 0
        msg_img_limit = 2
        limit_count = 0
        out_msg_list = [[
            Plain("又到了涩图时间!此次共有{}张图!".format(str(len(img_bytes))))
        ]]
        for i in range(len(pid_list)):
            out_msg = []
            for n in range(pages_num[i]):
                out_msg.append(
                    Image.fromUnsafeBytes(img_bytes[page_count].getvalue()))
                page_count += 1
                limit_count += 1
                if limit_count >= msg_img_limit:
                    limit_count = 0
                    out_msg_list.extend([out_msg])
                    out_msg = []
            if out_msg:
                out_msg_list.extend([out_msg])
                limit_count = 0
        self.logger.info("消息链构建成功")
        return out_msg_list

    async def _change_recent_save(self, pid: list):
        """
        保存更新作品pid
        :param pid: list
        :return:
        """
        await Pix.change_recent_save(self.account, pid)
        self.logger.info("{}的最近更新已正常写入".format(str(self.account)))

    async def run(self):
        """
        pix更新
        :return: List[List[MessageChain], ...]
        """
        self.logger.info("即将获取更新列表")
        res, last_pid_list = await self._get_new_follow_art()
        if not res:
            self.logger.info("操作完成,无作品更新")
            pass
        else:
            self.logger.info("json分离结束")
            pid_list, pages_num, url_list = self._resolve_art_list(res)
            self.logger.info("进行链接代理转化")
            pages_path, new_urls, downloaded_bt_list = await self._url_to_path(
                url_list)
            self.logger.info("进行下载")
            img_bytes = await self._downloader(new_urls, self.logger,
                                               self.proxy)
            self.logger.info("下载完成,进行打包")
            fin_img_list = self._zip_img(downloaded_bt_list, img_bytes)
            self.logger.info("打包完成,保存至本地文件")
            await self._save_from_downloader(pages_path, img_bytes)
            self.logger.info("保存完毕,开始构建消息链")
            out_msg_list = self._creat_msg_chain(pid_list, pages_num,
                                                 fin_img_list)
            await self._change_recent_save(last_pid_list)
            self.recent_update = last_pid_list
            return out_msg_list