从0到能用:ReadCat书源插件开发实战指南
从0到能用ReadCat书源插件开发实战指南【免费下载链接】read-cat一款免费、开源、简洁、纯净、无广告的小说阅读器项目地址: https://gitcode.com/gh_mirrors/re/read-cat你有没有过这样的瞬间——深夜想重温某本老书搜了一圈发现常用阅读 App 里的书源要么失效、要么提示资源已被删除书架空空如也。ReadCat 这类开源小说阅读器之所以能一书在手、全网都有靠的正是书源插件它本质上是一段 JavaScript负责把某个小说网站的网页内容翻译成 App 能读懂的固定数据结构。本文就用一个完整可运行的示例带你走完 ReadCat 书源插件开发的全流程。一、先认识三方约法书源插件到底长什么样在动手前先理解插件和 App 的关系。可以把书源插件想象成一名同声传译网站吐出来的是杂乱的 HTMLApp 要求的是整齐的 JSON 字段插件站在中间做转换。这份翻译合同就写在src/core/plugins/defined/booksource.d.ts里只有三条任何书源都必须签字画押方法入参返回值职责search搜索关键词searchkeySearchEntity[]数组根据关键词找出候选书籍列表getDetail详情页 URLDetailEntity拉取一本书的完整信息书名、作者、简介、封面、章节列表getTextContent章节对象Chapterstring[]字符串数组返回某个章节的正文段落对应的数据结构同样在src/core/book/book.d.ts也很直观SearchEntity书名、作者、封面图、详情页链接、最新章节标题Chapter标题 URL 序号DetailEntity在SearchEntity基础上追加简介和chapterList。也就是说你只需要回答三个问题书名从哪搜、详情去哪抓、正文怎么取App 其余的事情书架、历史、缓存、阅读排版全都自己搞定。二、搭建环境拿到源码只需要两条命令开发书源插件你不需要完整构建桌面端但有一份源码在手查接口定义会方便得多git clone https://gitcode.com/gh_mirrors/re/read-cat cd read-cat npm install装完依赖后重点盯住这几个位置src/core/plugins/插件系统的核心实现booksource.ts里能看到加载时的校验逻辑——三个方法缺失或不是函数导入会直接报错src/core/plugins/defined/全部接口定义书源、书城、TTS、通用属性src/store/plugins.ts插件在前端的状态管理决定它在列表里的启停、分组和展示。三、写代码之前先补齐身份证属性除了三个业务方法每个插件类还必须带一组静态属性它们相当于插件的身份证导入时逐项校验格式不对会被拒绝。从src/core/plugins/index.ts的_isPlugin方法可以反推出全部硬性要求ID1632 位字符串只能含字母、数字、下划线和短横线TYPE数字类型0 表示书源另有书城、TTS 两类GROUP115 位分组名比如笔趣阁系NAME115 位显示名称VERSION字符串版本号如1.0.0VERSION_CODE数字版本号用于新旧对比PLUGIN_FILE_URL插件文件的更新地址必须是.js结尾的 http 链接BASE_URL书源的主站地址书源和书城插件必填。四、20 行起步写一个能跑通的最小书源下面这个骨架几乎是最短的可运行版本结构上只有一个类、三个方法、一组属性class MyNovelSource { static ID example_source_000001; static TYPE 0; // 0 代表书源 static GROUP 示例; static NAME 我的小说书源; static VERSION 1.0.0; static VERSION_CODE 1; static PLUGIN_FILE_URL ; static BASE_URL https://example.com; async search(keyword) { // 请求搜索结果页解析成 SearchEntity[] } async getDetail(detailPageUrl) { // 请求详情页返回 DetailEntity } async getTextContent(chapter) { // 请求章节页返回 string[] } } plugin.exports MyNovelSource;注意最后一行插件代码运行在一个隔离沙箱里必须通过plugin.exports把类交出去App 才能拿到它。接下来逐段填实现。搜索是最容易出效果的一步思路是用this.request.get请求搜索页 → 用cheerio解析 HTML → 映射成SearchEntity数组async search(keyword) { const { body } await this.request.get(${this.constructor.BASE_URL}/search?q${encodeURIComponent(keyword)}); const $ this.cheerio.load(body); const result []; $(.book-item).each((_, el) { const link $(el).find(a).first(); result.push({ bookname: $(el).find(.name).text().trim(), author: $(el).find(.author).text().trim(), coverImageUrl: $(el).find(img).attr(src), detailPageUrl: new URL(link.attr(href), this.constructor.BASE_URL).href, latestChapterTitle: $(el).find(.last-chapter).text().trim() }); }); return result; }this.request和this.cheerio都是构造插件实例时注入的能力不需要你自己引入依赖直接拿来用即可。getDetail比搜索多两件事要拼出完整的chapterList以及处理相对路径。很多网站章节链接写的是相对地址记得用new URL(href, BASE_URL).href补全async getDetail(detailPageUrl) { const { body } await this.request.get(detailPageUrl); const $ this.cheerio.load(body); const chapterList []; $(#chapter-list a).each((_, el) { chapterList.push({ title: $(el).text().trim(), url: new URL($(el).attr(href), this.constructor.BASE_URL).href, index: chapterList.length }); }); return { bookname: $(.book-name).text().trim(), author: $(.book-author).text().trim(), coverImageUrl: $(.book-cover img).attr(src), intro: $(#intro).text().trim(), chapterList }; }最后是正文解析getTextContent。最省事的做法是选中所有正文段落逐段取文本、去掉空行再整体返回async getTextContent(chapter) { const { body } await this.request.get(chapter.url); const $ this.cheerio.load(body); const paragraphs []; $(#content p).each((_, el) { const text $(el).text().trim(); text paragraphs.push(text); }); return paragraphs; }到这里一个能搜、能看详情、能翻正文的书源就完成了。剩下的问题只有一个怎么验证它真的能用。五、最快的验证方式用内置调试工具直接跑ReadCat 自带一套插件开发调试环境入口在设置 → 插件 → 开发代码在electron/plugin-devtools.ts与src/core/plugin-devtools/中。它的工作方式是本地起一个服务默认端口 6028通过 WebSocket 与调试面板通信然后把你的插件代码加载到隔离沙箱里。具体的验证路线是这样的在设置 → 插件页面导入工具包配置好端口号点击启动打开调试窗口把书源代码贴进去依次触发搜索 / 获取详情 / 获取正文三个动作在面板里观察返回结果与console日志——插件的log、error、warn都会以时间戳格式回传到面板。调试模式下导入插件不会写入数据库import方法里options.debug为真时跳过存储所以你反复改代码不会污染正式插件列表。调试模式下代码也不会被压缩报错信息更直观。用这个工具最快能在 1 分钟内确认搜索解析选择器写没写对这比在正式环境里反复导入导出高效得多。六、绕开三个常见的坑书源写出来容易跑顺难。根据源码里的实现细节新手最容易踩的是这三处坑一正文没经过消毒可能被脏数据污染。导入时插件系统会重写getTextContent对返回值逐个执行sanitizeHTML并过滤空串见src/core/plugins/index.ts。所以在方法内部不必手动清洗但返回结构必须是string[]传错类型会被拦截。坑二字符编码不对全文乱码。中文小说站不少是 GBK/GB2312 编码。this.request.get支持charset和urlencode两个配置项遇到乱码时显式声明编码即可例如const { body } await this.request.get(url, { charset: gbk });坑三封面图加载不出来。有些站点对防盗链做了限制图片直接用可能 403。ReadCat 的请求方法支持代理模式proxy: true由 App 统一走代理转发可以绕开大部分引用限制。七、进阶打磨分页、缓存与错误兜底基础功能跑通后可以从三个方向提升体验搜索结果分页。当站点一页只展示 20 条结果时可以把search的入参设计成关键词 页码或者参考src/hooks/default-pagination.ts的思路在书源内维护页码状态配合 App 的分页组件使用。搜索翻页的体验直接影响用户找书的效率。请求缓存。this.store是插件专属的键值存储上限 4MB可以把详情页的解析结果按 URL 缓存起来再次阅读时直接读缓存减少对目标站点的重复请求也能明显加快翻页速度。错误兜底。目标网站改版是常态。给每个方法包一层 try/catch解析失败时返回空数组而不是抛异常能让单个书源失效时不拖垮整个搜索流程。App 导入时本身也会校验函数是否存在但运行期异常要靠你自己兜住。八、打包分享与日常维护书源写好后把它导出成一个.js文件分享给朋友后对方在设置 → 插件的管理列表里点击导入按钮即可一键安装。维护方面有一个技巧值得养成习惯PLUGIN_FILE_URL字段填上你托管插件文件的可访问地址后App 内置的更新按钮会自动拉取新版本来比对VERSION_CODE用户侧无需手动反复导入。这意味着你只需要维护一个远程文件所有用户都能同步升级版本号记得每次递增。九、实战路线图接下来做什么如果你不想从零开始可以沿着这条路线一步步走先读src/core/plugins/defined/booksource.d.ts把三个方法的签名和字段含义吃透挑一个结构简单的网站比如静态列表页实现三方法并跑通调试工具对照src/store/plugins.ts观察插件导入、启停、分组的完整状态流转处理分页、缓存、编码这些加分项把成熟的书源放到线上填好PLUGIN_FILE_URL让用户一键更新。判断自己是否真正入门的标准很简单随手打开一个没做过的网站能在半小时内产出一个能搜能读的书源。第一次你可能要对照本文慢慢写第二个、第三个会越来越快。现在就打开你平时最常访问的小说站用这篇文章当脚手架写出你的第一个书源插件吧。相关的接口定义在src/core/plugins/defined/调试工具说明见electron/plugin-devtools.ts动手之后遇到任何问题回到源码里找答案往往比搜索更快。【免费下载链接】read-cat一款免费、开源、简洁、纯净、无广告的小说阅读器项目地址: https://gitcode.com/gh_mirrors/re/read-cat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考