news 2026/8/14 1:54:47

ReadCat书源插件开发指南:三个方法讲透自定义小说书源的完整流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ReadCat书源插件开发指南:三个方法讲透自定义小说书源的完整流程

ReadCat书源插件开发指南:三个方法讲透自定义小说书源的完整流程

【免费下载链接】read-cat一款免费、开源、简洁、纯净、无广告的小说阅读器项目地址: https://gitcode.com/gh_mirrors/re/read-cat

ReadCat是一款免费、开源、简洁纯净的小说阅读器,它的书源插件系统是这款应用最核心的能力:通过一段JavaScript插件代码,就能让阅读器支持任意小说网站。这篇文章带你反向拆解一个成品书源插件,看透它背后的三个核心方法和一份声明,然后亲手写出属于你的第一个自定义小说书源。

先看成品:一个书源插件长什么样

在动手之前,我们先见一见"成品"。一个标准ReadCat书源插件,本质上就是一个带有固定静态属性的JavaScript类。把它保存成.js文件,在应用的"设置→插件"里一键导入,就能立刻使用。

class MyBookSource { static ID = 'mybooksource-00000001'; static TYPE = 0; static GROUP = '我的书源'; static NAME = '示例书源'; static VERSION = '1.0.0'; static VERSION_CODE = 1; static PLUGIN_FILE_URL = ''; static BASE_URL = 'https://example.com'; constructor(config) { this.request = config.request; this.store = config.store; this.cheerio = config.cheerio; } async search(searchkey) {} async getDetail(detailPageUrl) {} async getTextContent(chapter) {} }

是不是比想象中简单?这个类没有继承任何基类,也没有复杂的框架约束。接下来,我们像解谜一样,把每一块拼图拆开来看。

拆解第一层:那些静态属性是干什么的

类名上方那一串static属性,是插件在ReadCat眼中的"身份证"。在src/core/plugins/index.tsPlugins类中,每次导入插件都会先执行一次严格的校验(_isPlugin方法),逐项检查这些属性是否存在、格式是否正确:

  • ID:唯一标识,长度必须落在 16 到 32 位之间,只能由字母、数字、下划线和连字符组成,一旦重复会被拒绝导入;
  • TYPE:插件类型,0代表书源(BOOK_SOURCE),1是书城,2是TTS朗读引擎;
  • GROUP 与 NAME:分组名和显示名称,用于在插件列表里归类和展示;
  • VERSION 与 VERSION_CODE:版本号和版本代号,未来在线更新时靠它们做版本比较;
  • PLUGIN_FILE_URL:插件文件的更新地址,留空表示不支持在线更新;
  • BASE_URL:书源请求的基础域名,ReadCat用它来拼接页面链接。

对照src/core/plugins/defined/plugins.d.ts里的PluginBaseProps定义,你会发现这七项就是全部清单。写错任何一项,导入时都会弹出明确的错误提示,这也是排错最快的地方——先检查静态属性,再检查方法。

拆解第二层:三个核心方法,就是一条完整阅读链路

接口定义在src/core/plugins/defined/booksource.d.ts中,总共只有三个方法,它们恰好串起了用户从找书到读书的全流程:

第一步,search(searchkey):搜索入口。用户输入关键词后,ReadCat会把这个词传给所有启用的书源,收集它们返回的结果列表。每个结果是一个SearchEntity,包含书名、作者、封面图、详情页链接和最新章节标题。

async search(searchkey) { const { body } = await this.request.get(`${this.BASE_URL}/search?q=${searchkey}`); const $ = this.cheerio.load(body); const results = []; $('.book-item').each((i, el) => { results.push({ bookname: $(el).find('.name').text(), author: $(el).find('.author').text(), coverImageUrl: $(el).find('img').attr('src'), detailPageUrl: $(el).find('a').attr('href') }); }); return results; }

第二步,getDetail(detailPageUrl):详情与章节目录。传入搜索得到或书架中的详情页链接,返回完整的DetailEntity:书名、作者、封面、简介,以及最重要的——章节列表chapterList。每个章节是一个Chapter对象(标题、链接、序号),列表会按顺序显示在书籍详情页里。

async getDetail(detailPageUrl) { const { body } = await this.request.get(detailPageUrl); const $ = this.cheerio.load(body); const chapterList = []; $('.chapter-list a').each((i, el) => { chapterList.push({ title: $(el).text(), url: $(el).attr('href'), index: i }); }); return { bookname: $('.book-name').text(), author: $('.book-author').text(), coverImageUrl: $('.book-cover img').attr('src'), intro: $('.book-intro').text(), chapterList }; }

第三步,getTextContent(chapter):正文解析。传入一个章节对象,返回一个字符串数组,每个元素是一段正文。ReadCat在src/core/plugins/index.ts中会自动对返回结果做HTML消毒处理(sanitizeHTML),过滤掉广告脚本和不安全的标签,所以你只需要把正文抽干净就行。

async getTextContent(chapter) { const { body } = await this.request.get(chapter.url); const $ = this.cheerio.load(body); const paragraphs = []; $('#chapter-content p').each((i, el) => { paragraphs.push($(el).text().trim()); }); return paragraphs; }

src/core/plugins/booksource.ts里的isBookSource检查函数会逐一验证这三个方法是否都存在且为函数,缺一不可——所以哪怕你的书源只需要搜索功能,三个方法也得全部实现。

