From 0cf5eccaf12a2f4de2bfd9208ddc4f97584daa7c Mon Sep 17 00:00:00 2001 From: lifegpc Date: Mon, 16 Feb 2026 11:44:32 +0800 Subject: [PATCH] Add support to get book info from book info page --- build.js | 1 + inject/qdbook.js | 3 ++ manifest.json | 21 ++++++++++ src/popup.tsx | 9 +++++ src/qdbook.ts | 103 +++++++++++++++++++++++++++++++++++++++++++++++ src/qdtypes.ts | 42 +++++++++++++++++++ src/types.ts | 17 +++++++- src/utils.ts | 9 +++++ 8 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 inject/qdbook.js create mode 100644 src/qdbook.ts diff --git a/build.js b/build.js index fd47972..c3ad43a 100644 --- a/build.js +++ b/build.js @@ -91,4 +91,5 @@ async function buildTsx(names, tsnames) { fs.rmSync('dist', { recursive: true, force: true }); fs.mkdirSync('dist', { recursive: true }); await build('qdchapter'); +await build('qdbook'); await buildTsx(['popup', 'settings'], ['background']); diff --git a/inject/qdbook.js b/inject/qdbook.js new file mode 100644 index 0000000..6fea686 --- /dev/null +++ b/inject/qdbook.js @@ -0,0 +1,3 @@ +window.addEventListener('load', function() { + window.postMessage({'@type': 'g_data', g_data}, '*'); +}) diff --git a/manifest.json b/manifest.json index 8b648b7..39107d2 100644 --- a/manifest.json +++ b/manifest.json @@ -41,6 +41,27 @@ ], "run_at": "document_start", "world": "MAIN" + }, + { + "matches": [ + "http://*.qidian.com/book/*", + "https://*.qidian.com/book/*" + ], + "js": [ + "dist/qdbook.js" + ], + "run_at": "document_start" + }, + { + "matches": [ + "http://*.qidian.com/book/*", + "https://*.qidian.com/book/*" + ], + "js": [ + "inject/qdbook.js" + ], + "run_at": "document_start", + "world": "MAIN" } ], "options_ui": { diff --git a/src/popup.tsx b/src/popup.tsx index 19f8cf6..91583e2 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -42,6 +42,12 @@ function PopupBody() { }); setResult(msg); } + if (params.page === 'qdbook') { + const msg = await sendMessageToTab(tab.id, { + type: 'GetQdBookInfo', + }); + setResult(msg); + } } catch (e) { setError(e instanceof Error ? e.message : 'Unknown error'); return; @@ -63,6 +69,9 @@ function PopupBody() { delete body.type; return ; } + if (result.ok && result.body?.type === 'QdBookInfo') { + return ; + } return ; } if (error) { diff --git a/src/qdbook.ts b/src/qdbook.ts new file mode 100644 index 0000000..9f6c05b --- /dev/null +++ b/src/qdbook.ts @@ -0,0 +1,103 @@ +import type { BookGData, QdBookTag } from "./qdtypes"; +import type { SendMessage, Message } from "./types"; +import { QdBookTagType } from "./qdtypes"; + +let g_data: BookGData | undefined; + +function get_book_name() { + const bookName = document.getElementById('bookName') as HTMLHeadingElement | null; + if (!bookName) { + throw new Error('Failed to find book name element'); + } + return bookName.innerText.trim(); +} + +function get_book_tags() { + const tags: QdBookTag[] = []; + const attribute = document.querySelector('p.book-attribute'); + if (attribute) { + for (const children of attribute.children) { + if (children instanceof HTMLSpanElement) { + if (children.classList.contains("dot")) { + continue; + } + tags.push({ + type: QdBookTagType.System, + name: children.innerText.trim(), + }); + } else if (children instanceof HTMLAnchorElement) { + tags.push({ + type: QdBookTagType.Category, + name: children.innerText.trim(), + url: children.href, + }); + } else { + throw new Error(`Unknown tag element: ${children.outerHTML}`); + } + } + } + const allLabel = document.querySelector('p.all-label'); + if (allLabel) { + for (const label of allLabel.children) { + if (label instanceof HTMLAnchorElement) { + tags.push({ + type: QdBookTagType.User, + name: label.innerText.trim(), + url: label.href, + }); + } else { + throw new Error(`Unknown user tag element: ${label.outerHTML}`); + } + } + } + return tags; +} + +window.addEventListener('message', (event) => { + const data = event.data; + if (data && data['@type'] === 'g_data') { + g_data = data.g_data; + console.log('Received g_data:', g_data); + } +}); + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + const m = message as SendMessage; + try { + if (m.type === 'GetQdBookInfo') { + if (!g_data) { + const msg: Message = { + ok: false, + code: 404, + msg: '没有找到g_data,可能是页面数据还没有加载完成,请稍后再试或者尝试刷新页面', + for: m.type, + }; + sendResponse(msg); + return; + } + const bookName = get_book_name(); + const msg: Message = { + ok: true, + code: 0, + body: { + type: 'QdBookInfo', + bookInfo: g_data, + bookName, + id: g_data.pageJson.bookId, + tags: get_book_tags(), + }, + for: m.type, + }; + sendResponse(msg); + } + } catch (e) { + console.error(e); + const msg: Message = { + ok: false, + code: 500, + msg: e instanceof Error ? e.message : 'Unknown error', + for: m.type, + }; + sendResponse(msg); + } +}); diff --git a/src/qdtypes.ts b/src/qdtypes.ts index 418e3ad..a48e9e6 100644 --- a/src/qdtypes.ts +++ b/src/qdtypes.ts @@ -166,3 +166,45 @@ export type PageContext = { }; }; }; + +export type BookGData = { + chanId: number; + checkLevel8: boolean; + firstChapterId: number; + imgUrl: string; + isBookAlbum: boolean; + isCatelog: boolean; + isPublication: number; + pageJson: { + authorInfo: { + authorId: string; + authorName: string; + avatar: string; + }, + bookId: number; + bookType: number; + isLogin: boolean; + isPublication: boolean; + isSign: number; + isVip: number; + salesMode: number; + signStatus: string; + } +} + +export type Volume = { + name: string; + id: number; +} + +export enum QdBookTagType { + System, + Category, + User, +} + +export type QdBookTag = { + name: string; + type: QdBookTagType; + url?: string; +} diff --git a/src/types.ts b/src/types.ts index 69bc4a8..9b98680 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import * as QdTypes from "./qdtypes"; +import type * as QdTypes from "./qdtypes"; export type DiscriminatedUnion< K extends PropertyKey, @@ -22,8 +22,17 @@ export type QdChapterInfo = { hash?: string; } +export type QDBookInfo = { + bookInfo: QdTypes.BookGData; + bookName: string; + /**Book ID */ + id: number; + tags: QdTypes.QdBookTag[]; +} + export type SendMessageMap = { GetQdChapterInfo: {}; + GetQdBookInfo: {}; SaveQdChapterInfo: { info: QdChapterInfo; }; @@ -33,6 +42,7 @@ export type SendMessage = DiscriminatedUnion<"type", SendMessageMap>; export type MessageMap = { QdChapterInfo: QdChapterInfo, + QdBookInfo: QDBookInfo, }; export type MessageBody = DiscriminatedUnion<"type", MessageMap>; @@ -51,8 +61,13 @@ export type QdChapterUrlParams = { chapterId: string; } +export type QdBookUrlParams = { + bookId: string; +} + export type UrlParamsMap = { qdchapter: QdChapterUrlParams; + qdbook: QdBookUrlParams; } export type UrlParams = DiscriminatedUnion<"page", UrlParamsMap>; diff --git a/src/utils.ts b/src/utils.ts index f750a43..4333870 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,7 @@ import type { UrlParams, SendMessage, Message } from "./types"; export const QD_CHAPTER_URLPATH_REGEX = /^\/chapter\/(\d+)\/(\d+)\/?$/; +export const QD_BOOK_URLPATH_REGEX = /^\/book\/(\d+)\/?$/; export function parseUrlParams(url: string | URL): UrlParams | undefined { const u = url instanceof URL ? url : new URL(url); @@ -14,6 +15,14 @@ export function parseUrlParams(url: string | URL): UrlParams | undefined { chapterId, } as UrlParams; } + const bookMatch = u.pathname.match(QD_BOOK_URLPATH_REGEX); + if (bookMatch) { + const [, bookId] = bookMatch; + return { + page: 'qdbook', + bookId, + } as UrlParams; + } } }