docs: spec 004 (KakaoPage source), its plan, and captured KakaoPage fixtures

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas Winther
2026-10-15 14:17:16 +02:00
co-authored by Claude Opus 5.5
parent 80185210ac
commit 899e8a0d54
10 changed files with 255 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
# KakaoPage source — implementation plan
> superpowers:executing-plans (native). **Spec:** [`docs/specs/004-kakaopage.md`](../specs/004-kakaopage.md).
**Goal:** a `kakaopage` Source registered next to `webtoons` and `tapas`, plus per-host request headers.
## Global constraints
`src/core` pure; tests offline on captured fixtures (done); no new dependencies.
## Review focus
1. `seriesId=N` must not match `N0` in Wayback rows. → Task 1 test, Task 4 history test.
2. A series without `land_thumbnail` (19+ edition) → one cover, no crash. → Task 3 test.
3. Old `dn-img-page.kakao.com …&filename=th3` URLs normalise to the original. → Task 1 test.
4. The Referer reaches `bff-page.kakao.com` through both the server proxy and the userscript. → Task 2 tests.
5. An unknown id gives a clear error. → Task 4 test.
### Task 1: URLs (`kakaopage/urls.ts`)
- [ ] Links (`/content/N`, `/home?seriesId=N`, `m.`), rejects; `normalizeImageUrl`; `isSameKakaoSeries`;
`overviewUrl(id)`. RED → GREEN → commit.
### Task 2: Request headers (`core/transport.ts`, `server/app.ts`, `userscript/gm.ts`)
- [ ] `requestHeadersFor(url)`; the server proxy and `gmTransport` send it (tests on both). RED → GREEN → commit.
### Task 3: Parsing (`kakaopage/parse.ts`)
- [ ] `parseOverview(json)` (comic, novel, 19+ without land thumbnail, error body); `extractCovers(html, pageUrl)`
for 2020/2022/2024 captures. RED → GREEN → commit.
### Task 4: Source and registry
- [ ] `kakaopage` Source through `FakeTransport` (resolve, currentCovers, unknown id, Wayback over both CDX
fixtures keeps only this id); registry, allowlists, `@connect`, hint copy; README, ARCHITECTURE, DECISIONS.
RED → GREEN → commit.
### Task 5: Verify and finish
- [ ] Real run in headless Chrome; final review; remove this plan; merge; push.
+64
View File
@@ -0,0 +1,64 @@
---
status: approved
---
# 004 — KakaoPage source
A third source (spec 001 §4.1): covers of a [KakaoPage](https://page.kakao.com) series, comics (웹툰) and
novels (웹소설). Design approved by the user in chat on 2026-10-15. Kakao Webtoon (`webtoon.kakao.com`) is a
different site and out of scope.
## 1. Verified facts (2026-10-15)
Checked against *악역의 엔딩은 죽음뿐* (Villains Are Destined to Die; comic 54845189, novel 52654302) and
*울프 인더 하우스 [19세 완전판]* (63659962, 19+). Fixtures in `test/fixtures/kakaopage/` and
`test/fixtures/wayback/kakaopage-*`.
- **Pages are rendered in the browser.** `page.kakao.com/content/<id>` is an 11 KB shell with a generic
`og:image`; the data comes from
`GET https://bff-page.kakao.com/api/gateway/api/v1/content/overview?series_id=<id>`.
- **That endpoint needs a `page.kakao.com` Referer (or Origin)**; without one it answers 403. It answers 19+
series without a login.
- **Its `result.content`:** `series_id`, `title`, `category` (`웹툰` / `웹소설`), `age_grade` (0, 12, 15, 19),
`lang` (`ko`), `thumbnail` (portrait key, e.g. 720×1030), `land_thumbnail` (landscape key, 540×370; may be null).
An unknown id answers with an error (`상품 정보를 찾을 수 없습니다`, "product not found").
- **Images:** `https://page-images.kakaoentcdn.com/download/resource?kid=<key>&filename=o1` is the original
(`th2`, `th3` and `/dims/resize/…` are smaller). No Referer needed. Older pages use `dn-img-page.kakao.com` with
the same query; old keys answer on the new host.
- **Wayback:** `page.kakao.com/home?seriesId=<id>` (the old address, 2020–2022) and `page.kakao.com/content/<id>`
(2022 on) are captured server-rendered. Every capture's `og:image` is this series' image (landscape in 2020–22,
portrait in 2024). 2020 captures also hold `props.initialState.series.series.thumbnailUrl` and
`landThumbnailUrl` in `__NEXT_DATA__`. Other images on the page (`sameSeriesList`, comments) are other series
or users.
## 2. Behaviour
- **Links:** `page.kakao.com/content/<id>` (with or without a trailing path/query) and
`page.kakao.com/home?seriesId=<id>`; `m.` hosts too.
- **Series:** from the overview endpoint: `externalId` = id, `title`, `language` = `lang` (`ko`),
`canonicalUrl` = `https://page.kakao.com/content/<id>`, `mature` = `age_grade >= 15` (the rating warning),
`flags.novel`, `archiveUrls` = [`page.kakao.com/content/<id>`, `page.kakao.com/home?seriesId=<id>`].
- **Current covers:** `thumbnail` → role `poster` (the suggested main cover), `land_thumbnail` → role `banner`.
- **History:** from a capture: `og:image` (role `other`: portrait or landscape, the shape decides the type) and,
when present, the series' own `thumbnailUrl` (`poster`) and `landThumbnailUrl` (`banner`).
- **Same series:** a link is the same series when its id (`/content/<id>` or `seriesId=<id>`) equals ours exactly,
so `seriesId=548451890` is another series.
- **Image URLs:** both hosts → `page-images.kakaoentcdn.com`, `filename=o1`, the `kid` kept, any `/dims/…`
dropped.
- **Request headers per host:** `requestHeadersFor(url)` in `core/transport.ts` (User-Agent as before, plus
`Referer: https://page.kakao.com/` for `bff-page.kakao.com`). The server proxy and the userscript send them.
- **Hosts:** fetch `page.kakao.com`, `m.page.kakao.com`, `bff-page.kakao.com`, `page-images.kakaoentcdn.com`,
`dn-img-page.kakao.com`; images the last two. The userscript `@connect`s `kakao.com` and `kakaoentcdn.com`.
- **Chrome:** the link hint names "WEBTOON, Tapas or KakaoPage".
## 3. Testing
Offline unit tests on the captures: link parsing, URL normalisation, same-series, overview parsing (comic,
novel, 19+, unknown id), archived pages (2020, 2022, 2024), Wayback rows through both prefixes, request headers.
Then a real run in headless Chrome (current covers, then yearly history).
## 4. Risks
- The overview endpoint is an internal API: its shape or its Referer check can change.
- Sending a `page.kakao.com` Referer is what KakaoPage's own page sends; like the browser User-Agent for
Webtoons, it is a browser-like header, not an identity.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"result":{"page_continue":{"first_read_product_id":63709590,"first_read_product_title":"울프 인 더 하우스 1화","first_read_is_free":true,"has_new_single":true,"has_unread_single":true},"display_ad":{"section_uid":"A15","banner_uid":"A15","trevi_uid":"","moment_uid":"DAN-9PaWqQNQpqtf5FFM"},"content":{"service_property":{"view_count":364548,"rating_count":3568,"bookmark_count":0,"highlighter_count":0,"rating_sum":35634,"comment_count":420,"preview":{"item":{"service_property":{},"operator_property":{},"asset_property":{},"uid":10525027,"product_id":63709590,"title":"울프 인 더 하우스 1화","thumbnail":"fg3Uw/hAdNQaSK14/b3yEf3a7xB6yICVVjkd5Hk","is_free":true,"state":"ST61","slide_type":"SD03","last_release_dt":"2024-03-18T18:01:47+09:00","size":3.0148,"page_count":33,"hidden":false,"free_change_dt":"1980-01-01T00:00:00+09:00","start_sale_dt":"2024-03-25T22:00:05+09:00","order_value":1,"version":"1.0"},"next_item":{"service_property":{},"operator_property":{},"asset_property":{},"uid":10525028,"product_id":63709592,"title":"울프 인 더 하우스 2화","thumbnail":"yffS8/hAd47i5f5Z/JwevO0PEbZFUa7hX7sRGEK","is_free":false,"state":"ST61","slide_type":"SD03","last_release_dt":"2024-03-18T18:01:47+09:00","size":2.9127,"page_count":33,"hidden":false,"free_change_dt":"1980-01-01T00:00:00+09:00","start_sale_dt":"2024-03-25T22:00:05+09:00","order_value":2,"version":"1.0"},"using_scroll":true}},"operator_property":{"is_manga":false},"asset_property":{},"series_id":63659962,"title":"울프 인더 하우스 [19세 완전판] ","thumbnail":"bn2lQs/hAd4XgsZ5Z/MtPVaiw3jf5BioxFC2tA91","category_uid":10,"category":"웹툰","sub_category_uid":119,"sub_category":"BL","is_all_free":false,"is_waitfree":true,"age_grade":19,"state":"ST61","on_issue":"N","series_type":"P","business_model":"T","authors":"박지연","description":"마지못해 사귀어왔던 애인과의 뻔한 이별. 후련한 마음으로 삶의 전환점을 찾던 민석은 대형견 백산이를 임시로 보호하게 된다. \n구질구질 구남친 대신 귀여운 멍멍이와 함께 자유를 만끽할 줄 알았는데... 생각보다 많이 엉뚱한 존재가 삶에 들어와버렸다!","free_slide_count":1,"last_slide_added_dt":"2024-03-25T22:00:05+09:00","start_sale_dt":"2024-03-25T22:00:05+09:00","waitfree_block_count":10,"waitfree_period_by_minute":1440,"sale_method":"TICKET","publisher_uid":33761,"on_sale_count":79,"talk_viewer_type":"TAP_SCROLL","first_product_id":63709590,"per_price_type":"S","translate_property":{"sub_category":{"th":"วาย","ko":"BL","en":"BL"},"category":{"th":"เว็บตูน","ko":"웹툰","en":"Comic"}},"lang":"ko","id":63659962}},"result_code":0,"response_time":"2026-10-15T21:15:32+09:00","message":"성공","message_key":"api_common_success"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,48 @@
20220930003210 https://page.kakao.com/content/54845189
20221008033151 https://page.kakao.com/content/54845189
20221101090844 https://page.kakao.com/content/54845189
20221205234140 https://page.kakao.com/content/54845189
20230324163551 https://page.kakao.com/content/54845189
20230531233552 https://page.kakao.com/content/54845189
20231001142338 https://page.kakao.com/content/54845189
20240226032102 https://page.kakao.com/content/54845189
20240318073658 https://page.kakao.com/content/54845189
20240523032432 https://page.kakao.com/content/54845189
20240601102321 https://page.kakao.com/content/54845189
20240701122409 https://page.kakao.com/content/54845189
20240801000811 https://page.kakao.com/content/54845189
20240901010746 https://page.kakao.com/content/54845189
20241001105828 https://page.kakao.com/content/54845189
20241101010508 https://page.kakao.com/content/54845189
20241203053728 https://page.kakao.com/content/54845189
20250314051917 https://page.kakao.com/content/54845189
20250406172928 https://page.kakao.com/content/54845189
20250530014613 https://page.kakao.com/content/54845189
20250714081524 https://page.kakao.com/content/54845189
20250905000510 https://page.kakao.com/content/54845189
20251007180706 https://page.kakao.com/content/54845189
20251105010252 https://page.kakao.com/content/54845189
20260115194553 https://page.kakao.com/content/54845189
20260219143912 https://page.kakao.com/content/54845189
20260510090433 https://page.kakao.com/content/54845189
20260815043603 https://page.kakao.com/content/54845189
20250116002021 https://page.kakao.com/content/54845189%0A
20260222073655 https://page.kakao.com/content/54845189/viewer/54846128
20221112093759 https://page.kakao.com/content/54845189/viewer/54848086
20260218104652 https://page.kakao.com/content/54845189/viewer/54848086
20221112093801 https://page.kakao.com/content/54845189/viewer/54848086?utm_source=68413516352
20260116045007 https://page.kakao.com/content/54845189/viewer/54872355
20230316171512 https://page.kakao.com/content/54845189/viewer/60579636
20230505142706 https://page.kakao.com/content/54845189/viewer/61824808
20241220095541 https://page.kakao.com/content/54845189?utm_source=sh_clip&plpu=https%3A%2F%2Flink-page.kakao.com%2Fopen%2Fcontent%3Fseries_id%3D54845189%26utm_source%3Dsh_clip%26alfallback%3Dstore
20260908183105 https://page.kakao.com/content/54845189?orderby=desc
20221013131308 https://page.kakao.com/content/54845189?utm_source=sh_clip&pscme=kakaopage%3A%2F%2Fopen%2Fcontent%3Fseries_id%3D54845189%26utm_source%3Dsh_clip
20250813010346 https://page.kakao.com/content/54845189?tab_type=about
20260613024948 https://page.kakao.com/content/54845189?tab_type=about
20241204083755 https://page.kakao.com/content/54845189?tab_type=notice
20250813014455 https://page.kakao.com/content/54845189?tab_type=notice
20260317095133 https://page.kakao.com/content/54845189?tab_type=notice
20250812030043 https://page.kakao.com/content/54845189?tab_type=overview
20260119153727 https://page.kakao.com/content/54845189?tab_type=overview
20260224113711 https://page.kakao.com/content/54845189?tab_type=overview
20240522172309 https://page.kakao.com/content/54845189?tab_type=product
+17
View File
@@ -0,0 +1,17 @@
20200928100812 https://page.kakao.com/home?seriesId=54845189
20201001065424 https://page.kakao.com/home?seriesId=54845189
20201101082642 https://page.kakao.com/home?seriesId=54845189
20210402034928 https://page.kakao.com/home?seriesId=54845189
20210523164837 https://page.kakao.com/home?seriesId=54845189
20211210081733 https://page.kakao.com/home?seriesId=54845189
20220506064341 https://page.kakao.com/home?seriesId=54845189
20220608173932 https://page.kakao.com/home?seriesId=54845189
20220902101346 https://page.kakao.com/home?seriesId=54845189
20210906090332 https://page.kakao.com/home?seriesId=54845189&utm_source=tw&utm_medium=thread&utm_campaign=09&utm_id=kakao
20210825154421 https://page.kakao.com/home?seriesId=54845189&utm_source=tw&utm_medium=thread&utm_campaign=endingdeath&utm_id=kakao
20210903094133 https://page.kakao.com/home?seriesId=54845189&utm_source=tw&utm_medium=thread&utm_campaign=endingdeath&utm_id=kakao
20210825185306 https://page.kakao.com/home?seriesId=54845189&utm_id=kakao
20220331090657 https://page.kakao.com/home?seriesId=54845189&utm_source=%E3%85%81%E3%84%B4%E3%84%B7%E3%84%B9
20211201094036 https://page.kakao.com/home?seriesId=54845189&utm_source=11
20220331122250 https://page.kakao.com/home?seriesId=54845189&utm_source=23%E3%85%87
20220601111259 https://page.kakao.com/home?seriesId=54845189&utm_source=5968