Skip to content

Commit 70d7cc7

Browse files
committed
feat: 라이브러리 지원 추가
1 parent e2fcaa0 commit 70d7cc7

1 file changed

Lines changed: 37 additions & 15 deletions

File tree

WebtoonScraper/scrapers/_lezhin_comics.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,21 @@ def __init__(self, webtoon_id: str | tuple[str, str], /, *, bearer: str | None =
9696

9797
self._shuffled_directory = None
9898
self._unshuffled_directory = None
99+
100+
self.is_library = language_code.endswith("-library")
101+
language_code = language_code.removesuffix("-library")
102+
99103
self.language_code = language_code
100104
self.base_url = self.BASE_URLS[language_code]
101-
referrer = f"{self.base_url}/{self.language_code}/comic/{webtoon_id}"
105+
if self.is_library:
106+
referrer = f"{self.base_url}/{self.language_code}/library/comic/{self.LOCALES[self.language_code]}/{webtoon_id}"
107+
else:
108+
referrer = f"{self.base_url}/{self.language_code}/comic/{webtoon_id}"
102109

103110
super().__init__(webtoon_id)
104111
extra_headers = {
105112
"Referer": referrer,
113+
# NOTE: 나중에 작동 안 할지도 모르니 잘 확인하기
106114
"X-Lz-Adult": "0",
107115
"X-Lz-Allowadult": "false",
108116
"X-Lz-Country": "kr",
@@ -168,8 +176,12 @@ async def fetch_webtoon_information(self, *, reload: bool = False) -> None:
168176
@async_reload_manager
169177
async def fetch_episode_information(self, *, reload: bool = False) -> None:
170178
with WebtoonIdError.redirect_error(self):
179+
if self.is_library:
180+
url = f"{self.base_url}/{self.language_code}/library/comic/{self.LOCALES[self.language_code]}/{self.webtoon_id}"
181+
else:
182+
url = f"{self.base_url}/{self.language_code}/comic/{self.webtoon_id}"
171183
try:
172-
res = await self.client.get(f"{self.base_url}/{self.language_code}/comic/{self.webtoon_id}")
184+
res = await self.client.get(url)
173185
except HTTPStatusError as exc:
174186
if not exc.response.status_code == 307:
175187
raise # InvalidWebtoonIdError이거나 기타 위로 전파해야 할 오류들
@@ -219,19 +231,24 @@ async def fetch_episode_information(self, *, reload: bool = False) -> None:
219231
else:
220232
raise WebtoonIdError.from_webtoon_id(self.webtoon_id, LezhinComicsScraper)
221233

222-
selector = "body > div.lzCntnr > div > div > ul > li > a" # cspell: ignore Cntnr
223-
episode_dates: list[str] = []
224-
episode_states: list[str] = []
225-
for episode in res.match(selector):
226-
# *_: 'N시간 후 무료' 요소의 경우 개수가 3개임
227-
date_element, state_element, *_ = episode.css("a > div > div > div > div")
228-
episode_dates.append(date_element.text())
229-
episode_states.append(state_element.text())
234+
if self.is_library:
235+
self.episode_dates = None
236+
self.episode_states = None
237+
else:
238+
selector = "body > div.lzCntnr > div > div > ul > li > a" # cspell: ignore Cntnr
239+
episode_dates: list[str] = []
240+
episode_states: list[str] = []
241+
for episode in res.match(selector):
242+
# *_: 'N시간 후 무료' 요소의 경우 개수가 3개임
243+
date_element, state_element, *_ = episode.css("a > div > div > div > div")
244+
episode_dates.append(date_element.text())
245+
episode_states.append(state_element.text())
246+
247+
self.episode_dates: list[str] | None = episode_dates
248+
self.episode_states: list[str] | None = episode_states
230249

231-
self.episode_dates: list[str] = episode_dates
232-
self.episode_states: list[str] = episode_states
233-
if self.open_free_episode is None:
234-
self.open_free_episode = False
250+
if self.open_free_episode is None:
251+
self.open_free_episode = False
235252

236253
# webtoon 정보를 받아옴.
237254
title = data["meta"]["content"]["display"]["title"]
@@ -267,6 +284,9 @@ def _parse_user_information(self, data) -> None:
267284
default = {"isCollected": False, "isViewed": False}
268285
self.purchased_episodes = [episode_data_dict.get(episode_id, default)["isCollected"] for episode_id in self.episode_int_ids]
269286
self.viewed_episodes = [episode_data_dict.get(episode_id, default)["isViewed"] for episode_id in self.episode_int_ids]
287+
# 라이브러리인 경우 어차피 구매한 회차만 다운로드할 수 있기 때문에 나머지를 skip_download에 저장함
288+
if self.is_library and not self.skip_download:
289+
self.skip_download = [i for i, purchased in enumerate(self.purchased_episodes) if not purchased]
270290
if self.is_fhd_downloaded is None:
271291
self.is_fhd_downloaded = any(self.purchased_episodes)
272292

@@ -296,7 +316,7 @@ async def get_episode_image_urls(self, episode_no: int, retry: int = 3) -> list[
296316
episode_id_str = urllib.parse.quote(self.episode_ids[episode_no])
297317
episode_id_int = self.episode_int_ids[episode_no]
298318

299-
if self.open_free_episode and self.episode_states[episode_no] == "무료 공개":
319+
if self.open_free_episode and self.episode_states and self.episode_states[episode_no] == "무료 공개":
300320
await self._open_free_episode(episode_id_str)
301321

302322
keygen_url = (
@@ -402,6 +422,8 @@ def _extract_webtoon_id(cls, url) -> str | tuple[str, str] | None:
402422
match url.host, url.parts:
403423
case "www.lezhin.com" | "www.lezhinus.com" | "www.lezhin.jp", ("/", language_code, "comic", webtoon_id):
404424
return language_code, webtoon_id
425+
case "www.lezhin.com" | "www.lezhinus.com" | "www.lezhin.jp", ("/", language_code, "library", "comic", locale, webtoon_id):
426+
return f"{language_code}-library", webtoon_id
405427

406428
async def _download_image(
407429
self,

0 commit comments

Comments
 (0)