diff --git a/PixivBrowserFactory.py b/PixivBrowserFactory.py index 751e9706..ce13b9e0 100644 --- a/PixivBrowserFactory.py +++ b/PixivBrowserFactory.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # pylint: disable=W0603, C0325 import http.client @@ -20,7 +20,7 @@ from PixivArtist import PixivArtist from PixivException import PixivException from PixivImage import PixivImage -from PixivModelFanbox import Fanbox, FanboxArtist +from PixivModelFanbox import FanboxArtist, FanboxPost from PixivOAuth import PixivOAuth from PixivTags import PixivTags @@ -233,26 +233,33 @@ def fixUrl(self, url, useHttps=True): return "http://www.pixiv.net" + url return url - def _loadCookie(self, cookie_value): + def _loadCookie(self, cookie_value, domain): """ Load cookie to the Browser instance """ - ck = http.cookiejar.Cookie(version=0, name='PHPSESSID', value=cookie_value, port=None, - port_specified=False, domain='pixiv.net', domain_specified=False, - domain_initial_dot=False, path='/', path_specified=True, - secure=False, expires=None, discard=True, comment=None, - comment_url=None, rest={'HttpOnly': None}, rfc2109=False) + if "pixiv.net" in domain: + ck = http.cookiejar.Cookie(version=0, name='PHPSESSID', value=cookie_value, port=None, + port_specified=False, domain='pixiv.net', domain_specified=False, + domain_initial_dot=False, path='/', path_specified=True, + secure=False, expires=None, discard=True, comment=None, + comment_url=None, rest={'HttpOnly': None}, rfc2109=False) + elif "fanbox.cc" in domain: + ck = http.cookiejar.Cookie(version=0, name='FANBOXSESSID', value=cookie_value, port=None, + port_specified=False, domain='fanbox.cc', domain_specified=False, + domain_initial_dot=False, path='/', path_specified=True, + secure=False, expires=None, discard=True, comment=None, + comment_url=None, rest={'HttpOnly': None}, rfc2109=False) self.addCookie(ck) -# cookies = cookie_value.split(";") -# for cookie in cookies: -# temp = cookie.split("=") -# name = temp[0].strip() -# value= temp[1] if len(temp) > 1 else "" -# ck = cookielib.Cookie(version=0, name=name, value=value, port=None, -# port_specified=False, domain='pixiv.net', domain_specified=False, -# domain_initial_dot=False, path='/', path_specified=True, -# secure=False, expires=None, discard=True, comment=None, -# comment_url=None, rest={'HttpOnly': None}, rfc2109=False) -# self.addCookie(ck) + # cookies = cookie_value.split(";") + # for cookie in cookies: + # temp = cookie.split("=") + # name = temp[0].strip() + # value= temp[1] if len(temp) > 1 else "" + # ck = cookielib.Cookie(version=0, name=name, value=value, port=None, + # port_specified=False, domain='pixiv.net', domain_specified=False, + # domain_initial_dot=False, path='/', path_specified=True, + # secure=False, expires=None, discard=True, comment=None, + # comment_url=None, rest={'HttpOnly': None}, rfc2109=False) + # self.addCookie(ck) def _getInitConfig(self, page): init_config = page.find('input', attrs={'id': 'init-config'}) @@ -261,6 +268,7 @@ def _getInitConfig(self, page): def loginUsingCookie(self, login_cookie=None): """ Log in to Pixiv using saved cookie, return True if success """ + result = False if login_cookie is None or len(login_cookie) == 0: login_cookie = self._config.cookie @@ -268,13 +276,12 @@ def loginUsingCookie(self, login_cookie=None): if len(login_cookie) > 0: PixivHelper.print_and_log('info', 'Trying to log in with saved cookie') self.clearCookie() - self._loadCookie(login_cookie) + self._loadCookie(login_cookie, "pixiv.net") res = self.open_with_retry('https://www.pixiv.net/') parsed = BeautifulSoup(res, features="html5lib").decode('utf-8') PixivHelper.get_logger().info('Logging in, return url: %s', res.geturl()) res.close() - result = False if "logout.php" in str(parsed): result = True if "pixiv.user.loggedIn = true" in str(parsed): @@ -296,7 +303,46 @@ def loginUsingCookie(self, login_cookie=None): PixivHelper.get_logger().info('Failed to log in using cookie') PixivHelper.print_and_log('info', 'Cookie already expired/invalid.') - del parsed + del parsed + return result + + def fanboxLoginUsingCookie(self, login_cookie=None): + """ Log in to Pixiv using saved cookie, return True if success """ + result = False + + if login_cookie is None or len(login_cookie) == 0: + login_cookie = self._config.cookieFanbox + + if len(login_cookie) > 0: + PixivHelper.print_and_log('info', 'Trying to log in FANBOX with saved cookie') + self.clearCookie() + self._loadCookie(login_cookie, "fanbox.cc") + + req = mechanize.Request("https://www.fanbox.cc") + req.add_header('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8') + req.add_header('Origin', 'https://www.fanbox.cc') + req.add_header('User-Agent', self._config.useragent) + try: + res = self.open_with_retry(req) + parsed = BeautifulSoup(res, features="html5lib").decode('utf-8') + PixivHelper.get_logger().info('Logging in with cookit to Fanbox, return url: %s', res.geturl()) + res.close() + except BaseException as e: + PixivHelper.get_logger().error('Error at fanboxLoginUsingCookie, please check cookieFanbox: ' + + format(sys.exc_info())) + return False + + if '"user":{"isLoggedIn":true' in str(parsed): + result = True + + if result: + PixivHelper.print_and_log('info', 'Login successful.') + PixivHelper.get_logger().info('Logged in using cookie') + else: + PixivHelper.get_logger().info('Failed to log in using cookie') + PixivHelper.print_and_log('info', 'Cookie already expired/invalid.') + + del parsed return result def login(self, username, password): @@ -501,7 +547,8 @@ def getMemberInfoWhitecube(self, member_id, artist, bookmark=False): self._put_to_cache(url, info) else: PixivHelper.print_and_log('info', 'Using OAuth to retrieve member info for: {0}'.format(member_id)) - if self._username is None or self._password is None or len(self._username) < 0 or len(self._password) < 0: + if self._username is None or self._password is None or len(self._username) < 0 or len( + self._password) < 0: raise PixivException("Empty Username or Password, remove cookie value and relogin, or add username/password to config.ini.") if self._oauth_manager is None: @@ -724,78 +771,136 @@ def handleDebugTagSearchPage(self, response, url): PixivHelper.safePrint(u"reply: {0}".format( PixivHelper.toUnicode(response))) - def fanboxGetSupportedUsers(self): - ''' get all supported users from the list from https://fanbox.pixiv.net/api/plan.listSupporting''' - url = 'https://fanbox.pixiv.net/api/plan.listSupporting' - PixivHelper.print_and_log('info', f'Getting supported artists from {url}') - referer = "https://www.pixiv.net/fanbox/support/creators" + def fanboxGetUsers(self, via): + if via == FanboxArtist.SUPPORTED: + url = 'https://api.fanbox.cc/plan.listSupporting' + PixivHelper.print_and_log('info', f'Getting supported artists from {url}') + referer = "https://www.fanbox.cc/creators/supporting" + elif via == FanboxArtist.FOLLOWED: + url = 'https://api.fanbox.cc/creator.listFollowing' + PixivHelper.print_and_log('info', f'Getting supported artists from {url}') + referer = "https://www.fanbox.cc/creators/following" + req = mechanize.Request(url) req.add_header('Accept', 'application/json, text/plain, */*') req.add_header('Referer', referer) - req.add_header('Origin', 'https://www.pixiv.net') + req.add_header('Origin', 'https://www.fanbox.cc') req.add_header('User-Agent', self._config.useragent) res = self.open_with_retry(req) # read the json response response = res.read() res.close() - result = Fanbox(response) - return result + _tzInfo = None + if self._config.useLocalTimezone: + _tzInfo = PixivHelper.LocalUTCOffsetTimezone() + artists = FanboxArtist.parseArtists(page=response, tzInfo=_tzInfo) + return artists + + def fanboxUpdateArtistToken(self, artist): + pixivArtist = PixivArtist(artist.artistId) + self.getMemberInfoWhitecube(artist.artistId, pixivArtist) + artist.artistName = pixivArtist.artistName + artist.artistToken = pixivArtist.artistToken + + def fanboxGetArtistById(self, id): + if re.match(r"^\d+$", id): + id_type = "userId" + else: + id_type = "creatorId" + + url = f'https://api.fanbox.cc/creator.get?{id_type}={id}' + PixivHelper.print_and_log('info', f'Getting artist information from {url}') + referer = "https://www.fanbox.cc" + if id_type == "creatorId": + referer += f"/@{id}" - def fanboxGetPostsFromArtist(self, artist_id, next_url=""): + req = mechanize.Request(url) + req.add_header('Accept', 'application/json, text/plain, */*') + req.add_header('Referer', referer) + req.add_header('Origin', 'https://www.fanbox.cc') + req.add_header('User-Agent', self._config.useragent) + + res = self.open_with_retry(req) + # read the json response + response = res.read() + res.close() + _tzInfo = None + if self._config.useLocalTimezone: + _tzInfo = PixivHelper.LocalUTCOffsetTimezone() + + js = demjson.decode(response) + if "error" in js and js["error"]: + raise PixivException("Error when requesting Fanbox", 9999, page) + + if "body" in js and js["body"] is not None: + js_body = js["body"] + artist = FanboxArtist(js_body["user"]["userId"], + js_body["user"]["name"], + js_body["creatorId"], + tzInfo=_tzInfo) + self.fanboxUpdateArtistToken(artist) + return artist + + def fanboxGetPostsFromArtist(self, artist, next_url=""): ''' get all posts from the supported user from https://fanbox.pixiv.net/api/post.listCreator?userId=1305019&limit=10 ''' # Issue #641 if next_url is None or next_url == "": - url = f"https://fanbox.pixiv.net/api/post.listCreator?userId={artist_id}&limit=10" + url = f"https://api.fanbox.cc/post.listCreator?userId={artist.artistId}&limit=10" elif next_url.startswith("https://"): url = next_url else: - url = "https://www.pixiv.net" + next_url + url = "https://www.fanbox.cc" + next_url # Fix #494 PixivHelper.print_and_log('info', 'Getting posts from ' + url) - referer = f"https://www.pixiv.net/fanbox/creator/{artist_id}" + referer = f"https://www.fanbox.cc/@{artist.creatorId}" req = mechanize.Request(url) req.add_header('Accept', 'application/json, text/plain, */*') req.add_header('Referer', referer) - req.add_header('Origin', 'https://www.pixiv.net') + req.add_header('Origin', 'https://www.fanbox.cc') req.add_header('User-Agent', self._config.useragent) res = self.open_with_retry(req) response = res.read() PixivHelper.get_logger().debug(response.decode('utf8')) res.close() - # Issue #420 - _tzInfo = None - if self._config.useLocalTimezone: - _tzInfo = PixivHelper.LocalUTCOffsetTimezone() - result = FanboxArtist(artist_id, response, tzInfo=_tzInfo) - - pixivArtist = PixivArtist(artist_id) - self.getMemberInfoWhitecube(artist_id, pixivArtist) - result.artistName = pixivArtist.artistName - result.artistToken = pixivArtist.artistToken - - for post in result.posts: - # https://fanbox.pixiv.net/api/post.info?postId=279561 - # https://www.pixiv.net/fanbox/creator/104409/post/279561 - p_url = f"https://fanbox.pixiv.net/api/post.info?postId={post.imageId}" - p_referer = f"https://www.pixiv.net/fanbox/creator/{artist_id}/post/{post.imageId}" - PixivHelper.get_logger().debug('Getting post detail from %s', p_url) - p_req = mechanize.Request(p_url) - p_req.add_header('Accept', 'application/json, text/plain, */*') - p_req.add_header('Referer', p_referer) - p_req.add_header('Origin', 'https://www.pixiv.net') - p_req.add_header('User-Agent', self._config.useragent) - - p_res = self.open_with_retry(p_req) - p_response = p_res.read() - PixivHelper.get_logger().debug(p_response.decode('utf8')) - p_res.close() - js = demjson.decode(p_response) + posts = artist.parsePosts(response) + + for post in posts: + js = self.fanboxGetPost(post.imageId, artist) post.parsePost(js["body"]) - return result + return posts + + def fanboxGetPost(self, post_id, artist=None): + # https://fanbox.pixiv.net/api/post.info?postId=279561 + # https://www.pixiv.net/fanbox/creator/104409/post/279561 + p_url = f"https://api.fanbox.cc/post.info?postId={post_id}" + # referer doesn't seeem to be essential + p_referer = f"https://www.fanbox.cc/@{artist.creatorId}/posts/{post_id}" + PixivHelper.get_logger().debug('Getting post detail from %s', p_url) + p_req = mechanize.Request(p_url) + p_req.add_header('Accept', 'application/json, text/plain, */*') + p_req.add_header('Referer', p_referer) + p_req.add_header('Origin', 'https://www.fanbox.cc') + p_req.add_header('User-Agent', self._config.useragent) + + p_res = self.open_with_retry(p_req) + p_response = p_res.read() + PixivHelper.get_logger().debug(p_response.decode('utf8')) + p_res.close() + js = demjson.decode(p_response) + if artist: + return js + else: + _tzInfo = None + if self._config.useLocalTimezone: + _tzInfo = PixivHelper.LocalUTCOffsetTimezone() + artist = FanboxArtist(js["body"]["user"]["userId"], js["body"]["creatorId"], js["body"]["user"]["name"]) + self.fanboxUpdateArtistToken(artist) + post = FanboxPost(post_id, artist, js["body"], _tzInfo) + return post def getBrowser(config=None, cookieJar=None): @@ -877,34 +982,34 @@ def testSearchTags(): oldest_first, start_page) resultS.PrintInfo() - assert(len(resultS.itemList) > 0) + assert (len(resultS.itemList) > 0) def testImage(): print("test image mode") print(">>") (result, page) = b.getImagePage(60040975) print(result.PrintInfo()) - assert(len(result.imageTitle) > 0) + assert (len(result.imageTitle) > 0) print(result.artist.PrintInfo()) - assert(len(result.artist.artistToken) > 0) - assert("R-18" not in result.imageTags) + assert (len(result.artist.artistToken) > 0) + assert ("R-18" not in result.imageTags) print(">>") (result2, page2) = b.getImagePage(59628358) print(result2.PrintInfo()) - assert(len(result2.imageTitle) > 0) + assert (len(result2.imageTitle) > 0) print(result2.artist.PrintInfo()) - assert(len(result2.artist.artistToken) > 0) - assert("R-18" in result2.imageTags) + assert (len(result2.artist.artistToken) > 0) + assert ("R-18" in result2.imageTags) print(">> ugoira") (result3, page3) = b.getImagePage(60070169) print(result3.PrintInfo()) - assert(len(result3.imageTitle) > 0) + assert (len(result3.imageTitle) > 0) print(result3.artist.PrintInfo()) print(result3.ugoira_data) - assert(len(result3.artist.artistToken) > 0) - assert(result3.imageMode == 'ugoira_view') + assert (len(result3.artist.artistToken) > 0) + assert (result3.imageMode == 'ugoira_view') def testMember(): print("Test member mode") @@ -912,26 +1017,26 @@ def testMember(): (result3, page3) = b.getMemberPage( 1227869, page=1, bookmark=False, tags=None) print(result3.PrintInfo()) - assert(len(result3.artistToken) > 0) - assert(len(result3.imageList) > 0) + assert (len(result3.artistToken) > 0) + assert (len(result3.imageList) > 0) print(">>") (result4, page4) = b.getMemberPage( 1227869, page=2, bookmark=False, tags=None) print(result4.PrintInfo()) - assert(len(result4.artistToken) > 0) - assert(len(result4.imageList) > 0) + assert (len(result4.artistToken) > 0) + assert (len(result4.imageList) > 0) print(">>") (result5, page5) = b.getMemberPage( 4894, page=1, bookmark=False, tags=None) print(result5.PrintInfo()) - assert(len(result5.artistToken) > 0) - assert(len(result5.imageList) > 0) + assert (len(result5.artistToken) > 0) + assert (len(result5.imageList) > 0) print(">>") (result6, page6) = b.getMemberPage( 4894, page=3, bookmark=False, tags=None) print(result6.PrintInfo()) - assert(len(result6.artistToken) > 0) - assert(len(result6.imageList) > 0) + assert (len(result6.artistToken) > 0) + assert (len(result6.imageList) > 0) def testMemberBookmark(): print("Test member bookmarks mode") @@ -939,14 +1044,14 @@ def testMemberBookmark(): (result5, page5) = b.getMemberPage( 1227869, page=1, bookmark=True, tags=None) print(result5.PrintInfo()) - assert(len(result5.artistToken) > 0) - assert(len(result5.imageList) > 0) + assert (len(result5.artistToken) > 0) + assert (len(result5.imageList) > 0) print(">>") (result6, page6) = b.getMemberPage( 1227869, page=2, bookmark=True, tags=None) print(result6.PrintInfo()) - assert(len(result6.artistToken) > 0) - assert(len(result6.imageList) > 0) + assert (len(result6.artistToken) > 0) + assert (len(result6.imageList) > 0) print(">>") (result6, page6) = b.getMemberPage( 1227869, page=10, bookmark=True, tags=None) @@ -956,8 +1061,8 @@ def testMemberBookmark(): 1227869, page=12, bookmark=True, tags=None) if result6 is not None: print(result6.PrintInfo()) - assert(len(result6.artistToken) > 0) - assert(len(result6.imageList) == 0) + assert (len(result6.artistToken) > 0) + assert (len(result6.imageList) == 0) # testSearchTags() testImage() diff --git a/PixivConfig.py b/PixivConfig.py index d17e14ae..c9a742e7 100644 --- a/PixivConfig.py +++ b/PixivConfig.py @@ -111,6 +111,7 @@ class PixivConfig(): ConfigItem("Authentication", "username", ""), ConfigItem("Authentication", "password", ""), ConfigItem("Authentication", "cookie", ""), + ConfigItem("Authentication", "cookieFanbox", ""), ConfigItem("Authentication", "refresh_token", ""), ConfigItem("Pixiv", "numberOfPage", 0), diff --git a/PixivDBManager.py b/PixivDBManager.py index 9b8ce0b5..319eb9bd 100644 --- a/PixivDBManager.py +++ b/PixivDBManager.py @@ -85,7 +85,20 @@ def createDatabase(self): PRIMARY KEY (image_id, page) )''') self.conn.commit() - + + # just to keep track of posts, not to record the details, so no columns like saved_to or caption or whatsoever + c.execute('''CREATE TABLE IF NOT EXISTS fanbox_master_post ( + member_id INTEGER, + post_id INTEGER PRIMARY KEY ON CONFLICT IGNORE, + title TEXT, + fee_required INTEGER, + published_date DATE, + updated_date DATE, + post_type TEXT, + last_update_date DATE + )''') + self.conn.commit() + print('done.') except BaseException: print('Error at createDatabase():', str(sys.exc_info())) @@ -102,6 +115,13 @@ def dropDatabase(self): c.execute('''DROP TABLE IF EXISTS pixiv_master_image''') self.conn.commit() + + c.execute('''DROP TABLE IF EXISTS pixiv_manga_image''') + self.conn.commit() + + c.execute('''DROP TABLE IF EXISTS fanbox_master_post''') + self.conn.commit() + except BaseException: print('Error at dropDatabase():', str(sys.exc_info())) print('failed.') @@ -211,6 +231,34 @@ def exportDetailedList(self, filename): c.close() print('done.') + def exportFanboxPostList(self, filename, sep=","): + print('Exporting FANBOX post list...', end=' ') + try: + c = self.conn.cursor() + c.execute('''SELECT * FROM fanbox_master_post + ORDER BY member_id, post_id''') + filename = filename + '.csv' + writer = codecs.open(filename, 'wb', encoding='utf-8') + columns = ['member_id','post_id','title','fee_required','published_date','update_date','post_type','last_update_date'] + writer.write(sep.join(columns)) + writer.write('\r\n') + for row in c: + for string in row: + # Unicode write!! + data = str(string) + writer.write(data) + writer.write(sep) + writer.write('\r\n') + writer.write('###END-OF-FILE###') + writer.close() + except BaseException: + print('Error at exportFanboxPostList(): ' + str(sys.exc_info())) + print('failed') + raise + finally: + c.close() + print('done.') + ########################################## # III. Print DB # ########################################## @@ -519,6 +567,73 @@ def insertMangaImages(self, manga_files): finally: c.close() + def insertPost(self, member_id, post_id, title, fee_required, published_date, post_type): + try: + c = self.conn.cursor() + post_id = int(post_id) + c.execute( + '''INSERT OR IGNORE INTO fanbox_master_post (member_id, post_id) VALUES(?, ?)''', + (member_id, post_id)) + c.execute( + '''UPDATE fanbox_master_post SET title = ?, fee_required = ?, published_date = ?, + post_type = ?, last_update_date = datetime('now') WHERE post_id = ?''', + (title, fee_required, published_date, post_type, post_id)) + self.conn.commit() + except BaseException: + print('Error at insertPost():', str(sys.exc_info())) + print('failed') + raise + finally: + c.close() + + def selectPostByPostId(self, post_id): + try: + c = self.conn.cursor() + post_id = int(post_id) + c.execute( + '''SELECT * FROM fanbox_master_post WHERE post_id = ?''', + (post_id,)) + return c.fetchone() + except BaseException: + print('Error at selectPostByPostId():', str(sys.exc_info())) + print('failed') + raise + finally: + c.close() + + def updatePostLastUpdateDate(self, post_id, updated_date): + try: + c = self.conn.cursor() + post_id = int(post_id) + c.execute( + '''UPDATE fanbox_master_post SET updated_date = ? + WHERE post_id = ?''', + (updated_date, post_id)) + self.conn.commit() + except BaseException: + print('Error at updatePostLastUpdateDate():', str(sys.exc_info())) + print('failed') + raise + finally: + c.close() + + def deleteFanboxPost(self, id, by): + id = int(id) + if by not in ["member_id", "post_id"]: + return + sql = f'''DELETE FROM fanbox_master_post WHERE {by} = ?''' + + try: + c = self.conn.cursor() + c.execute(sql, (id,)) + self.conn.commit() + except BaseException: + print('Error at deleteFanboxPost():', str(sys.exc_info())) + print('failed') + raise + finally: + c.close() + def blacklistImage(self, memberId, ImageId): try: c = self.conn.cursor() @@ -775,12 +890,16 @@ def menu(self): print('12. Blacklist image by image_id') print('13. Show all deleted member') print('===============================================') + print('f1. Export FANBOX post list') + print('f2. Delete FANBOX download history by post_id') + print('f3. Delete FANBOX download history by member_id') + print('===============================================') print('c. Clean Up Database') print('i. Interactive Clean Up Database') print('p. Compact Database') print('r. Replace Root Path') print('x. Exit') - selection = input('Select one?').rstrip("\r") + selection = input('Select one? ').rstrip("\r") return selection def main(self): @@ -859,6 +978,17 @@ def main(self): self.blacklistImage(member_id, image_id) elif selection == '13': self.printMemberList(isDeleted=True) + elif selection == 'f1': + filename = input('Filename? ').rstrip("\r") + sep = input('Separator? (1(default)=",", 2="\\t") ').rstrip("\r") + sep = "\t" if sep=="2" else "," + self.exportFanboxPostList(filename, sep) + elif selection == 'f2': + member_id = input('member_id? ').rstrip("\r") + self.deleteFanboxPost(member_id, "post_id") + elif selection == 'f3': + post_id = input('post_id? ').rstrip("\r") + self.deleteFanboxPost(post_id, "member_id") elif selection == 'c': self.cleanUp() elif selection == 'i': diff --git a/PixivModelFanbox.py b/PixivModelFanbox.py index 1bfd8a9c..7e2d2630 100644 --- a/PixivModelFanbox.py +++ b/PixivModelFanbox.py @@ -6,46 +6,55 @@ import demjson from bs4 import BeautifulSoup -import datetime_z import PixivHelper +import datetime_z from PixivException import PixivException -class Fanbox(object): - supportedArtist = None - - def __init__(self, page): - js = demjson.decode(page) - - if "error" in js and js["error"]: - raise PixivException("Error when requesting Fanbox", 9999, page) - - if "body" in js and js["body"] is not None: - self.parseSupportedArtists(js["body"]) - - def parseSupportedArtists(self, js_body): - self.supportedArtist = list() - # Fix #495 - if "supportingPlans" in js_body: - js_body = js_body["supportingPlans"] - for creator in js_body: - self.supportedArtist.append(int(creator["user"]["userId"])) - - class FanboxArtist(object): artistId = 0 - posts = None + creatorId = "" nextUrl = None hasNextPage = False _tzInfo = None - # require additional API call artistName = "" artistToken = "" - def __init__(self, artist_id, page, tzInfo=None): + SUPPORTED = 0 + FOLLOWED = 1 + + @classmethod + def parseArtists(cls, page, tzInfo=None): + artists = list() + js = demjson.decode(page) + + if "error" in js and js["error"]: + raise PixivException("Error when requesting Fanbox", 9999, page) + + if "body" in js and js["body"] is not None: + js_body = js["body"] + if "supportingPlans" in js["body"]: + js_body = js_body["supportingPlans"] + for creator in js_body: + artists.append( + FanboxArtist(creator["user"]["userId"], + creator["user"]["name"], + creator["creatorId"], + tzInfo=tzInfo + )) + return artists + + def __init__(self, artist_id, artist_name, creator_id, tzInfo=None): self.artistId = int(artist_id) + self.artistName = artist_name + self.creatorId = creator_id self._tzInfo = tzInfo + + def __str__(self): + return f"({self.artistId}, {self.creatorId}, {self.artistName})" + + def parsePosts(self, page): js = demjson.decode(page) if "error" in js and js["error"]: @@ -53,32 +62,33 @@ def __init__(self, artist_id, page, tzInfo=None): "Error when requesting Fanbox artist: {0}".format(self.artistId), 9999, page) if js["body"] is not None: - self.parsePosts(js["body"]) + js_body = js["body"] - def parsePosts(self, js_body): - self.posts = list() + posts = list() - if "creator" in js_body: - self.artistName = js_body["creator"]["user"]["name"] + if "creator" in js_body: + self.artistName = js_body["creator"]["user"]["name"] - if "post" in js_body: - # new api - post_root = js_body["post"] - else: - # https://www.pixiv.net/ajax/fanbox/post?postId={0} - # or old api - post_root = js_body + if "post" in js_body: + # new api + post_root = js_body["post"] + else: + # https://www.pixiv.net/ajax/fanbox/post?postId={0} + # or old api + post_root = js_body - for jsPost in post_root["items"]: - post_id = int(jsPost["id"]) - post = FanboxPost(post_id, self, jsPost, tzInfo=self._tzInfo) - self.posts.append(post) - # sanity check - assert (self.artistId == int(jsPost["user"]["userId"])), "Different user id from constructor!" + for jsPost in post_root["items"]: + post_id = int(jsPost["id"]) + post = FanboxPost(post_id, self, jsPost, tzInfo=self._tzInfo) + posts.append(post) + # sanity check + assert (self.artistId == int(jsPost["user"]["userId"])), "Different user id from constructor!" - self.nextUrl = post_root["nextUrl"] - if self.nextUrl is not None and len(self.nextUrl) > 0: - self.hasNextPage = True + self.nextUrl = post_root["nextUrl"] + if self.nextUrl is not None and len(self.nextUrl) > 0: + self.hasNextPage = True + + return posts class FanboxPost(object): @@ -87,7 +97,8 @@ class FanboxPost(object): coverImageUrl = "" worksDate = "" worksDateDateTime = None - updatedDatetime = "" + updatedDate = "" + updatedDateDatetime = None # image|text|file|article|video|entry _supportedType = ["image", "text", "file", "article", "video", "entry"] type = "" @@ -96,14 +107,14 @@ class FanboxPost(object): likeCount = 0 parent = None is_restricted = False - + feeRequired = 0 # compatibility imageMode = "" imageCount = 0 _tzInfo = None linkToFile = None - + # not implemented worksResolution = "" worksTools = "" @@ -121,9 +132,9 @@ def __init__(self, post_id, parent, page, tzInfo=None): self.imageId = int(post_id) self.parent = parent self._tzInfo = tzInfo - + self.linkToFile = dict() - + self.parsePost(page) if not self.is_restricted: @@ -148,12 +159,17 @@ def parsePost(self, jsPost): self.worksDate = jsPost["publishedDatetime"] self.worksDateDateTime = datetime_z.parse_datetime(self.worksDate) + self.updatedDate = jsPost["updatedDatetime"] + self.updatedDateDatetime = datetime_z.parse_datetime(self.updatedDate) + + if "feeRequired" in jsPost: + self.feeRequired = jsPost["feeRequired"] + # Issue #420 if self._tzInfo is not None: self.worksDateDateTime = self.worksDateDateTime.astimezone( self._tzInfo) - self.updatedDatetime = jsPost["updatedDatetime"] self.type = jsPost["type"] if self.type not in FanboxPost._supportedType: raise PixivException("Unsupported post type = {0} for post = {1}".format( @@ -221,30 +237,30 @@ def parseBody(self, jsPost): elif block["type"] == "image": imageId = block["imageId"] self.body_text = u"{0}
".format( - self.body_text, - jsPost["body"]["imageMap"][imageId]["originalUrl"], - jsPost["body"]["imageMap"][imageId]["thumbnailUrl"]) + self.body_text, + jsPost["body"]["imageMap"][imageId]["originalUrl"], + jsPost["body"]["imageMap"][imageId]["thumbnailUrl"]) self.try_add(jsPost["body"]["imageMap"][imageId]["originalUrl"], self.images) self.try_add(jsPost["body"]["imageMap"][imageId]["originalUrl"], self.embeddedFiles) elif block["type"] == "file": fileId = block["fileId"] self.body_text = u"{0}
{2}".format( - self.body_text, - jsPost["body"]["fileMap"][fileId]["url"], - jsPost["body"]["fileMap"][fileId]["name"]) + self.body_text, + jsPost["body"]["fileMap"][fileId]["url"], + jsPost["body"]["fileMap"][fileId]["name"]) self.try_add(jsPost["body"]["fileMap"][fileId]["url"], self.images) self.try_add(jsPost["body"]["fileMap"][fileId]["url"], self.embeddedFiles) elif block["type"] == "embed": # Implement #470 embedId = block["embedId"] self.body_text = u"{0}
{1}".format( - self.body_text, - self.getEmbedData(jsPost["body"]["embedMap"][embedId], jsPost)) + self.body_text, + self.getEmbedData(jsPost["body"]["embedMap"][embedId], jsPost)) # Issue #476 if "video" in jsPost["body"]: self.body_text = u"{0}
{1}".format( - self.body_text, - self.getEmbedData(jsPost["body"]["video"], jsPost)) + self.body_text, + self.getEmbedData(jsPost["body"]["video"], jsPost)) def getEmbedData(self, embedData, jsPost): if not os.path.exists("content_provider.json"): @@ -296,6 +312,13 @@ def try_add(self, item, list_data): if item not in list_data: list_data.append(item) + def printPost(self): + print("Post = {0}".format(self.imageId)) + print("Title = {0}".format(self.imageTitle)) + print("Type = {0}".format(self.type)) + print("Created Date = {0}".format(self.worksDate)) + print("Is Restricted = {0}".format(self.is_restricted)) + def WriteInfo(self, filename): info = None try: diff --git a/PixivUtil2.py b/PixivUtil2.py index 464d49f5..73284d81 100755 --- a/PixivUtil2.py +++ b/PixivUtil2.py @@ -1514,7 +1514,9 @@ def menu(): print('12. Download by Group Id') print('------------------------') print('f1. Download from supported artists (FANBOX)') - print('f2. Download by artist id (FANBOX)') + print('f2. Download by artist/creator id (FANBOX)') + print('f3. Download by post id (FANBOX)') + print('f4. Download from followed artists (FANBOX)') print('------------------------') print('d. Manage database') print('e. Export online bookmark') @@ -1912,8 +1914,14 @@ def menu_export_online_user_bookmark(opisvalid, args): export_bookmark(filename, 'n', 1, 0, member_id) -def menu_fanbox_download_supported_artist(op_is_valid, args): - __log__.info('Download FANBOX Supported Artists mode.') +def menu_fanbox_download_from_artist(op_is_valid, via, args): + via_type = "" + if via == PixivModelFanbox.FanboxArtist.SUPPORTED: + via_type = "supported" + elif via == PixivModelFanbox.FanboxArtist.FOLLOWED: + via_type = "followed" + + __log__.info(f'Download FANBOX {via_type.capitalize()} Artists mode.') end_page = 0 if op_is_valid and len(args) > 0: @@ -1922,94 +1930,126 @@ def menu_fanbox_download_supported_artist(op_is_valid, args): end_page = input("Max Page = ").rstrip("\r") or 0 end_page = int(end_page) - result = __br__.fanboxGetSupportedUsers() - if len(result.supportedArtist) == 0: - PixivHelper.print_and_log("info", "No supported artist!") + fanbox_login = __br__.fanboxLoginUsingCookie() + if not(fanbox_login): + __log__.info("FANBOX login cookie string invalid, please update in config.ini") + return + + artists = __br__.fanboxGetUsers(via) + if len(artists) == 0: + PixivHelper.print_and_log("info", f"No {via_type} artist!") return - PixivHelper.print_and_log("info", "Found {0} supported artist(s)".format(len(result.supportedArtist))) - print(result.supportedArtist) + PixivHelper.print_and_log("info", f"Found {len(artists)} {via_type} artist(s)") + print(", ".join(str(artists))) - for artist_id in result.supportedArtist: + for artist in artists: + __br__.fanboxUpdateArtistToken(artist) # Issue #567 try: - processFanboxArtist(artist_id, end_page) + processFanboxArtist(artist, end_page) + except PixivException as pex: + PixivHelper.print_and_log("error", f"Error processing {via_type} FANBOX Artist: {artist.artistId} ==> {pex.message}") + + +def menu_fanbox_download_by_post_id(op_is_valid, args): + __log__.info('Download FANBOX by post id mode.') + if op_is_valid and len(args) > 0: + post_ids = args + else: + post_ids = input("Post ids = ").rstrip("\r") or 0 + + post_ids = PixivHelper.get_ids_from_csv(post_ids, sep=" ") + for post_id in post_ids: + post_id = int(post_id) + post = __br__.fanboxGetPost(post_id) + try: + processFanboxImages(post, post.parent) except PixivException as pex: - PixivHelper.print_and_log("error", "Error processing FANBOX Artist: {0} ==> {1}".format(artist_id, pex.message)) + PixivHelper.print_and_log("error", "Error processing FANBOX post: {0} ==> {1}".format(post_id, pex.message)) + del post -def processFanboxArtist(artist_id, end_page): +def processFanboxArtist(artist, end_page): current_page = 1 next_url = None image_count = 1 - while(True): - PixivHelper.print_and_log("info", "Processing {0}, page {1}".format(artist_id, current_page)) + while (True): + PixivHelper.print_and_log("info", "Processing {0}, page {1}".format(artist, current_page)) try: - result_artist = __br__.fanboxGetPostsFromArtist(artist_id, next_url) + posts = __br__.fanboxGetPostsFromArtist(artist, next_url) except PixivException as pex: print(pex) break - for post in result_artist.posts: + for post in posts: print("#{0}".format(image_count)) - print("Post = {0}".format(post.imageId)) - print("Title = {0}".format(post.imageTitle)) - print("Type = {0}".format(post.type)) - print("Created Date = {0}".format(post.worksDate)) - print("Is Restricted = {0}".format(post.is_restricted)) - # cover image - if post.coverImageUrl is not None: - # fake the image_url for filename compatibility, add post id and pagenum - fake_image_url = post.coverImageUrl.replace("{0}/cover/".format(post.imageId), "{0}_".format(post.imageId)) - filename = PixivHelper.make_filename(__config__.filenameFormat, - post, - artistInfo=result_artist, - tagsSeparator=__config__.tagsSeparator, - tagsLimit=__config__.tagsLimit, - fileUrl=fake_image_url, - bookmark=None, - searchTags='') - filename = PixivHelper.sanitize_filename(filename, __config__.rootDirectory) - - post.linkToFile[post.coverImageUrl] = filename - - print("Downloading cover from {0}".format(post.coverImageUrl)) - print("Saved to {0}".format(filename)) - - referer = "https://www.pixiv.net/fanbox/creator/{0}/post/{1}".format(artist_id, post.imageId) - # don't pass the post id and page number to skip db check - (result, filename) = download_image(post.coverImageUrl, - filename, - referer, - __config__.overwrite, - __config__.retry, - __config__.backupOldFile) - PixivHelper.get_logger().debug("Download %s result: %s", filename, result) - - else: - PixivHelper.print_and_log("info", "No Cover Image for post: {0}.".format(post.imageId)) + post.printPost() # images if post.type in PixivModelFanbox.FanboxPost._supportedType: - processFanboxImages(post, result_artist) + processFanboxImages(post, artist) image_count = image_count + 1 - if not result_artist.hasNextPage: - PixivHelper.print_and_log("info", "No more post for {0}".format(artist_id)) + if not artist.hasNextPage: + PixivHelper.print_and_log("info", "No more post for {0}".format(artist)) break current_page = current_page + 1 if end_page > 0 and current_page > end_page: - PixivHelper.print_and_log("info", "Reaching page limit for {0}, limit {1}".format(artist_id, end_page)) + PixivHelper.print_and_log("info", "Reaching page limit for {0}, limit {1}".format(artist, end_page)) break - next_url = result_artist.nextUrl + next_url = artist.nextUrl if next_url is None: - PixivHelper.print_and_log("info", "No more next page for {0}".format(artist_id)) + PixivHelper.print_and_log("info", "No more next page for {0}".format(artist)) break -def processFanboxImages(post, result_artist): +def processFanboxImages(post, artist): + __dbManager__.insertPost(artist.artistId, post.imageId, post.imageTitle, + post.feeRequired, post.worksDate, post.type) if post.is_restricted: PixivHelper.print_and_log("info", "Skipping post: {0} due to restricted post.".format(post.imageId)) return + + result = __dbManager__.selectPostByPostId(post.imageId) + if result: + updated_date = result[5] + if updated_date is not None and post.updatedDateDatetime <= datetime_z.parse_datetime(updated_date): + PixivHelper.print_and_log("info", + "Skipping post: {0} bacause it was downloaded before.".format(post.imageId)) + return + + # cover image + if post.coverImageUrl is not None: + # fake the image_url for filename compatibility, add post id and pagenum + fake_image_url = post.coverImageUrl.replace("{0}/cover/".format(post.imageId), + "{0}_".format(post.imageId)) + filename = PixivHelper.make_filename(__config__.filenameFormat, + post, + artistInfo=artist, + tagsSeparator=__config__.tagsSeparator, + tagsLimit=__config__.tagsLimit, + fileUrl=fake_image_url, + bookmark=None, + searchTags='') + filename = PixivHelper.sanitize_filename(filename, __config__.rootDirectory) + + post.linkToFile[post.coverImageUrl] = filename + + print("Downloading cover from {0}".format(post.coverImageUrl)) + print("Saved to {0}".format(filename)) + + referer = "https://www.pixiv.net/fanbox/creator/{0}/post/{1}".format(artist.artistId, post.imageId) + # don't pass the post id and page number to skip db check + (result, filename) = download_image(post.coverImageUrl, + filename, + referer, + __config__.overwrite, + __config__.retry, + __config__.backupOldFile) + PixivHelper.get_logger().debug("Download %s result: %s", filename, result) + else: + PixivHelper.print_and_log("info", "No Cover Image for post: {0}.".format(post.imageId)) + if post.images is None or len(post.images) == 0: PixivHelper.print_and_log("info", "No Image available in post: {0}.".format(post.imageId)) # return @@ -2018,21 +2058,22 @@ def processFanboxImages(post, result_artist): print("Image Count = {0}".format(len(post.images))) for image_url in post.images: # fake the image_url for filename compatibility, add post id and pagenum - fake_image_url = image_url.replace("{0}/".format(post.imageId), "{0}_p{1}_".format(post.imageId, current_page)) + fake_image_url = image_url.replace("{0}/".format(post.imageId), + "{0}_p{1}_".format(post.imageId, current_page)) filename = PixivHelper.make_filename(__config__.filenameMangaFormat, - post, - artistInfo=result_artist, - tagsSeparator=__config__.tagsSeparator, - tagsLimit=__config__.tagsLimit, - fileUrl=fake_image_url, - bookmark=None, - searchTags='') + post, + artistInfo=artist, + tagsSeparator=__config__.tagsSeparator, + tagsLimit=__config__.tagsLimit, + fileUrl=fake_image_url, + bookmark=None, + searchTags='') filename = PixivHelper.sanitize_filename(filename, __config__.rootDirectory) post.linkToFile[image_url] = filename - referer = "https://www.pixiv.net/fanbox/creator/{0}/post/{1}".format(result_artist.artistId, post.imageId) + referer = "https://www.pixiv.net/fanbox/creator/{0}/post/{1}".format(artist.artistId, post.imageId) print("Downloading image {0} from {1}".format(current_page, image_url)) print("Saved to {0}".format(filename)) @@ -2054,13 +2095,13 @@ def processFanboxImages(post, result_artist): # Implement #447 filename = PixivHelper.make_filename(__config__.filenameInfoFormat, - post, - artistInfo=result_artist, - tagsSeparator=__config__.tagsSeparator, - tagsLimit=__config__.tagsLimit, - fileUrl="{0}".format(post.imageId), - bookmark=None, - searchTags='') + post, + artistInfo=artist, + tagsSeparator=__config__.tagsSeparator, + tagsLimit=__config__.tagsLimit, + fileUrl="{0}".format(post.imageId), + bookmark=None, + searchTags='') filename = PixivHelper.sanitize_filename(filename, __config__.rootDirectory) if __config__.writeImageInfo: @@ -2073,23 +2114,25 @@ def processFanboxImages(post, result_artist): reader.close() post.WriteHtml(html_template, __config__.useAbsolutePathsInHtml, filename + ".html") + __dbManager__.updatePostLastUpdateDate(post.imageId, post.updatedDate) -def menu_fanbox_download_by_artist_id(op_is_valid, args): - __log__.info('Download FANBOX by Artist ID mode.') + +def menu_fanbox_download_by_artist_or_creator_id(op_is_valid, args): + __log__.info('Download FANBOX by Artist or Creator ID mode.') end_page = 0 - artist_id = '' + id = '' if op_is_valid and len(args) > 0: - artist_id = str(int(args[0])) + id = args[0] if len(args) > 1: end_page = args[1] else: - artist_id = input("Artist ID = ").rstrip("\r") + id = input("Artist/Creator ID = ").rstrip("\r") end_page = input("Max Page = ").rstrip("\r") or 0 end_page = int(end_page) - - processFanboxArtist(artist_id, end_page) + artist = __br__.fanboxGetArtistById(id) + processFanboxArtist(artist, end_page) def menu_reload_config(): @@ -2109,7 +2152,7 @@ def set_console_title(title=''): def setup_option_parser(): global __valid_options - __valid_options = ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', 'f1', 'f2', 'd', 'e', 'm') + __valid_options = ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', 'f1', 'f2', 'f3', 'f4', 'd', 'e', 'm') parser = OptionParser() parser.add_option('-s', '--startaction', dest='startaction', help='''Action you want to load your program with: @@ -2126,7 +2169,9 @@ def setup_option_parser(): 11 - Download images from Member Bookmark 12 - Download images by Group Id f1 - Download from supported artists (FANBOX) -f2 - Download by artist id (FANBOX) +f2 - Download by artist/creator id (FANBOX) +f3 - Download by post id (FANBOX) +f4 - Download from followed artists (FANBOX) e - Export online bookmark m - Export online user bookmark d - Manage database''') @@ -2204,9 +2249,13 @@ def main_loop(ewd, op_is_valid, selection, np_is_valid_local, args): menu_import_list() # PIXIV FANBOX elif selection == 'f1': - menu_fanbox_download_supported_artist(op_is_valid, args) + menu_fanbox_download_from_artist(op_is_valid, PixivModelFanbox.FanboxArtist.SUPPORTED, args) elif selection == 'f2': - menu_fanbox_download_by_artist_id(op_is_valid, args) + menu_fanbox_download_by_artist_or_creator_id(op_is_valid, args) + elif selection == 'f3': + menu_fanbox_download_by_post_id(op_is_valid, args) + elif selection == 'f4': + menu_fanbox_download_from_artist(op_is_valid, PixivModelFanbox.FanboxArtist.FOLLOWED, args) # END PIXIV FANBOX elif selection == '-all': if not np_is_valid_local: @@ -2467,6 +2516,9 @@ def main(): __log__.info(msg) result = doLogin(password, username) + fanbox_login = __br__.fanboxLoginUsingCookie() + if not (fanbox_login): + __log__.info("FANBOX login cookie string invalid, please update in config.ini") if result: np_is_valid, op_is_valid, selection = main_loop(ewd, op_is_valid, selection, np_is_valid, args) diff --git a/test/Fanbox_supported_artist.json b/test/Fanbox_supported_artist.json index df80d261..cba8db16 100644 --- a/test/Fanbox_supported_artist.json +++ b/test/Fanbox_supported_artist.json @@ -1,732 +1,731 @@ { - "body": [ - { - "id": "10930", - "title": "「好きな作家さんの同人誌をコレで買いなさい」または「画材の足しにして」", - "fee": 500, - "description": "月に一冊同人誌をおごってくれる素敵な人(← 好き♪\r\nまたは画材代を支援してお絵かきを応援してくれる方(← 好き♪\r\n限定公開のHな絵が見れる。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/10930/cover/PZ07QSsQSzOe4sJ0y56GEKNK.jpeg", - "user": { - "userId": "4820", - "name": "猫耳 花音", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/4820/icon/AvB7gFWmDvmgaeDFB48XHXfA.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "50037", - "title": "500円", - "fee": 500, - "description": "投稿から6か月たったものはこちらに移動します。それとたまに500円限定の差分イラストを上げます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/50037/cover/QfOXflbPPvxdR4E59GMnvLG3.jpeg", - "user": { - "userId": "7968", - "name": "伊藤達哉", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/7968/icon/3TXFEP8ELMnaLhkS60eqEDH6.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "38114", - "title": "ラムネプラン", - "fee": 100, - "description": "双木こじろにブドウ糖90%のラムネを食べさせるプランです。\r\nペンタブの芯や絵の資料の資金になることもあります。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/38114/cover/8AE1M6JTYutnFLfUeRsE0ofL.jpeg", - "user": { - "userId": "11443", - "name": "双木こじろ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/11443/icon/quTymdIRXnfjFKEhlDVUM9ba.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "15330", - "title": "調教支援:ピンクローター", - "fee": 250, - "description": "あかいしの調教にピンクローターの力が加わり\nあかいしの戦闘力がちょっとだけ高まります。", - "coverImageUrl": null, - "user": { - "userId": "91029", - "name": "あかいししろいし", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/91029/icon/88cadMWRtY4Rak2TYCO2nxra.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "23834", - "title": "ドリンクタイム", - "fee": 200, - "description": "赤い牛とか怪物力を買います。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/23834/cover/VLnjWLNnz8n3pFfm6YyZPTu1.jpeg", - "user": { - "userId": "151050", - "name": "わっちー", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/151050/icon/530GbYo4o0fCjt6tiUHT6RCt.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "22444", - "title": "サポートA", - "fee": 200, - "description": "通常投稿作品のR18バージョンや高解像度版、長い動画(うごくイラスト)が閲覧できます。支援金はソフトウエア購入、使用代金に使わせていただきます。\r\nYou can view R18 version of the contribution work, high resolution version, long video (Ugoku_illustrations). I will use your donations for software purchase and usage fee.", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/22444/cover/CXjsJ3oeXNFcy2iYlGXhm26N.jpeg", - "user": { - "userId": "195364", - "name": "tooo", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/195364/icon/nbyDJpVBz9jMsBgN9lafj0eP.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "7329", - "title": "ストパンエロ絵置き場(ダウンロード用)", - "fee": 250, - "description": "趣味で描いてPIXIVに上げたエロ絵や、その差分を投稿する場所。無料のところとほとんど変わらないから特に有料プランに入る必要はないけど、ダウンロードしやすいよう差分ごとファイルにまとめるよ(断面図や拡大図付き差分ファイル追加するかも)。\n後、以前描いたエロ絵のリメイクと差分をのっけるかも。最低月1更新。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/7329/cover/n91lwh6eAjcV7w2MRah4Nmao.jpeg", - "user": { - "userId": "226267", - "name": "髭侍", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/226267/icon/sxsTDCwrUQwBj6uRuWKMAzj4.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "57409", - "title": "もっとお絵描き頑張ってね☆プラン", - "fee": 500, - "description": "特になにもありませんが、日頃のお絵描きをもっと頑張る気力が更に沸きます", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/57409/cover/cI6qBbgItDvHrHPOyx66gpS0.jpeg", - "user": { - "userId": "376735", - "name": "るいるい", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/376735/icon/GGTIP2kFojXxeHJE7cEcRva6.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "19421", - "title": " Just a pay some Rewards page,No payment content", - "fee": 100, - "description": "何もない!!!!!!!!!!!!!!!!!!!!\r\n只是一個打賞的頁面。\r\n沒有任何付費内容\r\nJust a paying some Tips/Reward's page.\r\nHas no payment content\r\nただ、いくつかのヒントのページを支払います。(machine)\r\n\r\n", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/19421/cover/Dr7lHtCssipQnCNLNFIMIgAz.jpeg", - "user": { - "userId": "634336", - "name": "喵行燈[pass]", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/634336/icon/YLe6B5XLbiWtXHiWVzn0WkJx.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "64153", - "title": "ラーメンプラン", - "fee": 500, - "description": "みかぐらがお昼ご飯にラーメンを食べられます。\npixivに投稿したイラストの高解像度版や未公開差分、限定公開イラストなど", - "coverImageUrl": null, - "user": { - "userId": "642151", - "name": "みかぐら", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/642151/icon/fpHzu25VUYF3y5y5kFBjMdjV.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "25985", - "title": "やる気スイッチ", - "fee": 300, - "description": "落書き・ラフ・高画質版が閲覧できます", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/25985/cover/jMlX9E92sLdTUyFjS9F30tYD.jpeg", - "user": { - "userId": "1030278", - "name": "とりあっとぐぬぬ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1030278/icon/lhS7qcfvYdo217eQunGlejQ1.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "43196", - "title": "エキスパートエディション", - "fee": 500, - "description": "マンガ形式の差分イラストなどを考えています。\r\n制作者は煮干しつけ麺が食べられます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/43196/cover/Xil20skVpr2sWsWVzZ1alOtt.jpeg", - "user": { - "userId": "1046936", - "name": "アブトマト", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1046936/icon/0iBaWBh8JeNE7NHtXJnVnU3R.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "29051", - "title": "18禁っぽいの", - "fee": 200, - "description": "スク水娘を描く上でスク水をまだ着ていないスク水少女とか見れます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/29051/cover/njuKDrsR3Ls7I9c4hoSHj1ZB.jpeg", - "user": { - "userId": "1094478", - "name": "ろひつか", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1094478/icon/PQCsaGKcNV59I2ilU3sFCIZY.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "77592", - "title": "500", - "fee": 500, - "description": "10月はPatreonの新作を中心に過去作品など投稿したいと思います。スケッチ、完成絵、モノクロなど含まれます。\nIn Octorber we will post new works mainly with past works of Patreon. Includes sketches, full shade, and monochrome.\n", - "coverImageUrl": null, - "user": { - "userId": "1131293", - "name": "Tenebre Publisher (CoCo)", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1131293/icon/9eY25HnINd09fbu9biGhbsTI.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "32339", - "title": "Plan 5", - "fee": 500, - "description": "PIXIV公開画の差分画や、支援者様限定画など全てをご覧になれます。", - "coverImageUrl": null, - "user": { - "userId": "1149958", - "name": "カミマキ ツカミ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1149958/icon/6waWjowBUvtCKTEXti9Qw1Ql.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "26356", - "title": "えびみそプラン", - "fee": 500, - "description": "18禁や特殊な性癖のイラストや短編の漫画を不定期に掲載していきたいです。ご支援いただけたらうれしいです。※ファンティアと投稿内容は同じです。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/26356/cover/ikiDiVF4usteRczoPkeH6Bn3.jpeg", - "user": { - "userId": "1276620", - "name": "いしみそ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1276620/icon/wqNjXH7IaRvWLxjcbmSuTc1h.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "51601", - "title": "実績解除:戦友", - "fee": 150, - "description": "ベア猫の作戦行動を援護した。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51601/cover/jg1YBTxD9cP3ruDvItU2uSzl.jpeg", - "user": { - "userId": "1368840", - "name": "ベア猫(遺伝子組み換えでない)", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1368840/icon/TcKSISCf2q5TTASuxlBOmmnn.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "79834", - "title": "投げ銭(小3)", - "fee": 300, - "description": "女子小学3年生向けのプランです。\n山田がお弁当を1つ食べられます。", - "coverImageUrl": null, - "user": { - "userId": "1769484", - "name": "山田の性活が第一", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1769484/icon/IXZrfHhSuDIGVTDihj0DLqYu.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "71632", - "title": "圧力", - "fee": 100, - "description": "創作活動を促す圧力がかかります。\r\n稀にイラストの原寸公開やラフ投稿があるかもしれません。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/71632/cover/l2SLNfM0ZvdwsCIHKhQQal6m.jpeg", - "user": { - "userId": "1831980", - "name": "pxkanif + huyusilver", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1831980/icon/0iYlM8p0zO477c7Tj2wW91ih.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "54568", - "title": "一筆に紅茶一杯のプラン", - "fee": 498, - "description": "エッチな絵の原動力に、一杯の紅茶を支援頂きたいプランです\npixivに投稿している絵の高解像度版や差分などを提供させていただきます\nあなたのご支援とお心に大変感謝いたします ", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/54568/cover/f2v7DFOuH0pDEM94Kqa5wVLL.jpeg", - "user": { - "userId": "2221925", - "name": "真綿", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/2221925/icon/TnrEk24ySSCTbgZxoiBCvrMU.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "62985", - "title": "300", - "fee": 300, - "description": "", - "coverImageUrl": null, - "user": { - "userId": "2408551", - "name": "ぽこにゃん", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/2408551/icon/SkhA1alyqbhMNs07daiHAbdc.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "51378", - "title": "毎月緊縛プラン", - "fee": 200, - "description": "(毎月更新しましたら古いほうを削除する予定です。お早めに保存して頂きますようお願いいたします。)\r\nFanbox始めました!!試運転として、月額200円で、毎月独占でお縛りのオリジナル画像を一枚上げてきます!基本、話題な女の子(アニメ、ソシャゲー中心)で書こうと思います!どうぞよろしくお願いします!!", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51378/cover/kKIN01S5y3jpV3zPyXWFi3e9.jpeg", - "user": { - "userId": "3452804", - "name": "HaneRu", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/3452804/icon/b4AWDuOCKPCsgxsdYYURLlSL.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "78034", - "title": "エネルギー注入プラン Patreon 300", - "fee": 300, - "description": "支援が多いほど早く描けます~(わがまま \r\n不定期で未公開ラフを支援者だけに公開します", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/78034/cover/uaBHEnYIhTyAKaDBvVEN0ZAx.jpeg", - "user": { - "userId": "4417141", - "name": "クロニ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/4417141/icon/hnNvWHyfFOXYGPl1vFTm2wKu.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "87526", - "title": "普通に応援", - "fee": 600, - "description": "普段のラフやイラストを観ることができます。\n気軽に応援プランより、月にもう一枚のスペシャルイラストを観ることがきるようになります。\nショットストーリーの1ー2ページを観ることができます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/87526/cover/6R5J5c5FohoeZ3BZoAHUZOyv.jpeg", - "user": { - "userId": "4704179", - "name": "万天気像", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/4704179/icon/1HlyC5Axr3StL3XQUHZvpQDj.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "70032", - "title": "PSD元ファイルを取得", - "fee": 750, - "description": "可以得到作者本月新作的PSD原文件\n作者の今月新作のPSDファイルを入手できます。\nget the original PSD file that the author wrote this month.", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/70032/cover/WhaKe10PO8BWn7ploP688BW4.jpeg", - "user": { - "userId": "5165814", - "name": "神奇火鸡(Wonder Turkey)", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/5165814/icon/NzsxKESsUOsH2gkEn8kxCFl8.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "14795", - "title": "見放題プラン", - "fee": 300, - "description": "見放題プランです。", - "coverImageUrl": null, - "user": { - "userId": "5664611", - "name": "Θωθ", - "iconUrl": null - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "95835", - "title": "Previews of drawing in progress.", - "fee": 150, - "description": "Sketches with news and ideas of drawings I am currently working on. \r\nJist like in patreon. :3\r\nMight also post colored progress reports of larger projects. ^^", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/95835/cover/s0tOyvCCcNXA2V24lAAwPcg7.jpeg", - "user": { - "userId": "5944252", - "name": "Hotel01", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/5944252/icon/R6Gu6354EmTMQO6NP97eDVdC.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "59121", - "title": "毎月一枚r18同人プラン", - "fee": 200, - "description": "月末で一枚以上のr18同人イラストを限定公開します!テーマは今期アニメや、ゲーム等、ネトラレ、陵辱等もあります!", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/59121/cover/kf5nD3JrXkjABocaaDgY9Mkb.jpeg", - "user": { - "userId": "6049901", - "name": "鬼針草", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/6049901/icon/SHqjwGwp3G3Ya7S3k6o4wfTf.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "39309", - "title": "プラン500", - "fee": 500, - "description": "高解像度イラストを閲覧することができます。\r\nYou can see high resolution images.", - "coverImageUrl": null, - "user": { - "userId": "6357272", - "name": "ろるゐ紳士", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/6357272/icon/XKIw7maSfZV8xAvPHbvL6lnO.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "86864", - "title": "600円プラン", - "fee": 600, - "description": "いつも通り、ファンボックスがpatreonみたいに使います。\n不安定ですが、毎月1差分絵セット保証します、2セットが頑張ります。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/86864/cover/eOEV1YvAzynCfL1aYj08ptOG.jpeg", - "user": { - "userId": "6610818", - "name": "サーモン", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/6610818/icon/uXK2ehrKEL8prJwLx5qebuXF.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "93775", - "title": "It helps me concentrate on my work.", - "fee": 200, - "description": "I will upload a private picture for the fan box once a month.", - "coverImageUrl": null, - "user": { - "userId": "10414967", - "name": "kkta", - "iconUrl": null - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "13242", - "title": "スタンダートプラン", - "fee": 500, - "description": "えっちな絵を見たい方のスタンダートなプランです。\r\nTwitterにはスケベすぎて載せられない支援者限定イラストやエロ差分、文字なし差分などを定期的に載せます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/13242/cover/KqDzOeEmxS2pa78DiuHEGkbB.jpeg", - "user": { - "userId": "10594960", - "name": "弥猫うた", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/10594960/icon/xXk4UqYlTXuHaFN6QEdWWkE2.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "49868", - "title": "お賽銭箱", - "fee": 200, - "description": "pixiv、ツイッター等で上げてる絵の裸verなど上げたいと思います~", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/49868/cover/mZnXCfifwWTrsCxB7W92WGqk.jpeg", - "user": { - "userId": "11229342", - "name": "サインこす", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/11229342/icon/AIfY06L4CPM2WiadhfMlHViT.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "72698", - "title": "喜欢的话给个赞助吧…", - "fee": 200, - "description": "", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/72698/cover/clb46Zb6PrwAHjh17fOHMN3D.jpeg", - "user": { - "userId": "12016484", - "name": "HLL.ALSG99", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/12016484/icon/EaoIZnz0fP6xxkCQ4siTyd4o.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "59173", - "title": "落書き", - "fee": 500, - "description": "ここで私の普段の落書きと受付のイラストの落書きを発表する。落書きほとんどは人体緊縛の姿についての絵です。そしてみんなと一緒にいい場面を構想したいと思います。\r\n我會在這裡發佈一些平時構想的草稿以及約稿的草圖,草圖大部分情況下是突然想到的能用在捆綁里的人體結構 我很樂意和大家一起想後面的構思之類的東西_(:з」∠)_", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/59173/cover/zjFQY2WTAs0bR6MNklWYXBlS.jpeg", - "user": { - "userId": "12063067", - "name": "臣訾", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/12063067/icon/ciyGaxmudPK7wsrqY0kDcwRj.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "51253", - "title": "毎月緊縛プラン", - "fee": 200, - "description": "Fanbox始めました!!試運転として、月額200円で、毎月独占でお縛りのオリジナル画像を一枚上げてきます!基本、話題な女の子(アニメ、ソシャゲー中心)で書こうと思います!どうぞよろしくお願いします!!", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51253/cover/RwTIAoJsAnIWJWNYNAkhEfBg.jpeg", - "user": { - "userId": "13379747", - "name": "ひみつ", - "iconUrl": null - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "94525", - "title": "水月的Fanbox计划", - "fee": 300, - "description": "水月的色图细化计划,应该每月能更新一次吧", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/94525/cover/A0AZiixXTt6NgsQw7NGQTDDF.jpeg", - "user": { - "userId": "13524241", - "name": "水月岩雲ー仕事募集中", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/13524241/icon/8XcQMzKzVLJlydmb1afNBg2w.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "61259", - "title": "レベル3:限定イラスト", - "fee": 1000, - "description": "イラストとマンガ以上 未公開イラスト1枚(縛りと拘束),限定イラストは差分もありよ", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/61259/cover/JQRm1mgrOe5U8xIqPpIExkmE.jpeg", - "user": { - "userId": "17305623", - "name": "雪ノ嵐&异端丶", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/17305623/icon/86rMpiapcC8E7wNGhz8KHghK.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "2399", - "title": "資金援助プラン", - "fee": 500, - "description": "入ってくれる方は神様仏様マミさん\n支援頂いたお金は画材購入、環境整備、同人活動の為に使わせて頂きます。\n現状では頒布物の内容の一部や原稿進捗(全体図、線画など)を投稿して行きます\n何かしらの御礼的コンテンツを考え中(差分絵とか", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/2399/cover/SwWbY8hVcTq2R1LIcRVPonYi.jpeg", - "user": { - "userId": "17813543", - "name": "やんまーみ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/17813543/icon/UYKInBg5X3ExpjAwmcnYZAOG.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "87716", - "title": "とどおが頑張って描こうとするプラン", - "fee": 500, - "description": "100円のプランの内容に加え、過去にpixivで上げた絵の高解像度版やボツ絵などが見れます。\r\nまた、とどおが支援に感謝して無理をしない範囲でがんばろうとします。\r\n", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/87716/cover/fL5I25T8uid0HGiVYH6QjqHN.jpeg", - "user": { - "userId": "18961759", - "name": "とどお", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/18961759/icon/7uqGq9JlT0XjdHSRiBAfs1Wi.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "20046", - "title": "イラスト気に入ったよ的な感じで・・・", - "fee": 300, - "description": "もしもpixiv等でのイラストで気に入った方がいましたら、投げ銭みたいな感じで支援していただけると嬉しいです。特に大きな見返りがあるわけではないのでご注意ください。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/20046/cover/fe1nD773JNAQld3Csw3LurpS.jpeg", - "user": { - "userId": "19116803", - "name": "かなとふ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/19116803/icon/ph0eU9nf0g2RiTyZvo0ZANoJ.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "95894", - "title": "おべんとプラン", - "fee": 500, - "description": "100円のプランに加え、pixivなどで公開したイラストの高解像度版や限定おまけ差分をご覧いただけます。\n\nご支援金は創作活動費として使用されます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/95894/cover/strRAGNfkBdtU6kFOeIcQJNY.jpeg", - "user": { - "userId": "22553630", - "name": "桜屋敷とんこつ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/22553630/icon/b6XKPWx6nZabxwPgFHlpXrC0.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "79440", - "title": "500円プラン", - "fee": 500, - "description": "I will post illustrations for only supporters every month\r\n毎月サポーターのみにイラストを投稿します", - "coverImageUrl": null, - "user": { - "userId": "23311297", - "name": "nekomom", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/23311297/icon/NZTaY6CgR8zHuwywXHfIURqC.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "85396", - "title": "ラフぽいぽい祭り", - "fee": 100, - "description": "ラフを載せまくる。以上です!よろしくお願いします!\r\n1週間に1~2回の更新を目標に頑張ります(。ᵕᴗᵕ。)\r\n", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/85396/cover/H0IqMyCwbwUQOLe6kICNYZA2.jpeg", - "user": { - "userId": "23626451", - "name": "にゆん", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/23626451/icon/c0X6sE0XAWId3KVWQqMhCgar.jpeg" - }, - "hasAdultContent": false, - "paymentMethod": "gmo_card" - }, - { - "id": "84482", - "title": "通常プラン", - "fee": 880, - "description": "現状のところプランは一つだけですので、すべての作品を閲覧することができます。", - "coverImageUrl": null, - "user": { - "userId": "23652509", - "name": "識林ほりう", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/23652509/icon/r4zsbbGehIK6QLuM4dO3iWkJ.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "67599", - "title": "スタンダードプラン", - "fee": 400, - "description": "「継続的に支援したい!」という方向けのスタンダードなプランです。\n\nThis is a standard plan.\nFor those who want to support me continuously.", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/67599/cover/yf2vSvP7JDsigIdHjoFCpevH.jpeg", - "user": { - "userId": "25900946", - "name": "ogyadya", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/25900946/icon/DlFEK7OYdUzSvSdle9EBTEA9.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "57468", - "title": "月刊誌", - "fee": 500, - "description": "お賽銭的なヤツ", - "coverImageUrl": null, - "user": { - "userId": "27569300", - "name": "仁外", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/27569300/icon/A85RkerebOnRg3B0PaLE1c63.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "51441", - "title": "うなぎ丼", - "fee": 600, - "description": "公開しない差分の閲覧が可能になります。その上、毎月独占で拘束の画像を一枚上げてきます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51441/cover/qtSz3auZWlThSBv5TFFJcqQY.jpeg", - "user": { - "userId": "30716447", - "name": "ginklaga", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/30716447/icon/t1uxsE6MApu2Lkn2azwnJvHp.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "48637", - "title": "+他作品", - "fee": 1000, - "description": "「γ-ガンマ-」以外の作品のイラストも観覧出来る様になります。\r\nこちらのプランで「γ-ガンマ-」のイラストも全て観覧出来ます。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/48637/cover/zf7Ia0StIIv9eoDf2Y9H8xGZ.jpeg", - "user": { - "userId": "35438167", - "name": "γ", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/35438167/icon/X7POgmP3psmeubiiglej7qCV.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "82919", - "title": "fan (+3pts/month)", - "fee": 300, - "description": "being a fan you get to see them exclusives c;", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/82919/cover/rgoQIUmWblcjGcjXmSuDu0zg.jpeg", - "user": { - "userId": "40169508", - "name": "crumbles", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/40169508/icon/buR2OoPLjCm1RagEvyHYatyC.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "93759", - "title": "100円TIPS", - "fee": 100, - "description": "full-size picture and the process of my works only! I would be very thankful!\nこちらは原サイズ画像と創作流れしかありません\n只有原尺寸圖與我的感謝(´∀`)", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/93759/cover/BsXC8FH3Wxi3VfVV2OYa8tl5.jpeg", - "user": { - "userId": "42936714", - "name": "BLAM", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/42936714/icon/dOnO2ZaKTk91VORpBchIYAEc.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - }, - { - "id": "96263", - "title": "投げ銭プラン", - "fee": 100, - "description": "投げ銭のプランです。ちょっとしたおまけ等が見れる予定です。プランに興味がなくても、フォローやコメント、コミッション等していただけると励みになります。", - "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/96263/cover/UEN4nNSXxVictnIr2NqM3z2U.jpeg", - "user": { - "userId": "44433394", - "name": "Pita17", - "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/44433394/icon/MjeSA6YIUQHHCAtcAlMAfpht.jpeg" - }, - "hasAdultContent": true, - "paymentMethod": "gmo_card" - } - ] -} \ No newline at end of file + "body": [{ + "id": "10930", + "title": "「好きな作家さんの同人誌をコレで買いなさい」または「画材の足しにして」", + "fee": 500, + "description": "月に一冊同人誌をおごってくれる素敵な人(← 好き♪\r\nまたは画材代を支援してお絵かきを応援してくれる方(← 好き♪\r\n限定公開のHな絵が見れる。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/10930/cover/PZ07QSsQSzOe4sJ0y56GEKNK.jpeg", + "user": { + "userId": "4820", + "name": "猫耳 花音", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/4820/icon/AvB7gFWmDvmgaeDFB48XHXfA.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "50037", + "title": "500円", + "fee": 500, + "description": "投稿から6か月たったものはこちらに移動します。それとたまに500円限定の差分イラストを上げます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/50037/cover/QfOXflbPPvxdR4E59GMnvLG3.jpeg", + "user": { + "userId": "7968", + "name": "伊藤達哉", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/7968/icon/3TXFEP8ELMnaLhkS60eqEDH6.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "38114", + "title": "ラムネプラン", + "fee": 100, + "description": "双木こじろにブドウ糖90%のラムネを食べさせるプランです。\r\nペンタブの芯や絵の資料の資金になることもあります。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/38114/cover/8AE1M6JTYutnFLfUeRsE0ofL.jpeg", + "user": { + "userId": "11443", + "name": "双木こじろ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/11443/icon/quTymdIRXnfjFKEhlDVUM9ba.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "15330", + "title": "調教支援:ピンクローター", + "fee": 250, + "description": "あかいしの調教にピンクローターの力が加わり\nあかいしの戦闘力がちょっとだけ高まります。", + "coverImageUrl": null, + "user": { + "userId": "91029", + "name": "あかいししろいし", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/91029/icon/88cadMWRtY4Rak2TYCO2nxra.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "23834", + "title": "ドリンクタイム", + "fee": 200, + "description": "赤い牛とか怪物力を買います。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/23834/cover/VLnjWLNnz8n3pFfm6YyZPTu1.jpeg", + "user": { + "userId": "151050", + "name": "わっちー", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/151050/icon/530GbYo4o0fCjt6tiUHT6RCt.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "22444", + "title": "サポートA", + "fee": 200, + "description": "通常投稿作品のR18バージョンや高解像度版、長い動画(うごくイラスト)が閲覧できます。支援金はソフトウエア購入、使用代金に使わせていただきます。\r\nYou can view R18 version of the contribution work, high resolution version, long video (Ugoku_illustrations). I will use your donations for software purchase and usage fee.", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/22444/cover/CXjsJ3oeXNFcy2iYlGXhm26N.jpeg", + "user": { + "userId": "195364", + "name": "tooo", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/195364/icon/nbyDJpVBz9jMsBgN9lafj0eP.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "7329", + "title": "ストパンエロ絵置き場(ダウンロード用)", + "fee": 250, + "description": "趣味で描いてPIXIVに上げたエロ絵や、その差分を投稿する場所。無料のところとほとんど変わらないから特に有料プランに入る必要はないけど、ダウンロードしやすいよう差分ごとファイルにまとめるよ(断面図や拡大図付き差分ファイル追加するかも)。\n後、以前描いたエロ絵のリメイクと差分をのっけるかも。最低月1更新。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/7329/cover/n91lwh6eAjcV7w2MRah4Nmao.jpeg", + "user": { + "userId": "226267", + "name": "髭侍", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/226267/icon/sxsTDCwrUQwBj6uRuWKMAzj4.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "57409", + "title": "もっとお絵描き頑張ってね☆プラン", + "fee": 500, + "description": "特になにもありませんが、日頃のお絵描きをもっと頑張る気力が更に沸きます", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/57409/cover/cI6qBbgItDvHrHPOyx66gpS0.jpeg", + "user": { + "userId": "376735", + "name": "るいるい", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/376735/icon/GGTIP2kFojXxeHJE7cEcRva6.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "19421", + "title": " Just a pay some Rewards page,No payment content", + "fee": 100, + "description": "何もない!!!!!!!!!!!!!!!!!!!!\r\n只是一個打賞的頁面。\r\n沒有任何付費内容\r\nJust a paying some Tips/Reward's page.\r\nHas no payment content\r\nただ、いくつかのヒントのページを支払います。(machine)\r\n\r\n", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/19421/cover/Dr7lHtCssipQnCNLNFIMIgAz.jpeg", + "user": { + "userId": "634336", + "name": "喵行燈[pass]", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/634336/icon/YLe6B5XLbiWtXHiWVzn0WkJx.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "64153", + "title": "ラーメンプラン", + "fee": 500, + "description": "みかぐらがお昼ご飯にラーメンを食べられます。\npixivに投稿したイラストの高解像度版や未公開差分、限定公開イラストなど", + "coverImageUrl": null, + "user": { + "userId": "642151", + "name": "みかぐら", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/642151/icon/fpHzu25VUYF3y5y5kFBjMdjV.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "25985", + "title": "やる気スイッチ", + "fee": 300, + "description": "落書き・ラフ・高画質版が閲覧できます", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/25985/cover/jMlX9E92sLdTUyFjS9F30tYD.jpeg", + "user": { + "userId": "1030278", + "name": "とりあっとぐぬぬ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1030278/icon/lhS7qcfvYdo217eQunGlejQ1.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "43196", + "title": "エキスパートエディション", + "fee": 500, + "description": "マンガ形式の差分イラストなどを考えています。\r\n制作者は煮干しつけ麺が食べられます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/43196/cover/Xil20skVpr2sWsWVzZ1alOtt.jpeg", + "user": { + "userId": "1046936", + "name": "アブトマト", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1046936/icon/0iBaWBh8JeNE7NHtXJnVnU3R.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "29051", + "title": "18禁っぽいの", + "fee": 200, + "description": "スク水娘を描く上でスク水をまだ着ていないスク水少女とか見れます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/29051/cover/njuKDrsR3Ls7I9c4hoSHj1ZB.jpeg", + "user": { + "userId": "1094478", + "name": "ろひつか", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1094478/icon/PQCsaGKcNV59I2ilU3sFCIZY.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "77592", + "title": "500", + "fee": 500, + "description": "10月はPatreonの新作を中心に過去作品など投稿したいと思います。スケッチ、完成絵、モノクロなど含まれます。\nIn Octorber we will post new works mainly with past works of Patreon. Includes sketches, full shade, and monochrome.\n", + "coverImageUrl": null, + "user": { + "userId": "1131293", + "name": "Tenebre Publisher (CoCo)", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1131293/icon/9eY25HnINd09fbu9biGhbsTI.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "32339", + "title": "Plan 5", + "fee": 500, + "description": "PIXIV公開画の差分画や、支援者様限定画など全てをご覧になれます。", + "coverImageUrl": null, + "user": { + "userId": "1149958", + "name": "カミマキ ツカミ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1149958/icon/6waWjowBUvtCKTEXti9Qw1Ql.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "26356", + "title": "えびみそプラン", + "fee": 500, + "description": "18禁や特殊な性癖のイラストや短編の漫画を不定期に掲載していきたいです。ご支援いただけたらうれしいです。※ファンティアと投稿内容は同じです。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/26356/cover/ikiDiVF4usteRczoPkeH6Bn3.jpeg", + "user": { + "userId": "1276620", + "name": "いしみそ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1276620/icon/wqNjXH7IaRvWLxjcbmSuTc1h.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "51601", + "title": "実績解除:戦友", + "fee": 150, + "description": "ベア猫の作戦行動を援護した。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51601/cover/jg1YBTxD9cP3ruDvItU2uSzl.jpeg", + "user": { + "userId": "1368840", + "name": "ベア猫(遺伝子組み換えでない)", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1368840/icon/TcKSISCf2q5TTASuxlBOmmnn.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "79834", + "title": "投げ銭(小3)", + "fee": 300, + "description": "女子小学3年生向けのプランです。\n山田がお弁当を1つ食べられます。", + "coverImageUrl": null, + "user": { + "userId": "1769484", + "name": "山田の性活が第一", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1769484/icon/IXZrfHhSuDIGVTDihj0DLqYu.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "71632", + "title": "圧力", + "fee": 100, + "description": "創作活動を促す圧力がかかります。\r\n稀にイラストの原寸公開やラフ投稿があるかもしれません。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/71632/cover/l2SLNfM0ZvdwsCIHKhQQal6m.jpeg", + "user": { + "userId": "1831980", + "name": "pxkanif + huyusilver", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/1831980/icon/0iYlM8p0zO477c7Tj2wW91ih.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "54568", + "title": "一筆に紅茶一杯のプラン", + "fee": 498, + "description": "エッチな絵の原動力に、一杯の紅茶を支援頂きたいプランです\npixivに投稿している絵の高解像度版や差分などを提供させていただきます\nあなたのご支援とお心に大変感謝いたします ", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/54568/cover/f2v7DFOuH0pDEM94Kqa5wVLL.jpeg", + "user": { + "userId": "2221925", + "name": "真綿", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/2221925/icon/TnrEk24ySSCTbgZxoiBCvrMU.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "62985", + "title": "300", + "fee": 300, + "description": "", + "coverImageUrl": null, + "user": { + "userId": "2408551", + "name": "ぽこにゃん", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/2408551/icon/SkhA1alyqbhMNs07daiHAbdc.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "51378", + "title": "毎月緊縛プラン", + "fee": 200, + "description": "(毎月更新しましたら古いほうを削除する予定です。お早めに保存して頂きますようお願いいたします。)\r\nFanbox始めました!!試運転として、月額200円で、毎月独占でお縛りのオリジナル画像を一枚上げてきます!基本、話題な女の子(アニメ、ソシャゲー中心)で書こうと思います!どうぞよろしくお願いします!!", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51378/cover/kKIN01S5y3jpV3zPyXWFi3e9.jpeg", + "user": { + "userId": "3452804", + "name": "HaneRu", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/3452804/icon/b4AWDuOCKPCsgxsdYYURLlSL.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "78034", + "title": "エネルギー注入プラン Patreon 300", + "fee": 300, + "description": "支援が多いほど早く描けます~(わがまま \r\n不定期で未公開ラフを支援者だけに公開します", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/78034/cover/uaBHEnYIhTyAKaDBvVEN0ZAx.jpeg", + "user": { + "userId": "4417141", + "name": "クロニ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/4417141/icon/hnNvWHyfFOXYGPl1vFTm2wKu.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "87526", + "title": "普通に応援", + "fee": 600, + "description": "普段のラフやイラストを観ることができます。\n気軽に応援プランより、月にもう一枚のスペシャルイラストを観ることがきるようになります。\nショットストーリーの1ー2ページを観ることができます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/87526/cover/6R5J5c5FohoeZ3BZoAHUZOyv.jpeg", + "user": { + "userId": "4704179", + "name": "万天気像", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/4704179/icon/1HlyC5Axr3StL3XQUHZvpQDj.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "70032", + "title": "PSD元ファイルを取得", + "fee": 750, + "description": "可以得到作者本月新作的PSD原文件\n作者の今月新作のPSDファイルを入手できます。\nget the original PSD file that the author wrote this month.", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/70032/cover/WhaKe10PO8BWn7ploP688BW4.jpeg", + "user": { + "userId": "5165814", + "name": "神奇火鸡(Wonder Turkey)", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/5165814/icon/NzsxKESsUOsH2gkEn8kxCFl8.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "14795", + "title": "見放題プラン", + "fee": 300, + "description": "見放題プランです。", + "coverImageUrl": null, + "user": { + "userId": "5664611", + "name": "Θωθ", + "iconUrl": null + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "95835", + "title": "Previews of drawing in progress.", + "fee": 150, + "description": "Sketches with news and ideas of drawings I am currently working on. \r\nJist like in patreon. :3\r\nMight also post colored progress reports of larger projects. ^^", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/95835/cover/s0tOyvCCcNXA2V24lAAwPcg7.jpeg", + "user": { + "userId": "5944252", + "name": "Hotel01", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/5944252/icon/R6Gu6354EmTMQO6NP97eDVdC.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "59121", + "title": "毎月一枚r18同人プラン", + "fee": 200, + "description": "月末で一枚以上のr18同人イラストを限定公開します!テーマは今期アニメや、ゲーム等、ネトラレ、陵辱等もあります!", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/59121/cover/kf5nD3JrXkjABocaaDgY9Mkb.jpeg", + "user": { + "userId": "6049901", + "name": "鬼針草", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/6049901/icon/SHqjwGwp3G3Ya7S3k6o4wfTf.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "39309", + "title": "プラン500", + "fee": 500, + "description": "高解像度イラストを閲覧することができます。\r\nYou can see high resolution images.", + "coverImageUrl": null, + "user": { + "userId": "6357272", + "name": "ろるゐ紳士", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/6357272/icon/XKIw7maSfZV8xAvPHbvL6lnO.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "86864", + "title": "600円プラン", + "fee": 600, + "description": "いつも通り、ファンボックスがpatreonみたいに使います。\n不安定ですが、毎月1差分絵セット保証します、2セットが頑張ります。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/86864/cover/eOEV1YvAzynCfL1aYj08ptOG.jpeg", + "user": { + "userId": "6610818", + "name": "サーモン", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/6610818/icon/uXK2ehrKEL8prJwLx5qebuXF.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "93775", + "title": "It helps me concentrate on my work.", + "fee": 200, + "description": "I will upload a private picture for the fan box once a month.", + "coverImageUrl": null, + "user": { + "userId": "10414967", + "name": "kkta", + "iconUrl": null + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "13242", + "title": "スタンダートプラン", + "fee": 500, + "description": "えっちな絵を見たい方のスタンダートなプランです。\r\nTwitterにはスケベすぎて載せられない支援者限定イラストやエロ差分、文字なし差分などを定期的に載せます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/13242/cover/KqDzOeEmxS2pa78DiuHEGkbB.jpeg", + "user": { + "userId": "10594960", + "name": "弥猫うた", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/10594960/icon/xXk4UqYlTXuHaFN6QEdWWkE2.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "49868", + "title": "お賽銭箱", + "fee": 200, + "description": "pixiv、ツイッター等で上げてる絵の裸verなど上げたいと思います~", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/49868/cover/mZnXCfifwWTrsCxB7W92WGqk.jpeg", + "user": { + "userId": "11229342", + "name": "サインこす", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/11229342/icon/AIfY06L4CPM2WiadhfMlHViT.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "72698", + "title": "喜欢的话给个赞助吧…", + "fee": 200, + "description": "", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/72698/cover/clb46Zb6PrwAHjh17fOHMN3D.jpeg", + "user": { + "userId": "12016484", + "name": "HLL.ALSG99", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/12016484/icon/EaoIZnz0fP6xxkCQ4siTyd4o.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "59173", + "title": "落書き", + "fee": 500, + "description": "ここで私の普段の落書きと受付のイラストの落書きを発表する。落書きほとんどは人体緊縛の姿についての絵です。そしてみんなと一緒にいい場面を構想したいと思います。\r\n我會在這裡發佈一些平時構想的草稿以及約稿的草圖,草圖大部分情況下是突然想到的能用在捆綁里的人體結構 我很樂意和大家一起想後面的構思之類的東西_(:з」∠)_", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/59173/cover/zjFQY2WTAs0bR6MNklWYXBlS.jpeg", + "user": { + "userId": "12063067", + "name": "臣訾", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/12063067/icon/ciyGaxmudPK7wsrqY0kDcwRj.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "51253", + "title": "毎月緊縛プラン", + "fee": 200, + "description": "Fanbox始めました!!試運転として、月額200円で、毎月独占でお縛りのオリジナル画像を一枚上げてきます!基本、話題な女の子(アニメ、ソシャゲー中心)で書こうと思います!どうぞよろしくお願いします!!", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51253/cover/RwTIAoJsAnIWJWNYNAkhEfBg.jpeg", + "user": { + "userId": "13379747", + "name": "ひみつ", + "iconUrl": null + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "94525", + "title": "水月的Fanbox计划", + "fee": 300, + "description": "水月的色图细化计划,应该每月能更新一次吧", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/94525/cover/A0AZiixXTt6NgsQw7NGQTDDF.jpeg", + "user": { + "userId": "13524241", + "name": "水月岩雲ー仕事募集中", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/13524241/icon/8XcQMzKzVLJlydmb1afNBg2w.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "61259", + "title": "レベル3:限定イラスト", + "fee": 1000, + "description": "イラストとマンガ以上 未公開イラスト1枚(縛りと拘束),限定イラストは差分もありよ", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/61259/cover/JQRm1mgrOe5U8xIqPpIExkmE.jpeg", + "user": { + "userId": "17305623", + "name": "雪ノ嵐&异端丶", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/17305623/icon/86rMpiapcC8E7wNGhz8KHghK.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "2399", + "title": "資金援助プラン", + "fee": 500, + "description": "入ってくれる方は神様仏様マミさん\n支援頂いたお金は画材購入、環境整備、同人活動の為に使わせて頂きます。\n現状では頒布物の内容の一部や原稿進捗(全体図、線画など)を投稿して行きます\n何かしらの御礼的コンテンツを考え中(差分絵とか", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/2399/cover/SwWbY8hVcTq2R1LIcRVPonYi.jpeg", + "user": { + "userId": "17813543", + "name": "やんまーみ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/17813543/icon/UYKInBg5X3ExpjAwmcnYZAOG.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "87716", + "title": "とどおが頑張って描こうとするプラン", + "fee": 500, + "description": "100円のプランの内容に加え、過去にpixivで上げた絵の高解像度版やボツ絵などが見れます。\r\nまた、とどおが支援に感謝して無理をしない範囲でがんばろうとします。\r\n", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/87716/cover/fL5I25T8uid0HGiVYH6QjqHN.jpeg", + "user": { + "userId": "18961759", + "name": "とどお", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/18961759/icon/7uqGq9JlT0XjdHSRiBAfs1Wi.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "20046", + "title": "イラスト気に入ったよ的な感じで・・・", + "fee": 300, + "description": "もしもpixiv等でのイラストで気に入った方がいましたら、投げ銭みたいな感じで支援していただけると嬉しいです。特に大きな見返りがあるわけではないのでご注意ください。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/20046/cover/fe1nD773JNAQld3Csw3LurpS.jpeg", + "user": { + "userId": "19116803", + "name": "かなとふ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/19116803/icon/ph0eU9nf0g2RiTyZvo0ZANoJ.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "95894", + "title": "おべんとプラン", + "fee": 500, + "description": "100円のプランに加え、pixivなどで公開したイラストの高解像度版や限定おまけ差分をご覧いただけます。\n\nご支援金は創作活動費として使用されます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/95894/cover/strRAGNfkBdtU6kFOeIcQJNY.jpeg", + "user": { + "userId": "22553630", + "name": "桜屋敷とんこつ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/22553630/icon/b6XKPWx6nZabxwPgFHlpXrC0.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "79440", + "title": "500円プラン", + "fee": 500, + "description": "I will post illustrations for only supporters every month\r\n毎月サポーターのみにイラストを投稿します", + "coverImageUrl": null, + "user": { + "userId": "23311297", + "name": "nekomom", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/23311297/icon/NZTaY6CgR8zHuwywXHfIURqC.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "85396", + "title": "ラフぽいぽい祭り", + "fee": 100, + "description": "ラフを載せまくる。以上です!よろしくお願いします!\r\n1週間に1~2回の更新を目標に頑張ります(。ᵕᴗᵕ。)\r\n", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/85396/cover/H0IqMyCwbwUQOLe6kICNYZA2.jpeg", + "user": { + "userId": "23626451", + "name": "にゆん", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/23626451/icon/c0X6sE0XAWId3KVWQqMhCgar.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": false, + "paymentMethod": "gmo_card" + }, { + "id": "84482", + "title": "通常プラン", + "fee": 880, + "description": "現状のところプランは一つだけですので、すべての作品を閲覧することができます。", + "coverImageUrl": null, + "user": { + "userId": "23652509", + "name": "識林ほりう", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/23652509/icon/r4zsbbGehIK6QLuM4dO3iWkJ.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "67599", + "title": "スタンダードプラン", + "fee": 400, + "description": "「継続的に支援したい!」という方向けのスタンダードなプランです。\n\nThis is a standard plan.\nFor those who want to support me continuously.", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/67599/cover/yf2vSvP7JDsigIdHjoFCpevH.jpeg", + "user": { + "userId": "25900946", + "name": "ogyadya", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/25900946/icon/DlFEK7OYdUzSvSdle9EBTEA9.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "57468", + "title": "月刊誌", + "fee": 500, + "description": "お賽銭的なヤツ", + "coverImageUrl": null, + "user": { + "userId": "27569300", + "name": "仁外", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/27569300/icon/A85RkerebOnRg3B0PaLE1c63.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "51441", + "title": "うなぎ丼", + "fee": 600, + "description": "公開しない差分の閲覧が可能になります。その上、毎月独占で拘束の画像を一枚上げてきます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/51441/cover/qtSz3auZWlThSBv5TFFJcqQY.jpeg", + "user": { + "userId": "30716447", + "name": "ginklaga", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/30716447/icon/t1uxsE6MApu2Lkn2azwnJvHp.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "48637", + "title": "+他作品", + "fee": 1000, + "description": "「γ-ガンマ-」以外の作品のイラストも観覧出来る様になります。\r\nこちらのプランで「γ-ガンマ-」のイラストも全て観覧出来ます。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/48637/cover/zf7Ia0StIIv9eoDf2Y9H8xGZ.jpeg", + "user": { + "userId": "35438167", + "name": "γ", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/35438167/icon/X7POgmP3psmeubiiglej7qCV.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "82919", + "title": "fan (+3pts/month)", + "fee": 300, + "description": "being a fan you get to see them exclusives c;", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/82919/cover/rgoQIUmWblcjGcjXmSuDu0zg.jpeg", + "user": { + "userId": "40169508", + "name": "crumbles", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/40169508/icon/buR2OoPLjCm1RagEvyHYatyC.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "93759", + "title": "100円TIPS", + "fee": 100, + "description": "full-size picture and the process of my works only! I would be very thankful!\nこちらは原サイズ画像と創作流れしかありません\n只有原尺寸圖與我的感謝(´∀`)", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/93759/cover/BsXC8FH3Wxi3VfVV2OYa8tl5.jpeg", + "user": { + "userId": "42936714", + "name": "BLAM", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/42936714/icon/dOnO2ZaKTk91VORpBchIYAEc.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }, { + "id": "96263", + "title": "投げ銭プラン", + "fee": 100, + "description": "投げ銭のプランです。ちょっとしたおまけ等が見れる予定です。プランに興味がなくても、フォローやコメント、コミッション等していただけると励みになります。", + "coverImageUrl": "https://pixiv.pximg.net/c/936x600_90_a2_g5/fanbox/public/images/plan/96263/cover/UEN4nNSXxVictnIr2NqM3z2U.jpeg", + "user": { + "userId": "44433394", + "name": "Pita17", + "iconUrl": "https://pixiv.pximg.net/c/160x160_90_a2_g5/fanbox/public/images/user/44433394/icon/MjeSA6YIUQHHCAtcAlMAfpht.jpeg" + }, + "creatorId": "madeToPassUnitTest", + "hasAdultContent": true, + "paymentMethod": "gmo_card" + }] +} diff --git a/test_PixivModel_fanbox.py b/test_PixivModel_fanbox.py index 21699b2e..a0022af8 100644 --- a/test_PixivModel_fanbox.py +++ b/test_PixivModel_fanbox.py @@ -7,7 +7,7 @@ import demjson import PixivHelper -from PixivModelFanbox import Fanbox, FanboxArtist, FanboxPost +from PixivModelFanbox import FanboxArtist, FanboxPost temp = PixivHelper.__re_manga_index @@ -21,90 +21,98 @@ def testFanboxSupportedArtist(self): reader = open('./test/Fanbox_supported_artist.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = Fanbox(p) + result = FanboxArtist.parseArtists(p) self.assertIsNotNone(result) - self.assertEqual(len(result.supportedArtist), 52) - self.assertTrue(4820 in result.supportedArtist) - self.assertTrue(11443 in result.supportedArtist) - self.assertTrue(226267 in result.supportedArtist) + self.assertEqual(len(result), 52) + self.assertTrue(4820 in [x.artistId for x in result]) + self.assertTrue(11443 in [x.artistId for x in result]) + self.assertTrue(226267 in [x.artistId for x in result]) def testFanboxArtistPosts(self): reader = open('./test/Fanbox_artist_posts.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(15521131, p) + + artist = FanboxArtist(15521131, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(15521131, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 15521131) - self.assertTrue(result.hasNextPage) - self.assertTrue(len(result.nextUrl) > 0) - self.assertTrue(len(result.posts) > 0) + self.assertEqual(artist.artistId, 15521131) + self.assertTrue(artist.hasNextPage) + self.assertTrue(len(artist.nextUrl) > 0) + self.assertTrue(len(result) > 0) - for post in result.posts: + for post in result: self.assertFalse(post.is_restricted) # post-136761 image - self.assertEqual(result.posts[0].imageId, 136761) - self.assertTrue(len(result.posts[0].imageTitle) > 0) - self.assertTrue(len(result.posts[0].coverImageUrl) > 0) - self.assertEqual(result.posts[0].type, "image") - self.assertEqual(len(result.posts[0].images), 5) + self.assertEqual(result[0].imageId, 136761) + self.assertTrue(len(result[0].imageTitle) > 0) + self.assertTrue(len(result[0].coverImageUrl) > 0) + self.assertEqual(result[0].type, "image") + self.assertEqual(len(result[0].images), 5) # post-132919 text - self.assertEqual(result.posts[2].imageId, 132919) - self.assertTrue(len(result.posts[2].imageTitle) > 0) - self.assertIsNone(result.posts[2].coverImageUrl) - self.assertEqual(result.posts[2].type, "text") - self.assertEqual(len(result.posts[2].images), 0) + self.assertEqual(result[2].imageId, 132919) + self.assertTrue(len(result[2].imageTitle) > 0) + self.assertIsNone(result[2].coverImageUrl) + self.assertEqual(result[2].type, "text") + self.assertEqual(len(result[2].images), 0) # post-79695 image - self.assertEqual(result.posts[3].imageId, 79695) - self.assertTrue(len(result.posts[3].imageTitle) > 0) - self.assertIsNone(result.posts[3].coverImageUrl) - self.assertEqual(result.posts[3].type, "image") - self.assertEqual(len(result.posts[3].images), 4) + self.assertEqual(result[3].imageId, 79695) + self.assertTrue(len(result[3].imageTitle) > 0) + self.assertIsNone(result[3].coverImageUrl) + self.assertEqual(result[3].type, "image") + self.assertEqual(len(result[3].images), 4) def testFanboxArtistArticle(self): reader = open('./test/Fanbox_artist_posts_article.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(190026, p) + + artist = FanboxArtist(190026, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(190026, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 190026) - self.assertTrue(result.hasNextPage) - self.assertTrue(len(result.nextUrl) > 0) - self.assertTrue(len(result.posts) > 0) + self.assertEqual(artist.artistId, 190026) + self.assertTrue(artist.hasNextPage) + self.assertTrue(len(artist.nextUrl) > 0) + self.assertTrue(len(result) > 0) # post-201946 article - self.assertEqual(result.posts[0].imageId, 201946) - self.assertTrue(len(result.posts[0].imageTitle) > 0) - self.assertIsNone(result.posts[0].coverImageUrl) - self.assertEqual(result.posts[0].type, "article") - self.assertEqual(len(result.posts[0].images), 5) - self.assertEqual(len(result.posts[0].body_text), 1292) + self.assertEqual(result[0].imageId, 201946) + self.assertTrue(len(result[0].imageTitle) > 0) + self.assertIsNone(result[0].coverImageUrl) + self.assertEqual(result[0].type, "article") + self.assertEqual(len(result[0].images), 5) + self.assertEqual(len(result[0].body_text), 1292) # result.posts[0].WriteInfo("./201946.txt") def testFanboxArtistArticleFileMap(self): reader = open('./test/creator_with_filemap.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(190026, p) + artist = FanboxArtist(190026, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(190026, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 190026) - self.assertTrue(result.hasNextPage) - self.assertTrue(len(result.nextUrl) > 0) - self.assertTrue(len(result.posts) > 0) + self.assertEqual(artist.artistId, 190026) + self.assertTrue(artist.hasNextPage) + self.assertTrue(len(artist.nextUrl) > 0) + self.assertTrue(len(result) > 0) # post-201946 article - self.assertEqual(result.posts[0].imageId, 210980) - self.assertTrue(len(result.posts[0].imageTitle) > 0) - self.assertIsNone(result.posts[0].coverImageUrl) - self.assertEqual(result.posts[0].type, "article") - self.assertEqual(len(result.posts[0].images), 15) - self.assertEqual(len(result.posts[0].body_text), 3006) + self.assertEqual(result[0].imageId, 210980) + self.assertTrue(len(result[0].imageTitle) > 0) + self.assertIsNone(result[0].coverImageUrl) + self.assertEqual(result[0].type, "article") + self.assertEqual(len(result[0].images), 15) + self.assertEqual(len(result[0].body_text), 3006) # result.posts[0].WriteInfo("./210980.txt") @@ -112,21 +120,24 @@ def testFanboxArtistVideo(self): reader = open('./test/creator_posts_with_video.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(711048, p) + + artist = FanboxArtist(711048, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(711048, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 711048) - self.assertTrue(result.hasNextPage) - self.assertTrue(len(result.nextUrl) > 0) - self.assertTrue(len(result.posts) > 0) + self.assertEqual(artist.artistId, 711048) + self.assertTrue(artist.hasNextPage) + self.assertTrue(len(artist.nextUrl) > 0) + self.assertTrue(len(result) > 0) # post-201946 article - self.assertEqual(result.posts[4].imageId, 330905) - self.assertTrue(len(result.posts[4].imageTitle) > 0) - self.assertEqual(result.posts[4].coverImageUrl, u'https://pixiv.pximg.net/c/1200x630_90_a2_g5/fanbox/public/images/post/330905/cover/3A2zPUg4s6iz17MM0Z45eWBj.jpeg') - self.assertEqual(result.posts[4].type, "video") - self.assertEqual(len(result.posts[4].images), 0) - self.assertEqual(len(result.posts[4].body_text), 99) + self.assertEqual(result[4].imageId, 330905) + self.assertTrue(len(result[4].imageTitle) > 0) + self.assertEqual(result[4].coverImageUrl, u'https://pixiv.pximg.net/c/1200x630_90_a2_g5/fanbox/public/images/post/330905/cover/3A2zPUg4s6iz17MM0Z45eWBj.jpeg') + self.assertEqual(result[4].type, "video") + self.assertEqual(len(result[4].images), 0) + self.assertEqual(len(result[4].body_text), 99) # result.posts[0].WriteInfo("./210980.txt") @@ -134,20 +145,23 @@ def testFanboxArtistArticleEmbedTwitter(self): reader = open('./test/creator_embedMap.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(68813, p) + + artist = FanboxArtist(68813, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(68813, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 68813) - self.assertFalse(result.hasNextPage) - self.assertTrue(len(result.posts) > 0) + self.assertEqual(artist.artistId, 68813) + self.assertFalse(artist.hasNextPage) + self.assertTrue(len(result) > 0) # post-201946 article - self.assertEqual(result.posts[0].imageId, 285502) - self.assertTrue(len(result.posts[0].imageTitle) > 0) - self.assertEqual(result.posts[0].coverImageUrl, u'https://pixiv.pximg.net/c/1200x630_90_a2_g5/fanbox/public/images/post/285502/cover/orx9TCsiPFi5sgDdbvg4zwkX.jpeg') - self.assertEqual(result.posts[0].type, "article") - self.assertEqual(len(result.posts[0].images), 7) - self.assertEqual(len(result.posts[0].body_text), 3164) + self.assertEqual(result[0].imageId, 285502) + self.assertTrue(len(result[0].imageTitle) > 0) + self.assertEqual(result[0].coverImageUrl, u'https://pixiv.pximg.net/c/1200x630_90_a2_g5/fanbox/public/images/post/285502/cover/orx9TCsiPFi5sgDdbvg4zwkX.jpeg') + self.assertEqual(result[0].type, "article") + self.assertEqual(len(result[0].images), 7) + self.assertEqual(len(result[0].body_text), 3164) # result.posts[0].WriteInfo("./285502.txt") @@ -156,71 +170,83 @@ def testFanboxArtistPostsNextPage(self): reader = open('./test/Fanbox_artist_posts_nextpage.json', 'r', encoding="utf-8") p2 = reader.read() reader.close() - result = FanboxArtist(91029, p2) + + artist = FanboxArtist(91029, "", "", None) + result = artist.parsePosts(p2) + # result = FanboxArtist(91029, p2) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 91029) - self.assertTrue(result.hasNextPage) - self.assertTrue(result.nextUrl is not None) - self.assertEqual(len(result.posts), 10) + self.assertEqual(artist.artistId, 91029) + self.assertTrue(artist.hasNextPage) + self.assertTrue(artist.nextUrl is not None) + self.assertEqual(len(result), 10) def testFanboxArtistPostsRestricted(self): reader = open('./test/Fanbox_artist_posts_restricted.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(15521131, p) + + artist = FanboxArtist(15521131, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(15521131, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 15521131) - self.assertTrue(result.hasNextPage) - self.assertTrue(len(result.nextUrl) > 0) - self.assertEqual(len(result.posts), 10) + self.assertEqual(artist.artistId, 15521131) + self.assertTrue(artist.hasNextPage) + self.assertTrue(len(artist.nextUrl) > 0) + self.assertEqual(len(result), 10) - for post in result.posts: + for post in result: self.assertTrue(post.is_restricted) def testFanboxArtistPostsRestrictedNextPage(self): reader = open('./test/Fanbox_artist_posts_next_page_restricted.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(15521131, p) + artist = FanboxArtist(15521131, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(15521131, p) self.assertIsNotNone(result) - self.assertEqual(result.artistId, 15521131) - self.assertFalse(result.hasNextPage) - self.assertTrue(result.nextUrl is None) - self.assertEqual(len(result.posts), 6) + self.assertEqual(artist.artistId, 15521131) + self.assertFalse(artist.hasNextPage) + self.assertTrue(artist.nextUrl is None) + self.assertEqual(len(result), 6) - self.assertTrue(result.posts[0].is_restricted) - self.assertFalse(result.posts[1].is_restricted) + self.assertTrue(result[0].is_restricted) + self.assertFalse(result[1].is_restricted) def testFanboxOldApi(self): reader = open('./test/fanbox-posts-old-api.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(104409, p) + artist = FanboxArtist(104409, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(104409, p) self.assertIsNotNone(result) - self.assertEqual(len(result.posts), 2) - self.assertEqual(result.posts[0].imageId, 916) - self.assertEqual(len(result.posts[0].images), 8) - print(result.posts[0].images) - self.assertEqual(result.posts[1].imageId, 915) - self.assertEqual(len(result.posts[1].images), 1) - print(result.posts[1].images) + self.assertEqual(len(result), 2) + self.assertEqual(result[0].imageId, 916) + self.assertEqual(len(result[0].images), 8) + print(result[0].images) + self.assertEqual(result[1].imageId, 915) + self.assertEqual(len(result[1].images), 1) + print(result[1].images) def testFanboxNewApi(self): reader = open('./test/fanbox-posts-new-api.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(104409, p) + artist = FanboxArtist(104409, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(104409, p) self.assertIsNotNone(result) - self.assertEqual(len(result.posts), 10) - self.assertEqual(result.posts[0].imageId, 577968) - self.assertEqual(len(result.posts[0].images), 2) - self.assertEqual(result.posts[1].imageId, 535518) - self.assertEqual(len(result.posts[1].images), 2) + self.assertEqual(len(result), 10) + self.assertEqual(result[0].imageId, 577968) + self.assertEqual(len(result[0].images), 2) + self.assertEqual(result[1].imageId, 535518) + self.assertEqual(len(result[1].images), 2) def testFanboxNewApi2_MultiImages(self): reader = open('./test/Fanbox_post_with_multi_images.json', 'r', encoding="utf-8") @@ -254,10 +280,12 @@ def testFanboxFilename(self): reader = open('./test/Fanbox_artist_posts.json', 'r', encoding="utf-8") p = reader.read() reader.close() - result = FanboxArtist(15521131, p) + artist = FanboxArtist(15521131, "", "", None) + result = artist.parsePosts(p) + # result = FanboxArtist(15521131, p) self.assertIsNotNone(result) root_dir = os.path.abspath(os.path.curdir) - post = result.posts[0] + post = result[0] image_url = post.images[0] current_page = 0 fake_image_url = image_url.replace("{0}/".format(post.imageId), "{0}_p{1}_".format(post.imageId, current_page)) @@ -271,7 +299,7 @@ def simple_from_images(): filename = PixivHelper.make_filename(filename_format, post, - artistInfo=result, + artistInfo=artist, tagsSeparator=" ", tagsLimit=0, fileUrl=fake_image_url, @@ -288,7 +316,7 @@ def more_format(): filename = PixivHelper.make_filename(filename_format, post, - artistInfo=result, + artistInfo=artist, tagsSeparator=" ", tagsLimit=0, fileUrl=fake_image_url, @@ -307,7 +335,7 @@ def cover_more_format(): filename = PixivHelper.make_filename(filename_format, post, - artistInfo=result, + artistInfo=artist, tagsSeparator=" ", tagsLimit=0, fileUrl=fake_image_url,