为了一直以来收集的大量电子书不用受制于本地,能够方便地在线阅读查看,于是便开始了这番折腾。
在本地我是使用calibre进行管理的。线上应用自然也选择了可以直接使用calibre书库的calibre-web。于是就在服务器上搭建了一套calibre-web系统。
但是calibre-web用起来,实感功能还是有些不足,有着不少的功能缺失和使用不便。可目前也没有其他更好的选择,好在calibre-web是开源的,会使用Python语言和Flask框架就可以直接自行修改了。接下来就是一边记录自己使用过程中感觉到的问题,一边试着看源码自己修改。
在线阅读的分片加载
首先,最为严重的问题就是在线阅读时的加载。calibre-web需要将整个文件完全下载完成才能开始渲染,这大大降低了使用体验,浪费了时间与服务器流量。这个问题需要第一个解决。
最开始,我还以为会是一个很麻烦的问题,所以才会这么久都没有加入这个功能。还想着是否是需要后端读文件一页页解析再发给前端,后来发现其实并没有这么复杂。
先查看源码,发现calibre-web的在线阅读功能基本就是直接调用解析对应格式文件的js库,那么问题就在于这些js库是怎么请求、读取、渲染文件的了。
先从使用最多的pdf格式开始,calibre-web使用了Mozilla的pdf.js来解析pdf文件。查看pdf.js的使用说明,发现其实pdf.js已经内置了这项功能,只需要调用时设置开启就行。在readpdf.html
中加入disableRange
和disableAutoFetch
两个参数
window.addEventListener('webviewerloaded', function() {
PDFViewerApplicationOptions.set('disableAutoFetch', true);
PDFViewerApplicationOptions.set('disableRange', true);
});
disableRange
设置为true开启了范围请求加载,这样就可以分块获取数据。
disableAutoFetch
设置为true开启了数据的预读取,即使是不需要显示的页面,PDF.js也会自动继续获取更多数据。
这个功能使用的是HTTP的 Range requests范围请求,也就是常说的断点续传。
服务器通过响应头Accept-Ranges: bytes
告知客服端支持Range requests,通过Content-Length
告知文件大小。
客户端可以通过请求头Range
发送一个范围,告知本次请求的是那一段数据。这样就可以分片获取数据了。
后端也需要开启这个功能。对于flask来说则是返回Response
时accept_ranges
要设置为True。
从calibre-web的源码来看。传输pdf文件使用的是send_from_directory()
函数,send_from_directory()
调用send_file()
函数,而send_file()
的conditional
参数默认为True
,也就是并不需要修改,已经默认开启了该功能。
此时再进入在线阅读,能看到已经可以分片加载了。再打开开发人员工具网络窗口,就会看到大量的状态码为206的请求,这就是分片了的pdf文件
接下来就是查看epub.js 等其他库的使用
字体缺失
使用过程中发现不少pdf文件文字不显示。打开控制台可以看到各种bcmap文件的404警告,这明显是缺少字体了。
calibre-web并没有携带这些字体。那就自行下载,放入相应的目录中。但从官方的pdf.js中提取好字体出来后。再一看,这个请求的地址也很有问题,/calibre-web/read/web/cmaps/
这个url,calibre-web内根本就没有写路由。我的calibre-web是用nginx部署的,要解决这个问题的方法,我首先想到的是使用nginx转发,在原有的conf设置文件中加入一条配置。
location /calibre-web/read/web {
alias /pdfjs/web;
}
但要完全解决这个问题还是要找到根源,这个url是在哪里被请求的。查看源码可以看到,这个路径实际上是pdf.js的cMapUrl
默认值../web/cmaps/
,这个路径在完整的pdf.js内是正确的,但calibre-web只包含了主要的js文件,并没有携带相应的字体,发送请求时的相对路径也不是相对于js文件,而是相对于pdf文件路径,这就导致了这个奇怪的url。
在readpdf.html中修改调用pdf.js的cMapUrl
设置,由../web/cmaps/
改到static下,把下载来的文件放到static对应文件夹内就解决问题了。
本地化翻译
calibre-web的中文翻译并不完整,还有些错误。
calibre-web使用的是Flask本地化最通用的库Flask_Babel
要修改翻译文本,首先运行下面的命令:
pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot .
# 先收集需要翻译的文本到messages.pot文件
pybabel update -i messages.pot -d cps/translations -l zh_Hans_CN
# 生成中文对应的po文件
然后打开calibre-web/cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po
文件。
这个文件记录原文本与翻译的对应关系。内容是像下面这样的格式:
#: cps/about.py:43 cps/about.py:59
msgid "installed"
msgstr "已安装"
其中,#:表明字符串在源码出现的位置,msgid 是原英文文本,msgstr 是对应的翻译。在msgstr中填入中文翻译就可以了。 在这个文件中添加好缺失的文本,修改错误的文本再保存后,运行下面的命令就完成了中文翻译的修改。再重启calibre-web就能看到效果。
pybabel compile -d cps/translations
# 编译生成mo文件
新用户的语言设置固定是英文
注册第二个用户并登录时发现一个问题。注册邮件是英文的,新用户的默认语言也是英文的,必须要登录后进入设置页面进行设置,才能改成中文。这个太别扭了。
我认为合适的逻辑应该是在注册页面就能够选择页面的语言,注册所发送的邮件和新用户默认语言也应该跟随这个语言设置。
不过这个逻辑需要修改的地方比较多,页面上要加按钮,注册要传额外数据加额外判断。暂时就直接把ub.py中User类的语言属性locale = Column(String(2), default="en")
,默认值由en改为zh_Hans_CN。
calibre-web还有其它例如不能批量操作、不能按isbn抓取元数据、抓取数据的豆瓣源早已失效等不少的问题,有时间再能力范围内慢慢修改吧。
评论列表,共 1 条评论
嗨您好,我用的是Docker《lscr.io/linuxserver/calibre-web》这里边默认就开启了分片加载,但是为什么不生效呢。。。。。。