让我们组建一支乐队,一起来超越我们的偶像吧!
WinterBreak 发布以后,所有型号的 Kindle 都能越狱了。我跟着教程把手上的 KT3 成功越狱,装了 KOReader。现在可以自定义屏保、无线传输文件、直接看 EPUB 格式的电子书、指定中英文字体、支持 PDF 重排,使用体验极佳。
和 @regymm 聊天时想到可以尝试用 Kindle 播放视频,这下不得不当成墨水屏 Linux 开发板用了。经过一番简单的研究,我发现每帧画面显示完成后再开始显示下一帧画面,效果比画面显示到一半时就开始显示下一帧更好。显示灰度图像要花 600 ms 以上的时间,显示黑白图像大概只要 300 ms(一帧能玩,两帧流畅,三帧电竞)。于是就有了大致的思路:用 Kdenlive 将视频缩放后导出为 PNG 序列,用 ImageMagick 批量进行旋转和抖动处理,最后在 Kindle 上显示图像。
显示图像既可以用 Kindle 内置的 eips 命令,也可以用 FBInk 库。后者可以通过 py-fbink 在 Python 中调用,而且在安装 kindle-python 时已经附带了(同样预装好的库还包括 PIL)。
录视频时用的是有序抖色法处理图片,录完才发现效果更好的 Floyd-Steingerg 算法。暂时没有重新录制视频。
Kindle 插电熄屏时不会断开 WiFi。此时 SSH 登录上去,运行脚本,就可以不受干扰地显示图像。这种方法比较简单。
在等待图像显示时读取下一张图片,这样做对帧率或许有点帮助吧。
1#!/usr/bin/env python3
2import time
3from datetime import datetime, timedelta
4
5from _fbink import ffi
6from _fbink import lib as FBInk
7from PIL import Image
8
9fbink_cfg = ffi.new("FBInkConfig *")
10fbfd = FBInk.fbink_open()
11
12
13TOTAL = 2792
14
15
16def clear_screen():
17 global im
18 global raw_data
19 global raw_len
20 w, h = 600, 800
21 im = Image.new("L", (w, h), (0,))
22 fbink_cfg.is_cleared = True
23 fbink_cfg.is_flashing = True
24 raw_data = im.tobytes("raw")
25 raw_len = len(raw_data)
26 FBInk.fbink_print_raw_data(fbfd, raw_data, w, h, raw_len, 0, 0, fbink_cfg)
27 im = Image.open(f"/mnt/us/video/frame/frame_{1:05}.png")
28 im = im.convert("L")
29 raw_data = im.tobytes("raw")
30 raw_len = len(raw_data)
31 FBInk.fbink_wait_for_complete(fbfd, 0)
32 fbink_cfg.is_cleared = False
33 fbink_cfg.is_flashing = False
34
35
36def show_image(name):
37 global im
38 global raw_data
39 global raw_len
40 FBInk.fbink_print_raw_data(
41 fbfd,
42 raw_data,
43 im.width,
44 im.height,
45 raw_len,
46 300 - int(im.width / 2),
47 400 - int(im.height / 2),
48 fbink_cfg,
49 )
50 im = Image.open(f"/mnt/us/video/frame/frame_{name:05}.png")
51 im = im.convert("L")
52 raw_data = im.tobytes("raw")
53 raw_len = len(raw_data)
54 FBInk.fbink_wait_for_complete(fbfd, 0)
55
56
57try:
58 FBInk.fbink_init(fbfd, fbink_cfg)
59
60 start_time = datetime.now()
61 delta = timedelta(seconds=1 / 24)
62 clear_screen()
63 count = 0
64 for i in range(TOTAL):
65 target_time = start_time + delta * i
66 diff = target_time - datetime.now()
67 diff = diff.total_seconds()
68 if diff < 0:
69 continue
70 if diff > 0:
71 time.sleep(diff)
72 count += 1
73 show_image(i)
74finally:
75 FBInk.fbink_close(fbfd)
76
77print(f"FPS: {24 * count / TOTAL}")
19:36
用 Python 的多进程时,创建进程的方式会影响导入模块的状态。
例如,在 a.py
中有一个变量 x
:
1x = 0
我们导入这个模块,修改 x
的值,然后创建子进程:
1from multiprocessing import Process
2
3import a
4
5
6def f():
7 print(a.x)
8
9
10if __name__ == "__main__":
11 a.x = 1
12 p = Process(target=f)
13 p.start()
14 p.join()
在 Linux 下默认使用 fork
的方式,输出的结果是 1
。而 Windows 下默认方式是 spawn
,输出是 0
。
在 Linux 下,如果使用 set_start_method("spawn")
使用 spawn
方式创建子进程,输出同样是 0
。
19:50
最近写博客,发现 Alma Linux 9 源里的 pandoc 2.9.2.1 无法识别 Markdown 里以 @
开头的链接,比如这种:
1[@EE0000](https://zhaozuohong.vip)
而我本地 LMDE 6 源里 2.17.1.1 版本的 pandoc 就没有这个 bug。于是,把 CI 的容器里的 pandoc 升级了一下,改用 GitHub Release 里的最新版本。
另外发现 NexT 主题没有 emoji 的支持。虽然找到了 hexo-filter-github-emojis,但也有段时间没更新了。于是运行里面的 update-emojis.js
,更新了 emojis.json
,推到了 zhao/hexo-filter-github-emojis。安装时就直接从源码安装:
1pnpm install git+https://git.zhaozuohong.vip/zhao/hexo-filter-github-emojis
GitHub 的 emoji 可以看 ikatyang/emoji-cheat-sheet。
昨天在博客里写了个贸易计算器,不得不说,自建的博客还是舒服。表格、公式、emoji 甚至 js,想加就加,完全不受平台的约束。
趁机试了下 petite-vue。虽然只有英文的说明,但上手还是挺快的。
16:04
我在 distrobox 里装 Arch Linux,用来跑 MaaAssistantArknights 这种要求高版本 glibc 的软件。长久以来,疏于维护;上周末升级时遇到了两个问题:一是 pacman 密钥环过期了,算是意料之中,按照 Arch Wiki 中 pacman/Package signing 的方案顺利解决了;二是升级之后 cage 命令用不了,一运行就报错。第二个问题没找到什么解决方法,只得放弃。
对我来说,滚动发行版,尤其是像 Arch 这样更新激进而频繁的发行版,哪怕是在容器中,维护成本也实在是太高了。思来想去,还是决定把 distrobox 里的发行版换成 Debian bookworm。一方面降低维护成本,不用频繁升级,也不用担心升级之后出问题;另一方面,等我升级到基于 Debian bookworm 的 LMDE 6 后(虽然还没发布),在容器外,就可以直接使用容器内编译的 Python 了。
08:12
好久没玩同步音律喵赛克了。
这首《独角》又好听又简单。截图是初见成绩。
19:44
更新了 Debian 11.7,顺便编译了 6.3.0-tkg-tt
内核,趁机重启。
编译很顺利,安装时遇到一个小问题,提了 Issue。
10:53
为了测试网站在旧版浏览器上的表现,分别从 Slimjet 和 Mozilla 的 FTP 网站 下载了 Chrome 63 的 deb 包和 Firefox 78 的压缩包。
这两个版本分别是 2017 和 2020 年发布的。本以为跑不起来,特意装了 Debian Stretch 虚拟机,没想到 Debian 的向后兼容还算不错,两个浏览器都直接在 Debian Bullseye 上跑起来了。
最后还要记得把 Chrome 添加的软件源禁用、在 Firefox 设置里关掉自动更新,否则更新到最新版本,旧版就白装了。
17:40
最近帮助一图流升级网站。把技术应用在真实的网站上,真是种前所未有的体验。
我遇到的第一个问题,是打包相关的。一图流的网站用 CDN,按流量计费,非常烧钱。最初观察到后端接口返回的 json 的体积很大,于是启用了 gzip 压缩。然后是图片,从 png 改为 webp 有损压缩,手动调整参数,在看起来不糊的条件下尽量减小体积。
于是只剩下 js 了。最初没有将 js 分页面打包,还把 Element UI 放到了单独的文件里。尽管首页上只用了一小部分 Element UI 的组件,但在访问首页时,还是要把其它页面用到的 Element UI 的代码都下载下来。ECharts 没有使用 npm 安装,而是把它的 js 放在 static 目录下。网站的 js,一大半都是 Element UI 和 ECharts,就觉得“钱白花了”。
网站用的是 Nuxt 2 框架。我以前没有接触过这个框架,在网上搜索打包相关的内容,又看了文档,然而一无所获。以前 Vite 用惯了,看 Nuxt 2 启动开发服务器都要好久,非常不爽。索性新建项目,尝试把代码迁移到 Vite + Vue 3 + Element Plus。出乎意料的是,迁移过程非常顺利,代码几乎不用改就能跑起来。我用 vue-router 实现了分页打包的功能。装了两个插件,一个可视化地分析 js 产物,另一个能够将库从产物中排除。我把 Vue、Router、Element Plus、ECharts 等库都排除掉,在 html 里通过 CDN 引入,这样产物体积就小很多了。
下一步是 SSR。原先使用 Nuxt 2 时,并没有充分利用 SSR,页面上的数据,都是在 mounted()
中下载的,这也是需要改进的地方。我尝试安装了 vite-plugin-ssr,参照文档和示例代码,成功跑通了。对不同页面,分别采用不同的渲染策略:首页和礼包页面以数据展示为主,所以用服务端渲染;攒抽计算器和基建排班器以交互为主,加载又很慢,于是干脆用 SPA,这样页面出现时就立即能交互。然而,攒抽计算器加载很快,如果用 SSR,用户注意不到加载 js 的时间,用 SPA 反而会产生页面加载很慢的错觉。基建排班器加载却很慢,页面出现后要等很久才能交互,如果用 SSR,反而体验不好,有种网页坏了的感觉。所以攒抽页面用 SSR,基建页面用 SPA。最后是路由,出于简单起见,用 vite-plugin-ssr 的服务端路由,不再使用 vue-router。
加了 SSR 后,在打包时也要区分服务端和客户端。按照上面的做法,将一些库从客户端的打包产物中排除,在 html 里通过 CDN 引入;在服务端则不必这样做。好在 Vite 足够灵活,能够满足这样的需求。接着又重写了导航栏。原来的导航栏用的是 El-Menu,这个组件有几个缺点:不能右键,在新选项卡中打开、只能在客户端渲染,不能在服务端渲染、不够灵活,不能加按钮或开关等其它组件。以往首页切换亮色和暗色的按钮,做成菜单项,不是个好办法。再加上导航栏比较简单,样式也是现成的,于是自己写了一个。
将新网站上线后,本以为万事大吉,不料接连有用户反馈打不开。现象是 html、css 和图片完全正常,但 js 却被某种神秘力量阻碍,无法运行。同时在 QQ 内置的浏览器里,有些地方的样式也不对。本以为是网络问题,但又不像。经过排查后发现,QQ 和微信内置的浏览器不能识别 8 位 16 进制格式的颜色,只能用 rgba
格式,调整打包选项,就解决了这个问题。但是,js 依旧无法运行,毫无头绪。
某日,无意间看 nginx 的日志,发现一位有问题的用户,用的浏览器很老,User Agent 包含 Chrome/79
字样。再结合他的手机型号的发布时间,可以推断:他使用的小米浏览器虽然版本很新,但是它调用系统的 WebView。系统的 WebView 版本太老,不支持新的特性。我在手机上安装了 Chrome 79,果然有问题,不支持 ??
运算符。于是改了打包设置,目标设为 es2015,顺利解决。顺便还学会了在电脑的 Chrome 上调试手机浏览器里的网页。
不料第二天,又有一名用户反馈网站打不开。这次不是手机了,而是在电脑上。一问,他用的是 2345 浏览器。2345 浏览器的内核是 Chromium 69,不支持 globalThis
。搜索了一圈,通过 Polyfill.io 补上了这个功能。
然而第三天,又有一名用户反馈网站仍然打不开。他的系统 WebView 内核是 Chromium 61。本来以为会和上次一样顺利,不料 Chrome 61 不支持正则的 s
标志,Polyfill.io 也不包括这一功能。@vite/plugin-legacy
插件还和 vite-plugin-ssr 冲突。尝试在浏览器里直接引入 core-js 也没有成功。无奈只得放弃,劝他装个新版浏览器。
这样,一图流前端的迁移算是告一段落了。
09:46