From 1472f1ae56ab4ff1bd873d36605dc2941272a1db Mon Sep 17 00:00:00 2001 From: alzamer2 Date: Sun, 26 Nov 2023 12:58:14 +0300 Subject: [PATCH 1/6] fix wuxiaworld.com this update include: * update for Bearer that in #1360, which will make the Bearer login work in both scraper and Browser method. * username/email and password can be used to login now. * the script will wait for "Time Until Free Chapter" msg until disappear and chapter will fully loaded PS-1: if in your account setting you enabled Auto-unlock, and script used browser method you will unlock all chapter you marked in range PS-2:dont put locked chapter in scraping range if chapter auto-unlock is disable as that will hang/enter the script into infinite loop of waiting --- sources/en/w/wuxiacom.py | 148 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 4 deletions(-) diff --git a/sources/en/w/wuxiacom.py b/sources/en/w/wuxiacom.py index 98a9bf558..a3aa4557f 100644 --- a/sources/en/w/wuxiacom.py +++ b/sources/en/w/wuxiacom.py @@ -4,6 +4,7 @@ import re from pyease_grpc import RpcSession +import time from lncrawl.core.exeptions import FallbackToBrowser from lncrawl.models import Chapter, Volume @@ -19,7 +20,7 @@ class WuxiaComCrawler(BasicBrowserTemplate): ] def initialize(self): - # self.headless = True + #self.headless = True self.api_url = "https://api2.wuxiaworld.com" self.grpc = RpcSession.from_descriptor(WUXIWORLD_PROTO) self.grpc._session = self.scraper @@ -27,9 +28,46 @@ def initialize(self): self.cleaner.bad_tags.add("hr") self.bearer_token = None self.cleaner.unchanged_tags.update(["span"]) + self.wuxia_login_info = list() + self.start_download_chapter_body_in_browser = False + #self.loged_in = False def login(self, email: str, password: str) -> None: - self.bearer_token = email + " " + password + #self.loged_in = False + if email == 'Bearer': + logger.debug("login type: %s", email) + self.bearer_token = email + " " + password + else: + logger.debug("login type: Email(%s)", email) + self.init_browser() + self.visit('https://www.wuxiaworld.com/manage/profile/') + self.browser.wait("h6 button") + self.browser.find("h6 button").click() + self.browser.wait("input#Username") + self.browser.find("input#Username").send_keys(email) + self.browser.find("input#Password").send_keys(password) + self.browser.find("button").click() + #time.sleep(10) # wait 10sec instead for waiting for elemnt in case login failed + #driver.find_element(By.CSS_SELECTOR, "h6 button").click() + #driver.find_element(By.CSS_SELECTOR, "input#Username").send_keys(email) + #driver.find_element(By.ID, "Password").send_keys(password) + #driver.find_element(By.NAME, "button").click() + #self.wuxia_cookies = self.get_cookies() + #self._restore_cookies() + #logger.debug("cookies: Email(%s)", str(self.cookies)) + #logger.debug("headers: Email(%s)", str(self.headers)) + try: + self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) + self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) + #self.loged_in = True + storage = LocalStorage(self.browser._driver) + if storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): + self.bearer_token = '{token_type} {access_token}'.format(**json.loads(storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'])) + except: + logger.debug("login Email: Failed") + self.wuxia_login_info = [email,password] + + def read_novel_info_in_scraper(self) -> None: slug = re.findall(r"/novel/([^/]+)", self.novel_url)[0] @@ -147,6 +185,14 @@ def download_chapter_body_in_scraper(self, chapter: Chapter) -> str: def read_novel_info_in_browser(self) -> None: self.visit(self.novel_url) + if self.bearer_token: + storage = LocalStorage(self.browser._driver) + logger.debug("LocalStorage: %s", storage) + if not storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): + storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) + logger.debug("LocalStorage: %s", storage) + self.visit(self.novel_url) + self.browser.wait("#novel-tabs #full-width-tab-2") self.browser.wait(".items-start h1, img.drop-shadow-ww-novel-cover-image") # Clear the annoying top menubar @@ -172,8 +218,19 @@ def read_novel_info_in_browser(self) -> None: self.novel_author = author_tag.text.strip() # Open chapters menu - self.browser.click("#novel-tabs #full-width-tab-1") - self.browser.wait("#full-width-tabpanel-1 .MuiAccordion-root") + #try: + # self.browser.find("#novel-tabs #full-width-tab-2") + # self.browser.click("#novel-tabs #full-width-tab-0") + # self.browser.wait("#full-width-tabpanel-0 .MuiAccordion-root") + #except: + # self.browser.click("#novel-tabs #full-width-tab-1") + # self.browser.wait("#full-width-tabpanel-1 .MuiAccordion-root") + if len(self.browser.find_all('//*[starts-with(@id, "full-width-tab-")]',By.XPATH)) == 3: + self.browser.click("#novel-tabs #full-width-tab-0") + self.browser.wait("#full-width-tabpanel-0 .MuiAccordion-root") + else: + self.browser.click("#novel-tabs #full-width-tab-1") + self.browser.wait("#full-width-tabpanel-1 .MuiAccordion-root") # Get volume list and a progress bar volumes = self.browser.find_all("#app .MuiAccordion-root") @@ -215,14 +272,97 @@ def read_novel_info_in_browser(self) -> None: # Close progress bar bar.close() + #time.sleep(1000) def download_chapter_body_in_browser(self, chapter: Chapter) -> str: + #logger.debug("login type: %s", str(self.get_cookies())) + if not self.start_download_chapter_body_in_browser: + #if self.loged_in: + # self.login(*self.wuxia_login_info) + if self.bearer_token: + self.visit('https://www.wuxiaworld.com/manage/profile/') + storage = LocalStorage(self.browser._driver) + logger.debug("LocalStorage: %s", storage) + if not storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): + storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) + logger.debug("LocalStorage: %s", storage) + self.visit('https://www.wuxiaworld.com/manage/profile/') + try: + self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) + self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) + except: + logger.debug("login Email: Failed") + + self.start_download_chapter_body_in_browser = True self.visit(chapter.url) # self.browser.wait("chapter-content", By.CLASS_NAME) + try: + self.browser.wait("chapter-content", By.CLASS_NAME) + #if self.loged_in or self.bearer_token: + if self.bearer_token: + self.browser.wait("//button[normalize-space()='Favorite']", By.XPATH, 10) + self.browser.find("//button[normalize-space()='Favorite']", By.XPATH) + #self.browser.wait("loading-container", By.CLASS_NAME, timeout=10, ignored_exceptions=[],reversed=True) + except: + logger.debug("error loading chapter (%s) or chapter is locked", str(chapter.url)) content = self.browser.find("chapter-content", By.CLASS_NAME).as_tag() self.cleaner.clean_contents(content) return content.decode_contents() +class LocalStorage: + + def __init__(self, driver) : + self.driver = driver + + def __len__(self): + return self.driver.execute_script("return window.localStorage.length;") + + def items(self) : + return self.driver.execute_script( \ + "var ls = window.localStorage, items = {}; " \ + "for (var i = 0, k; i < ls.length; ++i) " \ + " items[k = ls.key(i)] = ls.getItem(k); " \ + "return items; ") + + def keys(self) : + return self.driver.execute_script( \ + "var ls = window.localStorage, keys = []; " \ + "for (var i = 0; i < ls.length; ++i) " \ + " keys[i] = ls.key(i); " \ + "return keys; ") + + def get(self, key): + return self.driver.execute_script("return window.localStorage.getItem(arguments[0]);", key) + + def set(self, key, value): + self.driver.execute_script("window.localStorage.setItem(arguments[0], arguments[1]);", key, value) + + def has(self, key): + return key in self.keys() + + def remove(self, key): + self.driver.execute_script("window.localStorage.removeItem(arguments[0]);", key) + + def clear(self): + self.driver.execute_script("window.localStorage.clear();") + + def __getitem__(self, key) : + value = self.get(key) + if value is None : + raise KeyError(key) + return value + + def __setitem__(self, key, value): + self.set(key, value) + + def __contains__(self, key): + return key in self.keys() + + def __iter__(self): + return self.items().__iter__() + + def __repr__(self): + return self.items().__str__() WUXIWORLD_PROTO = json.loads( """ From b96f89172b7f966e96ace1cdb0cbcdb5db67f448 Mon Sep 17 00:00:00 2001 From: alzamer2 Date: Sun, 10 Dec 2023 11:48:12 +0300 Subject: [PATCH 2/6] clean commented --- sources/en/w/wuxiacom.py | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/sources/en/w/wuxiacom.py b/sources/en/w/wuxiacom.py index a3aa4557f..c5498d48c 100644 --- a/sources/en/w/wuxiacom.py +++ b/sources/en/w/wuxiacom.py @@ -30,10 +30,9 @@ def initialize(self): self.cleaner.unchanged_tags.update(["span"]) self.wuxia_login_info = list() self.start_download_chapter_body_in_browser = False - #self.loged_in = False + def login(self, email: str, password: str) -> None: - #self.loged_in = False if email == 'Bearer': logger.debug("login type: %s", email) self.bearer_token = email + " " + password @@ -47,19 +46,9 @@ def login(self, email: str, password: str) -> None: self.browser.find("input#Username").send_keys(email) self.browser.find("input#Password").send_keys(password) self.browser.find("button").click() - #time.sleep(10) # wait 10sec instead for waiting for elemnt in case login failed - #driver.find_element(By.CSS_SELECTOR, "h6 button").click() - #driver.find_element(By.CSS_SELECTOR, "input#Username").send_keys(email) - #driver.find_element(By.ID, "Password").send_keys(password) - #driver.find_element(By.NAME, "button").click() - #self.wuxia_cookies = self.get_cookies() - #self._restore_cookies() - #logger.debug("cookies: Email(%s)", str(self.cookies)) - #logger.debug("headers: Email(%s)", str(self.headers)) try: self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) - #self.loged_in = True storage = LocalStorage(self.browser._driver) if storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): self.bearer_token = '{token_type} {access_token}'.format(**json.loads(storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'])) @@ -217,14 +206,6 @@ def read_novel_info_in_browser(self) -> None: if author_tag: self.novel_author = author_tag.text.strip() - # Open chapters menu - #try: - # self.browser.find("#novel-tabs #full-width-tab-2") - # self.browser.click("#novel-tabs #full-width-tab-0") - # self.browser.wait("#full-width-tabpanel-0 .MuiAccordion-root") - #except: - # self.browser.click("#novel-tabs #full-width-tab-1") - # self.browser.wait("#full-width-tabpanel-1 .MuiAccordion-root") if len(self.browser.find_all('//*[starts-with(@id, "full-width-tab-")]',By.XPATH)) == 3: self.browser.click("#novel-tabs #full-width-tab-0") self.browser.wait("#full-width-tabpanel-0 .MuiAccordion-root") @@ -270,15 +251,13 @@ def read_novel_info_in_browser(self) -> None: bar.update() - # Close progress bar + bar.close() - #time.sleep(1000) + def download_chapter_body_in_browser(self, chapter: Chapter) -> str: - #logger.debug("login type: %s", str(self.get_cookies())) + if not self.start_download_chapter_body_in_browser: - #if self.loged_in: - # self.login(*self.wuxia_login_info) if self.bearer_token: self.visit('https://www.wuxiaworld.com/manage/profile/') storage = LocalStorage(self.browser._driver) @@ -295,14 +274,12 @@ def download_chapter_body_in_browser(self, chapter: Chapter) -> str: self.start_download_chapter_body_in_browser = True self.visit(chapter.url) - # self.browser.wait("chapter-content", By.CLASS_NAME) try: self.browser.wait("chapter-content", By.CLASS_NAME) - #if self.loged_in or self.bearer_token: if self.bearer_token: self.browser.wait("//button[normalize-space()='Favorite']", By.XPATH, 10) self.browser.find("//button[normalize-space()='Favorite']", By.XPATH) - #self.browser.wait("loading-container", By.CLASS_NAME, timeout=10, ignored_exceptions=[],reversed=True) + except: logger.debug("error loading chapter (%s) or chapter is locked", str(chapter.url)) content = self.browser.find("chapter-content", By.CLASS_NAME).as_tag() From f74856976ea557800fe4d8d0d34e29c1b1374430 Mon Sep 17 00:00:00 2001 From: alzamer2 Date: Sun, 10 Dec 2023 12:01:23 +0300 Subject: [PATCH 3/6] added some comment --- sources/en/w/wuxiacom.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sources/en/w/wuxiacom.py b/sources/en/w/wuxiacom.py index c5498d48c..8fef71ff1 100644 --- a/sources/en/w/wuxiacom.py +++ b/sources/en/w/wuxiacom.py @@ -31,8 +31,9 @@ def initialize(self): self.wuxia_login_info = list() self.start_download_chapter_body_in_browser = False - def login(self, email: str, password: str) -> None: + # Login now will use Bearer Token if supplied as main login method, + # and if username used it will exctract Bearer Token and use it for further login process if email == 'Bearer': logger.debug("login type: %s", email) self.bearer_token = email + " " + password @@ -47,6 +48,7 @@ def login(self, email: str, password: str) -> None: self.browser.find("input#Password").send_keys(password) self.browser.find("button").click() try: + # Testing if logging has succeeded self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) storage = LocalStorage(self.browser._driver) @@ -174,6 +176,7 @@ def download_chapter_body_in_scraper(self, chapter: Chapter) -> str: def read_novel_info_in_browser(self) -> None: self.visit(self.novel_url) + # Login if self.bearer_token: storage = LocalStorage(self.browser._driver) logger.debug("LocalStorage: %s", storage) @@ -206,6 +209,7 @@ def read_novel_info_in_browser(self) -> None: if author_tag: self.novel_author = author_tag.text.strip() + # Open chapters menu (note: the order of tabs in novel info change whether if you are logged in or not) if len(self.browser.find_all('//*[starts-with(@id, "full-width-tab-")]',By.XPATH)) == 3: self.browser.click("#novel-tabs #full-width-tab-0") self.browser.wait("#full-width-tabpanel-0 .MuiAccordion-root") @@ -251,12 +255,13 @@ def read_novel_info_in_browser(self) -> None: bar.update() - + # Close progress bar bar.close() def download_chapter_body_in_browser(self, chapter: Chapter) -> str: + # login if not self.start_download_chapter_body_in_browser: if self.bearer_token: self.visit('https://www.wuxiaworld.com/manage/profile/') @@ -275,17 +280,18 @@ def download_chapter_body_in_browser(self, chapter: Chapter) -> str: self.start_download_chapter_body_in_browser = True self.visit(chapter.url) try: + # wait untill chapter fully loaded self.browser.wait("chapter-content", By.CLASS_NAME) if self.bearer_token: self.browser.wait("//button[normalize-space()='Favorite']", By.XPATH, 10) self.browser.find("//button[normalize-space()='Favorite']", By.XPATH) - except: logger.debug("error loading chapter (%s) or chapter is locked", str(chapter.url)) content = self.browser.find("chapter-content", By.CLASS_NAME).as_tag() self.cleaner.clean_contents(content) return content.decode_contents() +# this class to read browser LocalStorage class LocalStorage: def __init__(self, driver) : From bbd07dc3075ee8e5d133f8f5cd21e883836276ae Mon Sep 17 00:00:00 2001 From: alzamer2 Date: Wed, 13 Dec 2023 08:03:43 +0300 Subject: [PATCH 4/6] fixing lint1 --- sources/en/w/wuxiacom.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/sources/en/w/wuxiacom.py b/sources/en/w/wuxiacom.py index 8fef71ff1..33ea57c6a 100644 --- a/sources/en/w/wuxiacom.py +++ b/sources/en/w/wuxiacom.py @@ -4,12 +4,12 @@ import re from pyease_grpc import RpcSession -import time from lncrawl.core.exeptions import FallbackToBrowser from lncrawl.models import Chapter, Volume from lncrawl.templates.browser.basic import BasicBrowserTemplate from lncrawl.webdriver.elements import By +import selenium logger = logging.getLogger(__name__) @@ -28,8 +28,8 @@ def initialize(self): self.cleaner.bad_tags.add("hr") self.bearer_token = None self.cleaner.unchanged_tags.update(["span"]) - self.wuxia_login_info = list() self.start_download_chapter_body_in_browser = False + self.localstorageuser = 'oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa' def login(self, email: str, password: str) -> None: # Login now will use Bearer Token if supplied as main login method, @@ -52,13 +52,10 @@ def login(self, email: str, password: str) -> None: self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) storage = LocalStorage(self.browser._driver) - if storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): - self.bearer_token = '{token_type} {access_token}'.format(**json.loads(storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'])) - except: + if storage.has(self.localstorageuser): + self.bearer_token = '{token_type} {access_token}'.format(**json.loads(storage[self.localstorageuser])) + except Exception as e: logger.debug("login Email: Failed") - self.wuxia_login_info = [email,password] - - def read_novel_info_in_scraper(self) -> None: slug = re.findall(r"/novel/([^/]+)", self.novel_url)[0] @@ -180,8 +177,8 @@ def read_novel_info_in_browser(self) -> None: if self.bearer_token: storage = LocalStorage(self.browser._driver) logger.debug("LocalStorage: %s", storage) - if not storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): - storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) + if not storage.has(self.localstorageuser): + storage[self.localstorageuser] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) logger.debug("LocalStorage: %s", storage) self.visit(self.novel_url) self.browser.wait("#novel-tabs #full-width-tab-2") @@ -258,25 +255,22 @@ def read_novel_info_in_browser(self) -> None: # Close progress bar bar.close() - def download_chapter_body_in_browser(self, chapter: Chapter) -> str: - # login if not self.start_download_chapter_body_in_browser: if self.bearer_token: self.visit('https://www.wuxiaworld.com/manage/profile/') storage = LocalStorage(self.browser._driver) logger.debug("LocalStorage: %s", storage) - if not storage.has('oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'): - storage['oidc.user:https://identity.wuxiaworld.com:wuxiaworld_spa'] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) + if not storage.has(self.localstorageuser): + storage[self.localstorageuser] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) logger.debug("LocalStorage: %s", storage) self.visit('https://www.wuxiaworld.com/manage/profile/') try: self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) - except: + except Exception as e: logger.debug("login Email: Failed") - self.start_download_chapter_body_in_browser = True self.visit(chapter.url) try: @@ -285,7 +279,7 @@ def download_chapter_body_in_browser(self, chapter: Chapter) -> str: if self.bearer_token: self.browser.wait("//button[normalize-space()='Favorite']", By.XPATH, 10) self.browser.find("//button[normalize-space()='Favorite']", By.XPATH) - except: + except Exception as e: logger.debug("error loading chapter (%s) or chapter is locked", str(chapter.url)) content = self.browser.find("chapter-content", By.CLASS_NAME).as_tag() self.cleaner.clean_contents(content) From 97b9a2b57a008231a62104b6edc640d55cc95b12 Mon Sep 17 00:00:00 2001 From: Sudipto Chandra Date: Thu, 14 Dec 2023 01:52:31 +0400 Subject: [PATCH 5/6] Fix linting --- sources/en/w/wuxiacom.py | 47 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/sources/en/w/wuxiacom.py b/sources/en/w/wuxiacom.py index 33ea57c6a..1d1f18632 100644 --- a/sources/en/w/wuxiacom.py +++ b/sources/en/w/wuxiacom.py @@ -9,7 +9,6 @@ from lncrawl.models import Chapter, Volume from lncrawl.templates.browser.basic import BasicBrowserTemplate from lncrawl.webdriver.elements import By -import selenium logger = logging.getLogger(__name__) @@ -55,7 +54,7 @@ def login(self, email: str, password: str) -> None: if storage.has(self.localstorageuser): self.bearer_token = '{token_type} {access_token}'.format(**json.loads(storage[self.localstorageuser])) except Exception as e: - logger.debug("login Email: Failed") + logger.debug("login Email: Failed", e) def read_novel_info_in_scraper(self) -> None: slug = re.findall(r"/novel/([^/]+)", self.novel_url)[0] @@ -207,7 +206,7 @@ def read_novel_info_in_browser(self) -> None: self.novel_author = author_tag.text.strip() # Open chapters menu (note: the order of tabs in novel info change whether if you are logged in or not) - if len(self.browser.find_all('//*[starts-with(@id, "full-width-tab-")]',By.XPATH)) == 3: + if len(self.browser.find_all('//*[starts-with(@id, "full-width-tab-")]', By.XPATH)) == 3: self.browser.click("#novel-tabs #full-width-tab-0") self.browser.wait("#full-width-tabpanel-0 .MuiAccordion-root") else: @@ -263,14 +262,18 @@ def download_chapter_body_in_browser(self, chapter: Chapter) -> str: storage = LocalStorage(self.browser._driver) logger.debug("LocalStorage: %s", storage) if not storage.has(self.localstorageuser): - storage[self.localstorageuser] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) + token_type, token = self.bearer_token.split(" ", 1) + storage[self.localstorageuser] = json.dumps({ + "access_token": token, + "token_type": token_type, + }) logger.debug("LocalStorage: %s", storage) self.visit('https://www.wuxiaworld.com/manage/profile/') try: self.browser.wait("//h2[normalize-space()='Your Profile']", By.XPATH, 10) self.browser.find("//h2[normalize-space()='Your Profile']", By.XPATH) except Exception as e: - logger.debug("login Email: Failed") + logger.debug("login Email: Failed", e) self.start_download_chapter_body_in_browser = True self.visit(chapter.url) try: @@ -280,14 +283,14 @@ def download_chapter_body_in_browser(self, chapter: Chapter) -> str: self.browser.wait("//button[normalize-space()='Favorite']", By.XPATH, 10) self.browser.find("//button[normalize-space()='Favorite']", By.XPATH) except Exception as e: - logger.debug("error loading chapter (%s) or chapter is locked", str(chapter.url)) + logger.debug("error loading (%s)", str(chapter.url), e) content = self.browser.find("chapter-content", By.CLASS_NAME).as_tag() self.cleaner.clean_contents(content) return content.decode_contents() -# this class to read browser LocalStorage -class LocalStorage: +# Class for reading localStorage from Browser +class LocalStorage: def __init__(self, driver) : self.driver = driver @@ -295,18 +298,20 @@ def __len__(self): return self.driver.execute_script("return window.localStorage.length;") def items(self) : - return self.driver.execute_script( \ - "var ls = window.localStorage, items = {}; " \ - "for (var i = 0, k; i < ls.length; ++i) " \ - " items[k = ls.key(i)] = ls.getItem(k); " \ - "return items; ") + return self.driver.execute_script( + "var ls = window.localStorage, items = {}; " + "for (var i = 0, k; i < ls.length; ++i) " + " items[k = ls.key(i)] = ls.getItem(k); " + "return items; " + ) def keys(self) : - return self.driver.execute_script( \ - "var ls = window.localStorage, keys = []; " \ - "for (var i = 0; i < ls.length; ++i) " \ - " keys[i] = ls.key(i); " \ - "return keys; ") + return self.driver.execute_script( + "var ls = window.localStorage, keys = []; " + "for (var i = 0; i < ls.length; ++i) " + " keys[i] = ls.key(i); " + "return keys; " + ) def get(self, key): return self.driver.execute_script("return window.localStorage.getItem(arguments[0]);", key) @@ -325,8 +330,8 @@ def clear(self): def __getitem__(self, key) : value = self.get(key) - if value is None : - raise KeyError(key) + if value is None: + raise KeyError(key) return value def __setitem__(self, key, value): @@ -342,7 +347,7 @@ def __repr__(self): return self.items().__str__() WUXIWORLD_PROTO = json.loads( - """ +""" {"file": [{"name": "google/protobuf/wrappers.proto", "package": "google.protobuf", "messageType": [{"name": "DoubleValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_DOUBLE", "jsonName": "value"}]}, {"name": "FloatValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_FLOAT", "jsonName": "value"}]}, {"name": "Int64Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT64", "jsonName": "value"}]}, {"name": "UInt64Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_UINT64", "jsonName": "value"}]}, {"name": "Int32Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "value"}]}, {"name": "UInt32Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_UINT32", "jsonName": "value"}]}, {"name": "BoolValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "value"}]}, {"name": "StringValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "value"}]}, {"name": "BytesValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BYTES", "jsonName": "value"}]}], "options": {"javaPackage": "com.google.protobuf", "javaOuterClassname": "WrappersProto", "javaMultipleFiles": true, "goPackage": "google.golang.org/protobuf/types/known/wrapperspb", "ccEnableArenas": true, "objcClassPrefix": "GPB", "csharpNamespace": "Google.Protobuf.WellKnownTypes"}, "syntax": "proto3"}, {"name": "google/protobuf/timestamp.proto", "package": "google.protobuf", "messageType": [{"name": "Timestamp", "field": [{"name": "seconds", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT64", "jsonName": "seconds"}, {"name": "nanos", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "nanos"}]}], "options": {"javaPackage": "com.google.protobuf", "javaOuterClassname": "TimestampProto", "javaMultipleFiles": true, "goPackage": "google.golang.org/protobuf/types/known/timestamppb", "ccEnableArenas": true, "objcClassPrefix": "GPB", "csharpNamespace": "Google.Protobuf.WellKnownTypes"}, "syntax": "proto3"}, {"name": "wuxia.proto", "package": "wuxiaworld.api.v2", "dependency": ["google/protobuf/wrappers.proto", "google/protobuf/timestamp.proto"], "messageType": [{"name": "RelatedChapterUserInfo", "field": [{"name": "isChapterUnlocked", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isChapterUnlocked"}, {"name": "isNovelUnlocked", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isNovelUnlocked"}, {"name": "isChapterFavorite", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isChapterFavorite"}, {"name": "isNovelOwned", "number": 4, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isNovelOwned"}, {"name": "isChapterOwned", "number": 5, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isChapterOwned"}]}, {"name": "ChapterSponsor", "field": [{"name": "advanceChapter", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "advanceChapter"}, {"name": "advanceChapterNumber", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.Int32Value", "jsonName": "advanceChapterNumber"}, {"name": "plans", "number": 3, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterSponsor.AdvanceChapterPlan", "jsonName": "plans"}], "nestedType": [{"name": "AdvanceChapterPlan", "field": [{"name": "name", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "advanceChapterCount", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "advanceChapterCount"}]}]}, {"name": "ChapterPricing", "field": [{"name": "isFree", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isFree"}, {"name": "isLastHoldback", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isLastHoldback"}]}, {"name": "ChapterItem", "field": [{"name": "entityId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "entityId"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "slug", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "slug"}, {"name": "content", "number": 5, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "content"}, {"name": "novelId", "number": 6, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "novelId"}, {"name": "visible", "number": 7, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "visible"}, {"name": "isTeaser", "number": 8, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isTeaser"}, {"name": "spoilerTitle", "number": 10, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "spoilerTitle"}, {"name": "sponsorInfo", "number": 15, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterSponsor", "jsonName": "sponsorInfo"}, {"name": "relatedUserInfo", "number": 16, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.RelatedChapterUserInfo", "jsonName": "relatedUserInfo"}, {"name": "publishedAt", "number": 18, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.Timestamp", "jsonName": "publishedAt"}, {"name": "translatorThoughts", "number": 19, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "translatorThoughts"}, {"name": "pricingInfo", "number": 20, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterPricing", "jsonName": "pricingInfo"}]}, {"name": "ChapterGroupItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "title", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "title"}, {"name": "order", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "order"}, {"name": "chapterList", "number": 6, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterItem", "jsonName": "chapterList"}]}, {"name": "SponsorPlanItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "enabled", "number": 4, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "enabled"}, {"name": "visible", "number": 5, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "visible"}, {"name": "advanceChapterCount", "number": 6, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "advanceChapterCount"}, {"name": "paused", "number": 10, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "paused"}]}, {"name": "NovelItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "coverUrl", "number": 10, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "coverUrl"}, {"name": "translatorName", "number": 11, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "translatorName"}, {"name": "authorName", "number": 13, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "authorName"}, {"name": "isSneakPeek", "number": 18, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isSneakPeek"}]}, {"name": "UnlockedItem", "field": [{"name": "novelId", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "novelId"}, {"name": "chapterId", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "chapterId"}], "oneofDecl": [{"name": "id"}]}, {"name": "VipItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "enabled", "number": 7, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "enabled"}, {"name": "visible", "number": 8, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "visible"}]}, {"name": "SubscriptionItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "active", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "active"}, {"name": "plan", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.SubscriptionItem.Plan", "jsonName": "plan"}], "nestedType": [{"name": "Plan", "field": [{"name": "vip", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.VipItem", "oneofIndex": 0, "jsonName": "vip"}, {"name": "sponsor", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.SponsorPlanItem", "oneofIndex": 0, "jsonName": "sponsor"}], "oneofDecl": [{"name": "plan"}]}]}, {"name": "GetChapterByProperty", "field": [{"name": "chapterId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "chapterId"}, {"name": "slugs", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.GetChapterByProperty.ByNovelAndChapterSlug", "oneofIndex": 0, "jsonName": "slugs"}], "nestedType": [{"name": "ByNovelAndChapterSlug", "field": [{"name": "novelSlug", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "novelSlug"}, {"name": "chapterSlug", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "chapterSlug"}]}], "oneofDecl": [{"name": "byProperty"}]}, {"name": "GetNovelRequest", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "id"}, {"name": "slug", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "oneofIndex": 0, "jsonName": "slug"}], "oneofDecl": [{"name": "selector"}]}, {"name": "GetNovelResponse", "field": [{"name": "item", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.NovelItem", "jsonName": "item"}]}, {"name": "GetChapterListRequest", "field": [{"name": "novelId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "novelId"}, {"name": "filter", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.GetChapterListRequest.FilterChapters", "jsonName": "filter"}], "nestedType": [{"name": "FilterChapters", "field": [{"name": "chapterGroupId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.Int32Value", "jsonName": "chapterGroupId"}, {"name": "isAdvanceChapter", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isAdvanceChapter"}]}]}, {"name": "GetChapterListResponse", "field": [{"name": "items", "number": 1, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterGroupItem", "jsonName": "items"}, {"name": "novelInfo", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.NovelItem", "jsonName": "novelInfo"}]}, {"name": "GetChapterRequest", "field": [{"name": "chapterProperty", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.GetChapterByProperty", "jsonName": "chapterProperty"}]}, {"name": "GetChapterResponse", "field": [{"name": "item", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterItem", "jsonName": "item"}]}, {"name": "UnlockItemRequest", "field": [{"name": "unlockMethod", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_ENUM", "typeName": ".wuxiaworld.api.v2.UnlockItemMethod", "jsonName": "unlockMethod"}, {"name": "item", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.UnlockedItem", "jsonName": "item"}]}, {"name": "UnlockItemResponse", "field": [{"name": "unlockedItem", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.UnlockedItem", "jsonName": "unlockedItem"}]}, {"name": "GetSubscriptionsRequest", "field": [{"name": "novelId", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "novelId"}]}, {"name": "GetSubscriptionsResponse", "field": [{"name": "items", "number": 1, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.SubscriptionItem", "jsonName": "items"}]}], "enumType": [{"name": "UnlockItemMethod", "value": [{"name": "UnlockMethodNone", "number": 0}, {"name": "UnlockMethodKarma", "number": 1}, {"name": "UnlockMethodVip", "number": 2}, {"name": "UnlockMethodSponsor", "number": 3}]}], "service": [{"name": "Novels", "method": [{"name": "GetNovel", "inputType": ".wuxiaworld.api.v2.GetNovelRequest", "outputType": ".wuxiaworld.api.v2.GetNovelResponse"}]}, {"name": "Chapters", "method": [{"name": "GetChapterList", "inputType": ".wuxiaworld.api.v2.GetChapterListRequest", "outputType": ".wuxiaworld.api.v2.GetChapterListResponse"}, {"name": "GetChapter", "inputType": ".wuxiaworld.api.v2.GetChapterRequest", "outputType": ".wuxiaworld.api.v2.GetChapterResponse"}]}, {"name": "Unlocks", "method": [{"name": "UnlockItem", "inputType": ".wuxiaworld.api.v2.UnlockItemRequest", "outputType": ".wuxiaworld.api.v2.UnlockItemResponse"}]}, {"name": "Subscriptions", "method": [{"name": "GetSubscriptions", "inputType": ".wuxiaworld.api.v2.GetSubscriptionsRequest", "outputType": ".wuxiaworld.api.v2.GetSubscriptionsResponse"}]}], "publicDependency": [0, 1], "syntax": "proto3"}]} """ # noqa: E501 ) From 15057c696edb2f36d8e804e9f028ae26fdbdd5a5 Mon Sep 17 00:00:00 2001 From: Sudipto Chandra Date: Thu, 14 Dec 2023 01:54:39 +0400 Subject: [PATCH 6/6] Fix linting --- sources/en/w/wuxiacom.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sources/en/w/wuxiacom.py b/sources/en/w/wuxiacom.py index 1d1f18632..952d3c091 100644 --- a/sources/en/w/wuxiacom.py +++ b/sources/en/w/wuxiacom.py @@ -177,7 +177,11 @@ def read_novel_info_in_browser(self) -> None: storage = LocalStorage(self.browser._driver) logger.debug("LocalStorage: %s", storage) if not storage.has(self.localstorageuser): - storage[self.localstorageuser] = '{"access_token":"%s","token_type":"%s"}' % tuple(self.bearer_token.split(" ",1)[::-1]) + token_type, token = self.bearer_token.split(" ", 1) + storage[self.localstorageuser] = json.dumps({ + "access_token": token, + "token_type": token_type, + }) logger.debug("LocalStorage: %s", storage) self.visit(self.novel_url) self.browser.wait("#novel-tabs #full-width-tab-2") @@ -346,6 +350,7 @@ def __iter__(self): def __repr__(self): return self.items().__str__() + WUXIWORLD_PROTO = json.loads( """ {"file": [{"name": "google/protobuf/wrappers.proto", "package": "google.protobuf", "messageType": [{"name": "DoubleValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_DOUBLE", "jsonName": "value"}]}, {"name": "FloatValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_FLOAT", "jsonName": "value"}]}, {"name": "Int64Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT64", "jsonName": "value"}]}, {"name": "UInt64Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_UINT64", "jsonName": "value"}]}, {"name": "Int32Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "value"}]}, {"name": "UInt32Value", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_UINT32", "jsonName": "value"}]}, {"name": "BoolValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "value"}]}, {"name": "StringValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "value"}]}, {"name": "BytesValue", "field": [{"name": "value", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BYTES", "jsonName": "value"}]}], "options": {"javaPackage": "com.google.protobuf", "javaOuterClassname": "WrappersProto", "javaMultipleFiles": true, "goPackage": "google.golang.org/protobuf/types/known/wrapperspb", "ccEnableArenas": true, "objcClassPrefix": "GPB", "csharpNamespace": "Google.Protobuf.WellKnownTypes"}, "syntax": "proto3"}, {"name": "google/protobuf/timestamp.proto", "package": "google.protobuf", "messageType": [{"name": "Timestamp", "field": [{"name": "seconds", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT64", "jsonName": "seconds"}, {"name": "nanos", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "nanos"}]}], "options": {"javaPackage": "com.google.protobuf", "javaOuterClassname": "TimestampProto", "javaMultipleFiles": true, "goPackage": "google.golang.org/protobuf/types/known/timestamppb", "ccEnableArenas": true, "objcClassPrefix": "GPB", "csharpNamespace": "Google.Protobuf.WellKnownTypes"}, "syntax": "proto3"}, {"name": "wuxia.proto", "package": "wuxiaworld.api.v2", "dependency": ["google/protobuf/wrappers.proto", "google/protobuf/timestamp.proto"], "messageType": [{"name": "RelatedChapterUserInfo", "field": [{"name": "isChapterUnlocked", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isChapterUnlocked"}, {"name": "isNovelUnlocked", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isNovelUnlocked"}, {"name": "isChapterFavorite", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isChapterFavorite"}, {"name": "isNovelOwned", "number": 4, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isNovelOwned"}, {"name": "isChapterOwned", "number": 5, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isChapterOwned"}]}, {"name": "ChapterSponsor", "field": [{"name": "advanceChapter", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "advanceChapter"}, {"name": "advanceChapterNumber", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.Int32Value", "jsonName": "advanceChapterNumber"}, {"name": "plans", "number": 3, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterSponsor.AdvanceChapterPlan", "jsonName": "plans"}], "nestedType": [{"name": "AdvanceChapterPlan", "field": [{"name": "name", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "advanceChapterCount", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "advanceChapterCount"}]}]}, {"name": "ChapterPricing", "field": [{"name": "isFree", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isFree"}, {"name": "isLastHoldback", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isLastHoldback"}]}, {"name": "ChapterItem", "field": [{"name": "entityId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "entityId"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "slug", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "slug"}, {"name": "content", "number": 5, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "content"}, {"name": "novelId", "number": 6, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "novelId"}, {"name": "visible", "number": 7, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "visible"}, {"name": "isTeaser", "number": 8, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isTeaser"}, {"name": "spoilerTitle", "number": 10, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "spoilerTitle"}, {"name": "sponsorInfo", "number": 15, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterSponsor", "jsonName": "sponsorInfo"}, {"name": "relatedUserInfo", "number": 16, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.RelatedChapterUserInfo", "jsonName": "relatedUserInfo"}, {"name": "publishedAt", "number": 18, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.Timestamp", "jsonName": "publishedAt"}, {"name": "translatorThoughts", "number": 19, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "translatorThoughts"}, {"name": "pricingInfo", "number": 20, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterPricing", "jsonName": "pricingInfo"}]}, {"name": "ChapterGroupItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "title", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "title"}, {"name": "order", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "order"}, {"name": "chapterList", "number": 6, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterItem", "jsonName": "chapterList"}]}, {"name": "SponsorPlanItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "enabled", "number": 4, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "enabled"}, {"name": "visible", "number": 5, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "visible"}, {"name": "advanceChapterCount", "number": 6, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "advanceChapterCount"}, {"name": "paused", "number": 10, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "paused"}]}, {"name": "NovelItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "coverUrl", "number": 10, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "coverUrl"}, {"name": "translatorName", "number": 11, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "translatorName"}, {"name": "authorName", "number": 13, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.StringValue", "jsonName": "authorName"}, {"name": "isSneakPeek", "number": 18, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "isSneakPeek"}]}, {"name": "UnlockedItem", "field": [{"name": "novelId", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "novelId"}, {"name": "chapterId", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "chapterId"}], "oneofDecl": [{"name": "id"}]}, {"name": "VipItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "name", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "name"}, {"name": "enabled", "number": 7, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "enabled"}, {"name": "visible", "number": 8, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "visible"}]}, {"name": "SubscriptionItem", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "id"}, {"name": "active", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_BOOL", "jsonName": "active"}, {"name": "plan", "number": 3, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.SubscriptionItem.Plan", "jsonName": "plan"}], "nestedType": [{"name": "Plan", "field": [{"name": "vip", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.VipItem", "oneofIndex": 0, "jsonName": "vip"}, {"name": "sponsor", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.SponsorPlanItem", "oneofIndex": 0, "jsonName": "sponsor"}], "oneofDecl": [{"name": "plan"}]}]}, {"name": "GetChapterByProperty", "field": [{"name": "chapterId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "chapterId"}, {"name": "slugs", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.GetChapterByProperty.ByNovelAndChapterSlug", "oneofIndex": 0, "jsonName": "slugs"}], "nestedType": [{"name": "ByNovelAndChapterSlug", "field": [{"name": "novelSlug", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "novelSlug"}, {"name": "chapterSlug", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "jsonName": "chapterSlug"}]}], "oneofDecl": [{"name": "byProperty"}]}, {"name": "GetNovelRequest", "field": [{"name": "id", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "oneofIndex": 0, "jsonName": "id"}, {"name": "slug", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING", "oneofIndex": 0, "jsonName": "slug"}], "oneofDecl": [{"name": "selector"}]}, {"name": "GetNovelResponse", "field": [{"name": "item", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.NovelItem", "jsonName": "item"}]}, {"name": "GetChapterListRequest", "field": [{"name": "novelId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "novelId"}, {"name": "filter", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.GetChapterListRequest.FilterChapters", "jsonName": "filter"}], "nestedType": [{"name": "FilterChapters", "field": [{"name": "chapterGroupId", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.Int32Value", "jsonName": "chapterGroupId"}, {"name": "isAdvanceChapter", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".google.protobuf.BoolValue", "jsonName": "isAdvanceChapter"}]}]}, {"name": "GetChapterListResponse", "field": [{"name": "items", "number": 1, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterGroupItem", "jsonName": "items"}, {"name": "novelInfo", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.NovelItem", "jsonName": "novelInfo"}]}, {"name": "GetChapterRequest", "field": [{"name": "chapterProperty", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.GetChapterByProperty", "jsonName": "chapterProperty"}]}, {"name": "GetChapterResponse", "field": [{"name": "item", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.ChapterItem", "jsonName": "item"}]}, {"name": "UnlockItemRequest", "field": [{"name": "unlockMethod", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_ENUM", "typeName": ".wuxiaworld.api.v2.UnlockItemMethod", "jsonName": "unlockMethod"}, {"name": "item", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.UnlockedItem", "jsonName": "item"}]}, {"name": "UnlockItemResponse", "field": [{"name": "unlockedItem", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.UnlockedItem", "jsonName": "unlockedItem"}]}, {"name": "GetSubscriptionsRequest", "field": [{"name": "novelId", "number": 2, "label": "LABEL_OPTIONAL", "type": "TYPE_INT32", "jsonName": "novelId"}]}, {"name": "GetSubscriptionsResponse", "field": [{"name": "items", "number": 1, "label": "LABEL_REPEATED", "type": "TYPE_MESSAGE", "typeName": ".wuxiaworld.api.v2.SubscriptionItem", "jsonName": "items"}]}], "enumType": [{"name": "UnlockItemMethod", "value": [{"name": "UnlockMethodNone", "number": 0}, {"name": "UnlockMethodKarma", "number": 1}, {"name": "UnlockMethodVip", "number": 2}, {"name": "UnlockMethodSponsor", "number": 3}]}], "service": [{"name": "Novels", "method": [{"name": "GetNovel", "inputType": ".wuxiaworld.api.v2.GetNovelRequest", "outputType": ".wuxiaworld.api.v2.GetNovelResponse"}]}, {"name": "Chapters", "method": [{"name": "GetChapterList", "inputType": ".wuxiaworld.api.v2.GetChapterListRequest", "outputType": ".wuxiaworld.api.v2.GetChapterListResponse"}, {"name": "GetChapter", "inputType": ".wuxiaworld.api.v2.GetChapterRequest", "outputType": ".wuxiaworld.api.v2.GetChapterResponse"}]}, {"name": "Unlocks", "method": [{"name": "UnlockItem", "inputType": ".wuxiaworld.api.v2.UnlockItemRequest", "outputType": ".wuxiaworld.api.v2.UnlockItemResponse"}]}, {"name": "Subscriptions", "method": [{"name": "GetSubscriptions", "inputType": ".wuxiaworld.api.v2.GetSubscriptionsRequest", "outputType": ".wuxiaworld.api.v2.GetSubscriptionsResponse"}]}], "publicDependency": [0, 1], "syntax": "proto3"}]}