예제 #1
0
class PixivAPIs:
    def __init__(self, credentials):
        self.apapi = AppPixivAPI()
        self.papi = PixivAPI()
        self.apapi.auth(refresh_token=credentials[0])
        self.papi.auth(refresh_token=credentials[0])
예제 #2
0
class PixivHandler:
    def __init__(self, name, app_config={}):
        config_path = Path(app_config.get('handlers_config_dir', '.')) / 'pixiv.toml'
        data_path = Path(app_config.get('data_dir', './data/')) / '{}.toml'.format(name)
        self.config = Config(config_path, write_defaults=True, defaults={
            'refresh': 'xxxx',
        })
        self.config.save()
        self.data = Config(data_path)
        self.age_filter = None
        self.api = PixivAPI()
        if self.config.get('refresh'):
            print('logging in to Pixiv...')
            login_response = self.api.auth(refresh_token=self.config['refresh'])
            print('logged in into account {0.name} ({0.account}) [{0.id}]'.format(login_response['response']['user']))

    def set_age_filter(self, filter):
        self.age_filter = filter

    def handle(self, feed):
        if feed == 'followings':
            data = self.api.me_following_works(image_sizes=['large', 'medium'], include_stats=False)
        elif feed == 'bookmarks':
            data = self.api.me_favorite_works()
        else:
            return []
        if data['status'] != 'success':
            print('invalid response')
            print('got:')
            print(data)
            return []
        results = data['response']
        save_data = self.data.get(feed, {'last_id': 0})
        print('latest id: {}'.format(save_data.get('last_id')))
        results = list(filter(lambda x: x['id'] > save_data.get('last_id'), results))
        if len(results) == 0:
            return []
        save_data['last_id'] = results[0]['id']
        self.data[feed] = save_data
        self.data.save()
        ret = []
        for entry in results:
            print('Handling pixiv entry {}'.format(entry['id']))
            if self.age_filter != None:
                if entry['age_limit'] in ['r18', 'r18-g'] and self.age_filter == 'safe':
                    print('skipping because currently in safe mode')
                    continue
                if entry['age_limit'] == 'all-age' and self.age_filter == 'r18':
                    print('skipping because currently in r18 mode')
                    continue
            content = '<https://www.pixiv.net/artworks/{}>'.format(entry['id'])
            content += '\n{} by {} ({})'.format(entry['title'], entry['user']['name'], entry['user']['account'])
            content += '\nTags: {}'.format(' '.join(entry['tags']))
            if entry['is_manga']:
                print('it\'s a manga')
                work = self.api.works(entry['id'])
                if work['status'] != 'success':
                    continue
                work = work['response']
                if len(work) == 0:
                    continue
                work = work[0]
                urls = [x['image_urls']['medium'] for x in work['metadata']['pages']]
                if len(urls) > 4:
                    content += '\n{} more pictures not shown here'.format(len(urls) - 4)
                    urls = urls[:4]
            else:
                if entry['width'] > 2000 or entry['height'] > 2000:
                    content += '\n(not displaying full resolution because it is too large)'
                    urls = [entry['image_urls']['medium']]
                else:
                    urls = [entry['image_urls']['large']]
            files = []
            index = 0
            for url in urls:
                print('downloading picture...')
                response = requests.get(url, headers={'referer': 'https://pixiv.net'})
                if response.status_code != 200:
                    continue
                ext = Path(url).suffix
                files.append({'data': response.content, 'name': 'page{}{}'.format(index, ext)})
                index += 1
            ret.append({'content': content, 'files': files})
        ret.reverse()
        return ret
예제 #3
0
class CustomPixivPy:
    """
    A wrapper around PixivAPI and AppPixivAPI to facilitate automatic re-authentication
     (for required methods) and custom result format
    """
    TOKEN_LIFESPAN = datetime.timedelta(seconds=3600)
    MAX_PIXIV_RESULTS = 3000
    RESULTS_PER_QUERY = 50
    MAX_RETRIES = 5

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # forces reauth() to trigger if any method is called:
        self.last_auth = datetime.datetime.fromtimestamp(0)
        self.refresh_token = ""
        self.aapi = AppPixivAPI(**kwargs)
        self.papi = PixivAPI(**kwargs)

    def login(self, refresh_token):
        self.refresh_token = refresh_token
        self.aapi.auth(refresh_token=refresh_token)
        self.papi.auth(refresh_token=refresh_token)
        self.last_auth = datetime.datetime.now()
        logger.debug('Pyxiv login done')
        return self  # allows chaining

    @retry
    def illust_ranking(self, mode='daily', offset=None):
        self.reauth()
        offset = (offset or 0) // self.RESULTS_PER_QUERY + 1
        return self.papi.ranking('illust',
                                 mode,
                                 offset,
                                 include_stats=False,
                                 image_sizes=['medium', 'large'])

    @retry
    def search_illust(self,
                      word,
                      search_target='text',
                      sort='date',
                      offset=None):
        self.reauth()
        offset = (offset or 0) // self.RESULTS_PER_QUERY + 1
        return self.papi.search_works(word,
                                      offset,
                                      mode=search_target,
                                      types=['illustration'],
                                      sort=sort,
                                      include_stats=False,
                                      image_sizes=['medium', 'large'])

    @retry
    def illust_detail(self, illust_id, req_auth=True):
        self.reauth()
        return self.aapi.illust_detail(illust_id, req_auth)

    def reauth(self):
        """Re-authenticates with pixiv if the last login was more than TOKEN_LIFESPAN ago"""
        if datetime.datetime.now() - self.last_auth > self.TOKEN_LIFESPAN:
            self.login(self.refresh_token)
            self.papi.auth(refresh_token=self.refresh_token)
            logger.debug("Reauth successful")
            self.last_auth = datetime.datetime.now()

    def get_pixiv_results(self, offset=None, *, query="", nsfw=False):
        """
        Get results from Pixiv as a dict
        If no parameters are given, SFW daily ranking is returned
        :param offset: Optional. page offset
        :param query: Optional. Specify a search query
        :param nsfw: Whether to allow NSFW illustrations, false by default
        :return: list of dicts containing illustration information
        """
        json_result, last_error = None, None
        for attempt in range(1, self.MAX_RETRIES + 1):
            try:
                json_result = self.search_illust(query, offset=offset, sort='popular') \
                    if query else self.illust_ranking('daily_r18' if nsfw else 'daily', offset=offset)
            except PixivError as e:
                if attempt == self.MAX_RETRIES:
                    logger.warning("Failed fetching Pixiv data: %s", e)
                    raise e from None
            else:
                break

        results = []
        if json_result.get('has_error'):
            return results

        it = json_result.response if query else (
            x['work'] for x in json_result.response[0]['works'])
        for img in it:
            if not nsfw and img['sanity_level'] == 'black':
                continue  # white = SFW, semi_black = questionable, black = NSFW
            results.append({
                'url':
                img['image_urls']['large'],
                'thumb_url':
                img['image_urls']['medium'],
                'title':
                img['title'],
                'user_name':
                img['user']['name'],
                'user_link':
                f"https://www.pixiv.net/en/users/{img['user']['id']}"
            })
            logger.debug(results[-1])
        return results