拆解第三层:构造函数里注入的四个工具

细心的你可能注意到了,构造函数接收了一个config对象,里面有四个开箱即用的工具:

  • request.get / request.post:封装的网络请求工具,替你处理了跨域、代理、编码转换等问题,返回{ body, code, headers }。这是书源插件唯一推荐的请求方式;
  • cheerio:服务端jQuery,用$(...)选择器解析HTML,比正则抠标签可靠得多;
  • store:插件专用的小型存储空间(setStoreValue / getStoreValue / removeStoreValue),适合缓存搜索历史、记住用户上次阅读位置,单个插件最多4MB,实现在src/core/plugins/store.ts
  • nanoid / uuid:生成随机ID的小工具,做去重、拼请求参数时很顺手。

这些工具全部由createPluginClassInstancesrc/core/plugins/index.ts中注入,插件代码运行在沙箱环境(vm2)里,既能访问这些安全能力,又碰不到主程序的其他部分,这也是ReadCat插件系统"纯净无广告"的底气所在。

验证环节:插件开发工具怎么用

写完之后怎么验证?ReadCat在设置页的"插件→开发"区域内置了插件开发工具:先导入.rpdt工具包,设置端口号,点击启动,就会弹出一个专门的调试窗口(对应源码electron/plugin-devtools.ts)。

调试窗口会实时显示插件代码里的console.log输出和报错堆栈,你可以直接对照报错信息修解析逻辑,改完重新导入即可,不需要重启应用。对于书源插件来说,这是最高效的排错手段。

从示例到实用:三个常见坑

最后分享三个新手最容易踩的坑,提前避开能省下大量调试时间:

  1. 详情页链接可能不是完整URL:网站内部链接常是/book/123.html这样的相对路径,记得用BASE_URL拼接成完整地址再请求;
  2. 封面图需要补全协议:很多网站的封面路径以//开头,建议判断后补上https:,否则封面加载不出来;
  3. 章节正文用text()抽取后记得trim():否则每段开头结尾的空格会让阅读排版显得松散,ReadCat的消毒过滤也会把空段剔掉。

下一步,交给你的想象力

到这里,你已经完整拆解了一个书源插件:一份静态声明 + 三个核心方法 + 四个注入工具。这套模式同样适用于TTS朗读引擎和书城插件的开发,接口定义都集中在src/core/plugins/defined/目录下,随时可以翻阅参考。

现在就可以打开 ReadCat,从https://gitcode.com/gh_mirrors/re/read-cat克隆源码研究细节,或者直接新建一个.js文件开始你的第一个自定义小说书源。先从你平时用得最多的那个小说网站练手,把搜索和正文解析跑通,你就正式入门了。当你的书源能让别人一键导入、直接读书时,那种成就感绝对值得你此刻的尝试。动手吧,第一个书源插件正在等你写完它!🚀

【免费下载链接】read-cat一款免费、开源、简洁、纯净、无广告的小说阅读器项目地址: https://gitcode.com/gh_mirrors/re/read-cat

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 1:54:21

Python defaultdict 原理、应用与性能优化全解析

1. 项目概述:为什么defaultdict是Python字典的“瑞士军刀”?如果你写过一段时间的Python,尤其是在处理一些需要分组、计数或者构建复杂嵌套数据结构的任务时,肯定遇到过这样的场景:你需要检查一个键是否存在于字典中&a…

作者头像 李华
网站建设 2026/8/14 1:51:04

从零构建嵌入式低延迟视频流媒体系统:V4L2+FFmpeg+RTP实战

最近在做一个嵌入式设备上的视频监控项目,客户要求将摄像头采集的视频,以低于200毫秒的延迟,实时推送到远端的PC客户端进行显示。这个需求听起来简单,不就是“采集-编码-传输-解码-显示”一条龙吗?但真动手做&#xff…

作者头像 李华
网站建设 2026/8/14 1:48:52

浏览器解锁加密音乐:免费、无需安装,3步搞定

浏览器解锁加密音乐:免费、无需安装,3步搞定 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: htt…

作者头像 李华
网站建设 2026/8/14 1:46:42

Ohook:3步永久激活Office 365订阅版全功能

Ohook:3步永久激活Office 365订阅版全功能 【免费下载链接】ohook An universal Office "activation" hook with main focus of enabling full functionality of subscription editions 项目地址: https://gitcode.com/gh_mirrors/oh/ohook 凌晨十…

作者头像 李华
网站建设 2026/8/14 1:45:19

LangGraph状态管理与归约器实战:构建有记忆的AI工作流

1. 从“健忘”到“博闻强记”:为什么 LangGraph 的状态管理是核心如果你刚开始接触 LangGraph,可能会觉得它和 LangChain 有点像,都是用来构建 LLM 应用的工作流框架。但当你真正上手,尤其是尝试构建一个多轮对话、需要记住上下文…

作者头像 李华
网站建设 2026/8/14 1:43:56

Spark入门实战:从单机测试到集群部署的完整路径与避坑指南

这类工具最值得先看的不是功能列表,而是能不能在普通环境里稳定跑起来,以及从单机测试到集群部署的路径是否清晰。Spark 作为一个分布式计算框架,它的核心价值在于处理大规模数据,但很多人在第一步——环境搭建和基础概念理解上就…

作者头像 李华