ReadCat书源插件开发完全指南:三大接口一次讲透,让你想读什么就读什么
ReadCat书源插件开发完全指南:三大接口一次讲透,让你想读什么就读什么
【免费下载链接】read-cat一款免费、开源、简洁、纯净、无广告的小说阅读器项目地址: https://gitcode.com/gh_mirrors/re/read-cat
你有没有过这样的瞬间:装好一款小说阅读器,满怀期待地搜一本冷门书,结果页面转了几圈,弹出"没有找到相关内容";而别人的同款阅读器里,什么书都能搜到、能追更、能离线缓存。差别究竟在哪?答案往往藏在四个字里——书源插件。
ReadCat(一款免费、开源、简洁、纯净、无广告的小说阅读器)把"从哪里取内容"这件事完全开放了出来:任何网站的小说,只要有人为它写一个几十行的插件,就能无缝接入阅读器。也就是说,掌握ReadCat书源插件开发,就等于给自己配了一把打开任意书库的钥匙。这篇文章不打算按部就班地念说明书,而是模拟一次真实的开发闯关,带你从零把第一个可用书源跑起来。
一、先搞明白:书源插件到底是个什么东西
你可以把书源插件想象成一个"翻译官"。阅读器本身并不认识任何小说网站,它只知道三件事要做:搜书、看详情、读正文。书源插件的职责,就是替阅读器向某个具体网站发出这三类请求,再把网站返回的HTML"翻译"成阅读器能懂的数据格式。
翻译的结果长什么样,取决于一份约定俗成的"契约"。在ReadCat中,这份契约就写在src/core/plugins/defined/booksource.d.ts里,一个合格的书源只需要满足三个方法的签名:
interface BookSource { search: (searchkey: string) => Promise<SearchEntity[]>; getDetail: (detailPageUrl: string) => Promise<DetailEntity>; getTextContent: (chapter: Chapter) => Promise<string[]>; }是不是很直白?搜书返回书名列表,详情返回书籍信息和目录,正文返回章节文本数组。后面所有工作,都是围着这三个方法打转。
二、闯关第一站:把开发环境搭起来
动手之前,先确认手头有Node.js环境。接着在终端里把项目源码拉下来并安装依赖:
git clone https://gitcode.com/gh_mirrors/re/read-cat cd read-cat npm install依赖装完就够了吗?还差一步"热身"。建议先启动项目确认能跑通,再开始写插件,否则后面排查问题时会分不清是环境问题还是代码问题。启动方式看package.json里的scripts即可。
本关验收标准:项目能正常启动,你能在界面里找到"设置 → 插件"面板,并且看到内置的Edge TTS引擎已就位。看到这个面板,说明插件的加载链路是通的,你已经站在了正确的起跑线上。
三、闯关第二站:搭出插件的骨架
一个书源插件,本质上就是一个JavaScript类。类是外壳,类的静态属性负责"报名信息",构造函数里拿到的参数则是工具箱。
先看报名信息。src/core/plugins/index.ts里的_isPlugin方法相当于"体检中心",任何插件导入前都要过一遍检查,缺胳膊少腿的直接拒绝入场。体检项目包括:
- ID:16到32位,只能由字母、数字、下划线、短横线组成,相当于插件的身份证;
- TYPE:数字,0代表书源,1代表书城,2代表TTS引擎;
- GROUP / NAME:分组名与展示名,长度都有限制,注意别起太长的名字;
- VERSION / VERSION_CODE:一个给人看的版本字符串,一个用于程序比较的版本数字;
- PLUGIN_FILE_URL:插件文件的更新地址,便于以后自动升级;
- BASE_URL:书源对应的站点域名,书源类插件必须提供。
再看工具箱。插件类被实例化时,构造器会收到一个参数对象,里面是ReadCat替你封装好的能力:
class MyBookSource { constructor({ request, store, cheerio, nanoid, uuid }) { // request.get / request.post:发网络请求,自动处理代理与编码 // store:插件的私有存储,可跨会话保存数据 // cheerio:把HTML字符串变成可操作的DOM } }看到没?连HTML解析库都给你备好了,你真正要写的核心逻辑其实非常集中。
四、闯关第三站:实现三大核心能力
4.1 搜索:把关键词换成候选书单
search(keyword)做的事很简单:拼出搜索URL,发起请求,把结果列表"翻译"成SearchEntity。SearchEntity最少需要书名、作者和详情页链接三个字段,封面和最新章节标题是可选项。
async search(keyword) { const url = `${this.baseUrl}/search?q=${encodeURIComponent(keyword)}`; const { body } = await this.request.get(url); const $ = this.cheerio.load(body); const results = []; $('.book-item').each((_, el) => { results.push({ bookname: $(el).find('.name').text().trim(), author: $(el).find('.author').text().trim(), detailPageUrl: $(el).find('a').attr('href'), coverImageUrl: $(el).find('img').attr('src'), latestChapterTitle: $(el).find('.latest').text().trim(), }); }); return results; }这里的关键套路是"先看页面结构,再写选择器"。任何网站的搜索结果页打开浏览器开发者工具就能看到对应的DOM结构,照着写即可。
4.2 详情:把一本书的骨架拉出来
getDetail(detailPageUrl)接收搜索阶段拿到的详情页地址,返回书籍信息外加整本目录。注意这里有个特殊的字段——chapterList,它是一个章节数组,每个章节形如{ title, url, index }。
async getDetail(url) { const { body } = await this.request.get(url); const $ = this.cheerio.load(body); const chapterList = []; $('.chapter-list a').each((index, el) => { chapterList.push({ title: $(el).text().trim(), url: $(el).attr('href'), index, }); }); return { bookname: $('h1').text().trim(), author: $('.author').text().trim(), coverImageUrl: $('.cover img').attr('src'), intro: $('.intro').text().trim(), chapterList, }; }目录解析往往是全流程里最费神的一步:有的网站分卷、有的分页、有的章节藏在JS动态渲染里。先挑目录结构简单的站点练手,能少走很多弯路。
4.3 正文:把章节内容洗干净端上来
getTextContent(chapter)拿到的就是chapterList里的某一项,你要返回一个字符串数组,数组里的每个元素会被渲染成一个段落。
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(); if (text) paragraphs.push(text); }); return paragraphs; }有个细节值得留意:正文返回后,ReadCat会统一做一次HTML消毒(见src/core/plugins/index.ts中对getTextContent的包装),空段落会被过滤掉。这既是安全兜底,也提醒你返回的正文越干净,阅读体验越好——那些"请记住本站域名""手机阅读请访问XX"之类的广告尾巴,尽量在解析时就直接滤掉。
本关验收标准:三个方法都写完了,逻辑上能自洽——搜索能给出书,详情能给出目录,正文能给出段落。
五、闯关第四站:导入插件并跑通验证
写好的插件是一段JS文本,怎么让它被ReadCat认识?有两条路:
路径一:界面导入。进入"设置 → 插件"面板,点击导入按钮,选中你的my-book-source.js,导入成功的插件会出现在列表里,类型标签显示"书源"。
路径二:接口导入。在源码环境中,可以直接调用插件管理器的入口方法,把JS代码喂进去:
await GLOBAL_PLUGINS.importJSCode(jscode, { minify: true, enable: true });minify会压缩代码(默认也会压缩),enable决定导入后是否立即启用。导入失败的插件会进入"失败列表",并给出具体错误原因——这是排错的第一手资料。
本关验收标准:插件出现在列表中且状态为"启用",回到书架页搜索,能搜到你写的那个站点的书,点开能加载目录,点进章节能看到正文。到这一步,恭喜,你已经正式解锁了"自定义书源"这个技能。
六、避坑清单:新手最容易栽的五个坑
把高频翻车点整理成一张速查表,对号入座即可:
- 静态属性不过检:ID短于16位、GROUP超过15字符、BASE_URL没写
http(s)://前缀,导入直接失败。先看_isPlugin的校验规则再动手,别凭感觉写。 - 返回结构对不上:
search忘了返回detailPageUrl,详情页跳不过去;getDetail忘了拼chapterList,目录永远空白。返回字段名一个都不能改。 - 选择器写死:网站改版后旧选择器失效,这是书源"失联"的头号原因。解析逻辑尽量写健壮些,并预留兜底选择器。
- 忽略编码问题:部分老站点返回的不是UTF-8,乱码了别急着怀疑解析。
request.get支持在配置里指定字符集,先确认编码再写选择器。 - 滥用存储:插件的私有存储有4MB上限(见
store.ts中的PLUGIN_STORE_MAX_BYTE_LENGTH),拿它当缓存时注意控制体积,存到上限会抛错。
七、再进一步:从能用走向好用
跑通第一个书源只是起点。往深了走,还有三块值得探索的领地:
- 阅读体验打磨:正文里的广告过滤规则、图片防盗链处理、分页章节的合并策略,都能显著提升日常使用的舒适度;
- 插件类型拓展:除了书源,ReadCat还支持书城插件(
bookstore.d.ts)和TTS语音引擎插件(ttsengine.d.ts),内置的Edge语音引擎就在src/core/plugins/built-in/tts/edge.ts,是绝佳的参考范本; - 调试工具链:项目里带了插件开发工具包(相关实现见
src/core/plugin-devtools/与electron/plugin-devtools.ts),在"设置 → 插件"的开发面板里导入工具包、设置端口并启动,就能一边改代码一边实时验证,效率翻倍。
如果你想看插件系统如何在后台运转,src/core/plugins/index.ts是整个插件的"总调度室";想知道插件数据如何持久化,src/core/database/store/里的plugin-store.ts和plugins-jscode.ts给出了答案;插件管理界面则位于src/components/settings/components/plugin/。
结语:你的第一行插件代码,现在就值得开始
回到开头的疑问——为什么别人的阅读器什么书都能看?答案已经摆在你面前:不是那款软件有什么魔法,而是有人为它写了书源插件。现在,轮到你给自己写一个了。
不必追求一步到位,先找一个结构简单的小说站点,把搜索、详情、正文三个方法各写一个朴素版本,导入、跑通、翻车、修好。当你第一次在自己写的书源里翻到正文的那一刻,那种"原来如此"的踏实感,就是最好的正反馈。
今天就做一件事:打开你的小说网站,按F12看一眼它的搜索结果页结构,然后在本地建一个空JS文件,写下第一行class MyFirstBookSource {。剩下的,交给好奇心和这篇指南。
【免费下载链接】read-cat一款免费、开源、简洁、纯净、无广告的小说阅读器项目地址: https://gitcode.com/gh_mirrors/re/read-cat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
