0%

小说章节合并插件-第六篇

小说合并插件第六篇

这一篇我们即将完成我们插件的第一个版本, 用这个插件可以把小说下载到本地。

上次我们编写了几个重要的事件处理函数, 接下来我们继续编写剩下的CHAPTER_DONE, CONTENT_DONE和NOVEL_DONE三个事件。

继续添加几个属性。

function Novel()
{
const EVT_CHAPTER_MORE = "chapterMore";
const EVT_CHAPTER_DONE = "chapterDone";
const EVT_CONTENT_MORE = "contentMore";
const EVT_CONTENT_DONE = "contentDone";
const EVT_NOVEL_DONE = "novelDone"

this.chapters = new Map(); // 存放小说所有数据, key为章节序号整数类型, value里包含了本章节的所有有用信息, 对象
this.escapeChapters = new Array(); // 保存没有URL的章节标题和序号
this.chapterCount = -1;
this.currentChapterCount = 0;
........

所有章节获取成功

需要说明的是我们在currentChapterCount里保存已经下载完成的章节总数, 在chapterCount里保存全部章节的总数。

所以chapterDone事件处理函数特别简单, 就一行, 不过前提是修改chapter_more函数, 需要把章节循环里的break去掉, 我在代码里特意注释过, 此外我们需要添加触发chapter_done事件的代码。。

  // 处理EVT_CHAPTER_MORE事件
this.main.addEventListener(EVT_CHAPTER_MORE, (e) =>
{
.........
if (url)
{
this.main.dispatchEvent(new CustomEvent(EVT_CHAPTER_MORE, {detail: url}));
}
else
{
this.main.dispatchEvent(new CustomEvent(EVT_CHAPTER_DONE, {detail: chapterNum}));
}
})
.............
});

在看chapter_done事件处理函数。

this.main.addEventListener(EVT_CHAPTER_DONE, (e) => 
{
this.chapterCount = e.detail;
});

把最后获取的章节序号付给chapterCount就可以了, 反正一本小说有300章那么这本小说的章节总数就是300, 当然在某些情况下这样做肯定出现问题, 不过先不管他。

下载缺失的章节

上次没写完的content_done函数, 注意最前面新增了一行内容, 此外这里判断是否开始下载缺失章节, 是否全部下载完成。

  this.main.addEventListener(EVT_CONTENT_DONE, (e) => 
{
this.currentChapterCount += 1; // 某个章节全部完成后自增一
........
this.updateTable({title, pageCount, summary})
// 是否开始下载缺失章节, 下载缺失章节写在了downloadEscape函数里
if (this.escapeChapters.length !== 0 && this.currentChapterCount === this.chapters.size)
{
this.downloadEscape();
}
// 所有任务是否完成
if (this.currentChapterCount === this.chapterCount)
{
this.main.dispatchEvent(new Event(EVT_NOVEL_DONE));
}
});

接下来是下载缺失章节的函数定义

this.downloadEscape = () => 
{
while (this.escapeChapters.length > 0)
{
let esc = this.escapeChapters.shift();
let url = this.chapters.get(esc.chapterNum - 1)?.nextChapterURL;
this.main.dispatchEvent(new CustomEvent(EVT_CONTENT_MORE, {detail: {url: url, chapterNum: esc.chapterNum}}));
}
}

只要在escapeChapters里有元素就拿出来触发CONTENT_MORE事件就可以了, 因为我们前面已经在上一张里存放了他们的URL。

生成全本小说生成下载接口

最后所有章节都下载完毕后触发如下事件

this.main.addEventListener(EVT_NOVEL_DONE, () => 
{
// 生成全本小说, 并且返回它的url, 是一个a元素, 可以加入到当前文档也可以直接click, 我们采用了第二种方法。
let downloadLink = this.makeFull();
this.downloadButton.innerHTML = "下载全本";
this.downloadButton.addEventListener("click", () => downloadLink.click());
});

在看看怎么生成全本小说, 怎么生成下载地址的。

this.makeFull = () => 
{
// 给章节列表排序, 因为chapters是无序的, 直接生成小说所有的顺序都乱了。
let chapterNums = Array.from(this.chapters.keys()).sort((x, y) => x - y);
let novelContent = new Array();
for (let num of chapterNums)
{
let chapter = this.chapters.get(num);
// 合并一张
let text = `\n${chapter.title}\n${chapter.pages.join("\n\n")}\n\n`;
novelContent.push(text);
}
// 生成二进制数据块
let blob = new Blob(novelContent);
// 创建url
let url = URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${document.title.split("_")[0]}.txt`;
return a;
}

到此为止我们可算是舒一口气了, 不过这是测试版, 还有很多问题, 比如下载一些小说比较好使, 换成其他小说可能就歇菜了, 所以最后一篇我们要完善这个插件, 让他相对良好稳定的运行。

我发现的一些问题列举如下:

  • 错误处理存在严重缺陷, 程序的健壮性; 稳定性不过关

  • 下载后的小说充斥大量空行, 少量乱码。

  • UI需要改进

    其中最大的还是错误处理, 那么下一次我们要解决如上三个问题, 尤其是错误处理需要重点突破……

    未完待续……