阅读视图

发现新文章,点击刷新页面。
🔲 ☆

Mikusa Annual Issue 6

我对 AI 的使用一直相当朴素,无非是把它当成一个更聪明一点的搜索引擎:遇到不会的东西,就在网页上来回车轮战,让 AI 猜解决方案,再把答案手动搬回编辑器里慢慢调试,最后一点点把功能做出来。例如之前提到的《给博客代码框增加复制按钮》,就是这样一点一点折腾出来的。

彼时的 AI 于我而言,更像一个“贴心的老师”——它愿意告诉我答案,却不能直接帮我写作业。因此,我对博客的修改大多只能停留在小修小补的层面。涉及稍微复杂一些的东西,我就无能为力了。

直到最近,我接触到了支持 AI Agent 的 IDE。第一次尝试借助 Google Antigravity 内置的 Claude Opus 模型编写小工具,亲眼看着代码从零开始一点点搭起来,我才真正意识到,如今的 AI 编程工具和前几年已经不是一回事了。它不再只是帮你补全几行代码、修正几处语法错误,而是可以直接阅读、理解,甚至参与修改整个项目。对于我这种完全不懂编程的小白来说,这种体验多少有点魔幻。

于是,我也终于决定开始实施一项蓄谋已久的计划:翻新 VOID 主题。

更新 VOID 主题

如果你对这个 AI 适配的 VOID 主题感兴趣,可以访问 这里 下载主题压缩包并安装。

距离 VOID 上一个 3.5.1 版本发布,已经过去了五年多。其间,Typecho 先后经历了 1.2.1 的小版本更新和 1.3.0 的大版本更新,而旧版 VOID 早在 1.2.0 时期就已经有启用后白屏的问题。

所以这一次,我没有一上来就急着做兼容适配,而是先借助 Claude Opus 4.6,把这些年我自己陆续加在 VOID 上的小改动一点点迁了过去。 毕竟对我来说,这些东西比适配新版 Typecho 还要更紧迫一些。

而后,我才让 Claude Code 针对 Typecho 1.3 及 PHP 8.2 的升级作专项适配,解决主题白屏等一系列兼容性问题。这中间我充分利用了上班摸鱼的时间,断断续续修改了大约一周。

功能增强

在我使用 VOID 的这些年,基于个人需求添加或改进了许多功能。我一直在思考这些小东西的必要性,是否只有我自己才用得到。最终还是决定,既然这是我个人修改的主题,那么就应该优先考虑自己的需求。在不破坏原主题整体设计的前提下,我完全可以将这些功能整合进主题源码,并公之于众。

代码复制

代码框的复制按钮其实早就合并到官方 VOID 仓库中了,这次是让 AI 根据原生剪切板 API 做了优化。

原本是想让它抄一下 GitHub 的剪切板方案,但没能抄成功。

GitHub 的复制按钮
GitHub 的复制按钮

最后改成鼠标划过代码框才浮出复制按钮,复制成功的提示则还是放在弹窗上。

OwO 表情包

这一块我曾让 Z 酱帮忙提交了我一些新增的米哈游表情包。这次则是针对我自己真实的使用情况,对这些表情包做了一些增删。

米哈游表情包
米哈游表情包

其实常用的还是不多。

写到这我才想起来,如果新增的表情包太多,超出数量的分组会因为溢出而被隐藏。于是火速让 Codex 修了这个 bug,现在的表情包选框是这样的:

支持横向滚动
支持横向滚动

但测试的时候又发现,这个表情包框只会往下弹,如果是没有评论的文章,就会把底部都撑开。于是就让 AI 糊成了这样:

感觉实际效果还是有些不太对,等后续有想法了再作修改吧。

归档页增强

这就完全属于我自娱自乐的功能了。去年写总结的时候想看看今年总共写了多少字、哪些主题,觉得一篇篇手数好麻烦,就让 AI 糊了这么个功能。

现在,归档页支持年度文章发布统计,只需将鼠标划过年份,即可展示年度总字数与分类聚合信息;同时,在文章列表右侧增加了其关联的分类标签,实测超多分类也可以正常显示。

该功能不可关闭

文章过时提示开关

VOID 会默认在文章发布 90 天后,提示其信息可能过时。

例如这个提示
例如这个提示

对于教程类或是强时效类的文章,这个提示很有必要。但若是日常类或其他不受时间影响的文章,就有些多余了。

于是,我在文章编辑页添加了一个开关,可按需展示内容是否过时。

新文章默认关闭过时提示。

主图来源

我的博客使用了大量来自 Pixiv、Twitter 和其他网站的图片。考虑到可能存在的风险,我曾使用 Copyright 插件将主图来源展示在文末。

但 VOID 的一大特色便是其主图,主图在主题的整体布局中具有十分重要的地位。那么,主图来源理应受到同等重视。

可我对这方面的设计毫无头绪。在询问 Gemini 后,它给出了下面 4 种方案。

文章封面图源样式演示
作者名称 · 2026-02-23
📷 题图:Pixiv @某画师
题图来自 Pixiv 📷
封面图片由 某画师 提供

(正文演示区域)前些时间提到,我的群晖 NAS 数据被套件误删了。由于是第三方套件造成的...

🎨 点击切换图源展示方案预览

如你所见,我选择的是最后一种。

设置也很简单。使用 markdown 或 HTML 格式填写主图来源,即可展示在文章顶部。

主图预览

严格来说,这个功能也有些鸡肋。但对我而言,它解决了一个很具体的问题:在正式发布前,确认主图在首页的展示效果。

Typecho 并不像 WordPress 那样提供完整的文章预览功能,所以我以前只能在本地另开一个测试博客,发布之后再查看实际呈现。这样的流程不仅低效,也很折腾。于是,我索性花了几天时间,借助 AI 把这个功能做了出来。

现在,VOID 支持在编辑页面预览首页封面卡片的实际效果,支持原 VOID 主题预设的三种首页主图样式,支持预览卡片的摘要随实际摘要变动。

卡片上方的文字描述可能还需要再稍微改改,但预览效果大体上是符合实际首页的。

评论交互增强

以前的评论其实也没什么大问题,只是层级一多,就看不出来回复顺序。

中间考虑过这么设计。但这样子的话,后续的回复会随着评论量增加越来越往右边挤。

我把目光移到了隔壁 B 站。

最后还是抄了一把叔叔的设计,为 VOID 的评论区做了这几点改动:

  • 不再纠结层级问题,不管是谁回复谁,都按回复时间顺序排列
  • 第一条回复、自己回复自己,均隐去 回复@xxx 的字样
  • 将评论时间、赞踩按钮移动到了评论主体下方,回复按钮转移到踩按钮的右侧
  • 默认折叠超过两条以上的评论

折叠效果如下图所示:

折叠时会有轻微的回弹,应该是个 bug,我问不出原因来,暂且搁置……

同时,我还调整了回复按钮的交互。点击 回复按钮 展开评论框时,回复按钮会自动变成 取消回复 按钮。

我本来想干掉右侧的 取消回复 按钮,但 AI 说应当保留,避免访客找不到回复按钮,就留着了。

字数统计

又一个鸡肋的功能,用于实时预估当前编辑内容的字数、图片数和标签数。

该功能依赖 VOID 插件,随插件启用生效。

兼容适配

关于 VOID 在 Typecho 1.2.0 等后续版本白屏的问题,AI 给出了几点主要原因:

  • 继承签名对齐 (致命修复):修复了 libs/Comments.php。Typecho 1.3 核心类引入了 PHP 强类型提示,主题原有的 ___children() 等重写方法因缺少 : array 或类型不匹配导致 Fatal Error (白屏)
  • Null 传参防御:PHP 8.1+ 严禁向系统函数(如 preg_match)传递 null。我们在libs/Utils.php (UA 处理) 和 libs/Contents.php (图片尺寸) 处添加了严谨的 null 保护。
  • 调试开关:发现主题 functions.phperror_reporting(0) 会掩盖报错堆栈,适配阶段已建议通过控制台或手动调整显隐。

这一部分就太过硬核了,没有我能插嘴的余地,AI 说啥就是啥。

后台优化

这个想法,源于我在添加字数统计功能时的一次临时起意。

原本我并没有打算细化这一部分的样式,没想到 AI 在生成功能时,顺带把字数统计区域的视觉样式也设计了出来。实际效果意外地不错,于是我索性以此为起点,将整个 VOID 中涉及到的字段样式重新梳理并统一设计了一遍。

整体设计基本由 Gemini 主导。它建议我将 选项 改为滑块、将 是否 改为开关;而我则根据自己的理解,把文章类型中的 landscape 译作 封面文章,以便与旁边的 一般文章 在字面上形成对应。

我还调整了部分字段的展示逻辑。例如,只有在填写文章主图后,才会展开与主图相关的其他设置。

首页卡片预览也默认隐藏,只能手动展开查看。展开后还可以根据首页主图样式自由调整并预览实际效果。

体验优化

在把我需要的功能全部补齐之后,我又问 AI:VOID 还有哪些地方值得继续升级?它首先指出的是 PJAX 的问题。

PJAX 上一次更新是 2017 年,而今天的现代浏览器已经具备实现这类局部刷新的核心能力。于是,我让 AI 把主题里原本依赖 jquery.pjax 的插件式方案,重构成了一个基于浏览器原生能力实现的 VoidPjax。新的实现直接使用 fetchDOMParserhistory.pushState/popstate 和原生事件代理来完成页面局部跳转、容器替换与历史管理,同时保留 pjax:* 事件兼容层。这样不仅减少了对老旧插件的依赖,也让后续在脚本重跑、一次性脚本去重、Safari 兼容,以及评论区局部刷新这类细节场景上的控制更加从容。

也许应该直接干掉 PJAX,但我没什么好的想法让 AI 执行,改天再问问看。

此外,bigfoot 已经十几年没更新过了,我用 littlefoot 完整替换了 bigfoot,优化了脚注在暗色模式、触屏设备和 PJAX 场景下的体验;pangu.js 也一并更新到最新版本,并切换使用新版 API,减少脚注、数学公式附近的错误加空格问题;MathJax 也从 2.7.4 迁移到 4.1.1,并精简运行时使用的资源包。

数学公式我用得不多,实际使用中可能会存在其他未知的 bug。

jQuery 更新到了 3.7.1;Prism高亮插件更新到了 1.30.0;fancybox 大版本改动太大,更到了最后一个 3.5.7;Headroom 导航栏自动隐藏、tocbot 目录树等也一并更新到了最新版本。

其他优化则是在主题构建方面,例如:

  • 移除 node-sass,切换到 Dart sass。
  • 升级 Gulp 到 5.x,并同步更新相关依赖锁文件。
  • 迁移 Sass 模块写法,转向 meta.load-css。
  • 升级 gulp-sass、sass、del、gulp-autoprefixer、gulp-rev 等构建依赖。
  • 升级 GitHub Actions 工作流与 CI 环境到 Node 20。
  • 修复构建过程中错误扫描 node_modules 并将其内容误带入产物的问题。
  • 调整 clean / move / md5 等构建任务实现,使新工具链下构建结果稳定。
  • 迁移 ESLint 到新配置格式。

至此,VOID 主题的适配与升级,就告一段落了。完整的修改记录可以查看从 这个 开始的 commit。

更新 VOID 插件

如果你对这个 AI 适配的 VOID 插件感兴趣,可以访问 这里 下载插件压缩包并安装。

接下来是 VOID 主题配套插件的适配问题。改起来倒是方便,AI 三下五除二就糊好了。

但主要的问题是,旧版本解析评论 IP 与归属地使用的 17monipdb.dat IP库太过古老,我想更换一个支持 IPv6 的、数据更新更及时一些的数据库。AI 给出的推荐是 ip2region

因此,现在 VOID 插件使用的 IP 数据库为 ip2region xdb,支持 IPv4 与 IPv6;UA 识别范围也更完整,后台互动页可以更准确地显示访客浏览器与系统信息。

修改前
修改前
修改后
修改后

当然,还包括后台编辑器新增的实时统计面板。可在文章及独立页面实时显示字数、图片数与标签数。

更新 ExSearch 插件

如果你对这个 AI 适配的 ExSearch 插件感兴趣,可以访问 这里 下载插件压缩包并安装。

既然 VOID 插件都适配了,索性把 ExSearch 插件也糊了一遍。

当前 ExSearch 插件已适配 Typecho 1.3,并将搜索前端改为原生实现,去除了运行时对 jQuery 的依赖。另优化了搜索界面的样式、适配了夜间模式,提升桌面与移动端的一致性。

日间
日间
夜间
夜间

更新 APlayer 插件

如果你对这个 AI 适配的 APlayer 插件感兴趣,可以访问 这里 下载插件压缩包并安装。

APlayer 在 Typecho 1.3 下直接失效了。我有时需要在文章内插入音乐,就也让 ChatGPT 完整适配了下。

顺便更新了 MetingJS。在新版本 MetingJS 的加持下,Aplayer 可以默认使用 <meting-js> 中 Custom Element 来渲染播放器。因此,在常见的 PJAX 场景中,新页面内容插入后会自动初始化播放器,旧节点移除时也会自动销毁,一般不需要再额外添加 loadMeting() 一类的重载函数。

最后

本来还想再写一写关于 Typecho-Plugin-PandaBangumiTypecho-Plugin-Copyright 这两个插件的修改,前者用于展示番剧日历、卡片,后者用于在文末展示版权声明。可尝试让 AI 总结 PandaBangumi 的改动情况时(因为这个插件还是我一点点用网页糊出来的,改得比较乱),它说:

我顺手看到的几个后续注意点

  • 番剧卡片样式被限制在 .yue .bgm-card 下,如果主题没有 .yue 这个容器类,卡片会功能正常但样式不完整。
  • loadCalendar() 只取了第一个 .bgm-calendar,如果同一页放多个日历实例,后面的不会渲染 PandaBangumi.js
  • 卡片标题做了转义,但列表里的 item.name / item.name_cn 仍然直接拼进 HTML,卡片标签 tag.name 也没有转义;数据源通常可信,但严格说这块还可以再补一层 PandaBangumi.js
  • cURL 关闭了 SSL 证书校验,能减少环境问题,但安全性偏保守。

所以,这部分还得等后续继续让 AI 把这些问题修完,才能正式发布。

Copyright 插件则是我计划塞一堆声明样式,但方向没设计好,越糊越乱,也不能发出来……

所以,对主题与插件的改动记录,便到此为止了。写完这篇日志,我也该回头去填别的坑了,不然时间一长,我就要忘掉它们怎么写了。

下篇文章见!

🔲 ☆

Backrest:自建备份方案的一种可能

前些时间提到,我的群晖 NAS 数据被套件误删了。由于是第三方套件造成的,即便我之前使用的是正版群晖硬件,也不可能就此向官方讨个说法。不过,我仍庆幸群晖有着强大的应用生态,能借助多种工具的协同配合完成重要数据的备份。例如,我通过 Hyper Backup 对 Docker 数据文件夹做了增量备份,又用 CloudSync 将这些备份同步到了云端。当本地数据意外丢失时,这些云端的备份全部得以幸免于难。

可换用飞牛的 fnOS 作为 NAS 系统之后,这套备份组合也随之失效。

fnOS 无论是界面、应用、生态还是移动端 APP ,都很符合我现阶段对一个 NAS 系统的基本需求。唯一美中不足的地方,就是它的备份功能。

作为 NAS 系统,其备份功能仍显不足。即便是迭代至正式版,它支持的备份方式也依旧有限。而且在实际使用过程中,备份到百度网盘的数据还会因为体积太大而经常上传失败。

我本想着,既然现在的 fnOS 备份不方便,说不定后续的更新迭代会优化这一块,要不就先放一边,等下次想起来再说。可谁曾想,飞牛于近日被爆出存在超高危的 0day 漏洞

这可把我吓了一跳。好在我一直通过 VPN 访问家庭网络,从未将 NAS 公开至公网,仔细检查后也没有发现病毒。

但要命的是,虽然该问题在系统更新后被修复(?),但是飞牛官方对此次事件采取的却是近乎冷处理的公关方式:既没有第一时间向用户发出明确的风险预警和紧急防护指引,也未做任何醒目的升级提醒,更是使用「异常访问风险」、「自身代码疏漏」这类模糊表述淡化问题本质,迟迟不肯正面承认此次漏洞产生的严重危害。

软件存在问题在所难免,承认错误及时处理便是,可飞牛对待此类安全问题却是这般敷衍的态度。我开始重新评估自己对该系统安全性的信任边界。这次的攻击者只是利用漏洞将 NAS 当作肉鸡对外攻击,更现实的担忧在于,若未来攻击直接针对数据本身,后果可能远比此次事件严重。我不希望遇上数据被病毒加密勒索的糟心事,不希望暴露 XP,更不想重新整理一遍动画片……

眼下 NAS 不在身边,我没法立刻更换系统,只能先决定即刻采取备份措施,避免后续出现任何数据安全意外。

但离了群晖的 Hyper Backup 和 Cloudsync 的助阵,有什么简单的方法,可以实现定时的增量备份,并将备份数据保存到云端呢?

这就需要请 Backrest 登场了。

关于

在介绍 Backrest 之前,有必要先引入一个几乎被所有备份方案反复提及的原则——「3-2-1 备份原则」。

所谓「3-2-1 原则」,是指在进行文件备份时,需要做到:

  • 3:至少保留 3 份完整的数据副本,防误删。例如:

    • 一份原始数据
    • 一份本地备份(同地 / 同机器)
    • 一份异地备份(云存储 / 异地 NAS)
  • 2:存储在 2 种不同的介质上,防硬盘损坏。例如:

    • 本地磁盘(机械硬盘、固态硬盘、光盘、磁带)
    • 云存储
  • 1:存储 1 份备份在异地,防勒索病毒、极端物理灾害。例如:

    • 异地 NAS
    • 云存储

在合理执行的前提下,可以最大限度降低数据丢失的风险。

可是,即便我们有意愿按「3-2-1 原则」执行备份,却需要面对另一个残酷的现实:如今机械硬盘的价格,已不是常人所能轻松负担的了。

8T机械硬盘
8T机械硬盘

我 2022 年 3 月购买的一块全新国行 8T 西数 HC320 的价格为 995 元。

但是!细心的小伙伴们应该已经注意到了,在「3-2-1 原则」的示例中,几乎每一步都可以将数据备份到「云存储」上。而云存储的价格,远比同等容量的硬盘便宜。

除了网盘,还有 COS、OSS、S3、B2 等一众云存储服务可供选择,价格都比购买实体硬盘要实惠,我们便可以考虑直接将数据备份到一个乃至多个不同的云端,以较低的成本满足「3-2-1 备份原则」的要求。

最终,我决定使用 Restic 作为我备份体系的核心。

Restic 是一款优秀的命令行备份工具,以快速、安全、高效著称。它支持:

  • 增量备份、数据去重和端到端加密,兼顾高效与安全
  • 多种主流存储后端,包括本地磁盘、网络共享目录(SMB/NFS)、远程服务器(SFTP/SSH)及各类对象存储(AWS S3、阿里云OSS、腾讯云COS等)、云存储服务(OpenStack Swift、Azure Blob Storage等)
  • 断点续传,备份中断后再次执行可从断点继续,无需重新开始
  • 任意快照点恢复(可恢复至某一次备份完整状态),也支持文件级精准恢复,恢复速度快且无需复杂配置,通过命令即可完成
  • 提供仓库校验命令,可定期检查备份数据块的完整性、修复损坏或丢失问题
  • 采用不可变仓库结构,备份过程中不修改已存储的历史数据块,避免历史备份损坏

但由于缺乏图形界面与调度管理功能,仍需配合脚本与 cron 实现自动化。对不想折腾这些东西的我来说,使用起来非常痛苦。

Backrest 正是在这一背景下登场的。作为一款专为简化 Restic 备份管理而设计的 Web UI 和编排工具,Backrest 通过直观的图形界面包装了 Restic 的命令行功能,并补齐了任务调度、状态监控与集中管理等关键功能,让复杂的 Restic 备份操作变得简单易行。

借助 Backrest,我们便能使用 Restic 实现一套可以长期稳定运行、也更容易被维护的备份方案。

准备

在正式开始之前,我们需要先思考一个很严肃的问题:

我们应该备份哪些数据?

备份并不是简单地把所有文件原封不动地复制一份。操作系统、软件包,甚至容器和镜像,本质上都属于可重建资源,只要环境还在,花一点时间就能重新部署。真正值得被长期保存的,是那些随着使用不断产生、积累,且一旦丢失就很难恢复的数据。

从实际使用场景来看,以下几类内容通常应当作为备份的重点:

  • 用户数据与业务数据
    例如文档、照片、媒体文件、项目资料,以及服务运行过程中产生的数据文件。这些内容往往具有唯一性,一旦丢失,几乎无法通过其他方式找回。
  • 服务与系统配置
    包括应用配置文件、服务参数、自定义脚本等。这些文件体积不大,却高度个性化。合理备份可以大幅降低系统重建时的时间成本。
  • 容器的持久化数据
    在使用 Docker 等容器化方案时,需要被备份的并不是容器本身,而是 Volume 或挂载目录中的数据。例如数据库文件、上传内容、状态数据等。
  • 高恢复成本数据

    例如游戏资源、音乐等。严格意义上说,这些文件并非完全不可再获取,但一旦涉及到命名规范、目录结构、封面整理和元数据获取,它们的恢复成本就不再只是「重新下载」那么简单。

相对应地,也有一些内容并不适合作为常规备份对象:

  • 操作系统本身
    系统可以重装,软件可以重新安装,将其纳入备份往往只会徒增体积和复杂度。
  • 缓存、临时文件和可再生成数据
    如构建产物、下载缓存、日志缓存等,这类数据即便丢失,恢复成本也相对较低。
  • 镜像、安装包等可从官方渠道重新获取的资源
  • 可下载的影音视频资源

通过这种取舍,将备份的重点从「尽可能多地保存数据」,转变为「只保存真正重要的那一部分」。不仅能有效控制备份体积和成本,也可以让后续的备份与恢复过程更加清晰、可控。

安装

这里以 Docker Compose 安装 Backrest 为例。

首先,需要在宿主机上创建好必要的数据文件夹,用于存放程序数据、配置、缓存和临时文件:

mkdir -p data config cache tmp rclone

随后新建 compose.yml 文件,填入以下内容:

services:
  backrest:
    image: garethgeorge/backrest:latest
    container_name: backrest
    hostname: backrest
    volumes:
    ## 程序数据文件夹
      - ./data:/data
      - ./config:/config
      - ./cache:/cache
      - ./tmp:/tmp
      - ./rclone:/root/.config/rclone # 挂载 rclone 配置(使用 rclone 远程时需要)
    ## 挂载本地备份路径
      - /vol1/1000/media:/vol1/1000/media
      - /volume2/docker:/volume2/docker:ro
    environment:
      - BACKREST_DATA=/data
      - BACKREST_CONFIG=/config/config.json
      - XDG_CACHE_HOME=/cache
      - TMPDIR=/tmp
      - TZ=Asia/Shanghai
    ports:
      - "9898:9898"
    restart: always

有几点需要注意:

  • 为避免后续误操作,建议挂载本地备份路径时直接使用宿主机原始路径
  • 对于只需备份、不需写入的目录,可适当设置只读 ro 权限(如上例 /volume2/docker)。
  • 如果使用远程存储(如 rclone 对接 S3 / COS / B2 等),记得提前在宿主机生成 rclone 配置文件,并挂载到容器内。
  • 端口 9898 用于访问 Backrest Web UI,如果通过反代访问,也可以不映射到宿主机。

设置

访问 IP:9898 启动 Backrest。Backrest 会根据浏览器语言自动切换显示语言,并弹出设置。你需要先为这台机器上启动的 Backrest 服务设置一个唯一的 实例ID,例如 my-nas ,用以区分多个 Backrest 服务。

接着,设置身份验证。如果你的 Backrest 只计划在内网中使用,那么可以不设置用户,并禁用身份验证。但如果是公开的服务,请务必设置好登录用户和高强度密码,并确保未勾选 禁用身份验证 选框,以启用身份验证功能。

鉴于公开服务可能带来的不可预测因素,建议即便是内网,也使用强密码认证身份,并在配置完毕后关闭 Backrest 服务的端口。

仓库

你应当尽可能自行阅读 backrest 官方指南 (英文) 以了解如何配置仓库,或者查看 Restic 文档 (英文) 了解更多有关仓库的信息。不过我也没看过,诶嘿

但本文演示时所使用的 Backrest 1.11.2 版本已原生支持中文界面,因此在实际配置过程中,基本可以做到无需额外查阅文档即可完成操作。

点击左侧的 「添加仓库」 按钮,新建一个 Restic 仓库。该仓库即为备份数据的最终存放位置,后续所有备份任务都可以将其作为统一的备份目的地。

仓库详情

首先,为其指定一个「仓库名称」。该名称仅用于 Backrest 内部识别不同存储库,例如区分本地仓库与云端仓库,本身并不影响实际的存储路径或数据结构。仓库名称创建后无法修改,因此建议在命名时保持清晰与可区分性。

而最关键的 「仓库 URI」,需要严格按照所选存储方式对应的 URI 格式填写。无论是本地路径、S3 对象存储,还是通过 rclone 访问的远程存储,只有格式正确,Backrest 才能成功连接并初始化仓库。

例如:

  • 本地存储库:/vol1/1000/backups/Restic
    使用本地磁盘或 NAS 上的目录作为仓库,适合作为第一层本地备份1
  • S3 / S3 兼容对象存储:s3:my-backup-bucket/Restic
    适用于 AWS S3 以及各类 S3 兼容服务(如 COS、OSS、MinIO 等)。
  • Backblaze B2:b2:my-b2-bucket/Restic
    以 B2 存储桶作为备份仓库,常见于低成本云端备份方案。
  • SFTP:sftp:user@example.com:/data/Restic-repo
    通过 SFTP 将备份数据存放到远程服务器的指定目录。
  • rclone 远程存储:rclone:remote-name:Restic
    通过 rclone 访问远程存储,其中 remote-namerclone config 中定义的远程名称,Restic 为仓库存放的子目录。

「仓库密码」 用于对 Restic 仓库中的所有数据进行加密。Restic 采用端到端加密设计,所有备份内容在写入存储库之前就已经完成加密,云端或远程存储只会保存加密后的数据本身。可以直接使用 Backrest 自动生成一段强度较高的随机密码,通常已满足安全需求。但如果有统一的密码管理策略,也可以自行指定符合习惯的密码。此外,Restic 也支持通过环境变量(如 Restic_PASSWORDRestic_PASSWORD_FILE 等)提供密码,方便在自动化或无交互环境中使用。

需要特别注意的是:该密码无法被找回或重置。一旦密码遗失,即使仓库文件仍然完整存在,也将无法再解密其中的任何数据。因此,应将仓库密码视为与数据本身同等重要,并妥善保存。建议使用密码管理器进行存储。

下方的 「自动解锁」 选项用于在执行清理(forget)和修剪(prune)操作前,自动处理仓库锁定文件。在单实例、单设备使用的情况下通常不会用到,但如果同一个仓库被多个设备或实例同时访问,开启该选项反而可能带来安全隐患,因此默认保持关闭即可。

参数变量

环境变量和命令参数(Environment & Flags)一节,本地仓库或通过 rclone 访问的常见云存储,大多数情况下可以留空。只有在使用对象存储、需要自定义认证方式,或对 Restic 行为有特殊需求时,才需要进行配置。

这里粘贴一段 AI 的见解,不一定准确。你可以在有需要时详尽地向 AI 发问、或阅读官方文档,并在正式将仓库投入使用前大量测试。

环境变量

用于向 Restic 传递环境变量,最常见的用途是:

  • 提供 对象存储的认证信息
    比如 S3、B2、Wasabi、MinIO 等
  • 设置 rclone 的运行环境变量
  • 或者传递一些 Restic 本身支持的高级参数

Backrest 会在执行 Restic 命令时,把这里填写的变量一并注入到运行环境中。常见示例包括:

  • S3 / 兼容对象存储

    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
    • AWS_DEFAULT_REGION
  • Backblaze B2

    • B2_ACCOUNT_ID
    • B2_ACCOUNT_KEY
  • Restic 通用

    • Restic_PASSWORD(仓库密码,不想写在仓库配置里的话)

另外一个比较实用的小细节是,这里 支持引用父进程中的环境变量,例如:

AWS_SECRET_ACCESS_KEY=${MY_AWS_KEY}

这在 Docker / systemd 环境中非常好用,可以把敏感信息只放在宿主环境里,而不是写死在 Backrest 配置中。

命令参数

对应的是 直接追加到 Restic 命令后的 flags,属于更偏高级/进阶的用法。适合以下场景:

  • 调整 Restic 的性能或行为
  • 解决网络不稳定、对象存储兼容性问题
  • 临时启用调试或特殊特性

常见例子有:

  • 限制并发、降低 IO 压力

    --limit-upload 20480
  • 指定缓存目录

    --cache-dir /data/Restic-cache
  • 某些 S3 兼容存储需要关闭 HTTP/2

    --option s3.disable-http2=true

这些参数 不会影响 Backrest 本身,只会在它调用 Restic 时生效。

定期修剪

日常执行备份时,Restic 的工作机制是:

  • backup:只往仓库里追加新数据块,从不删旧数据
  • snapshots:只是这些数据块的「索引」,方便按时间点恢复
  • forget:删除的是快照记录本身,不会立刻删除底层数据

还需配合 prune 来完成最后的空间回收工作。prune 意为修剪、精简,在 Restic 中,prune 用于删除不再使用的数据块。具体流程可以概括为:

  • 扫描整个仓库
  • 找出 不再被任何快照引用 的数据块
  • 删除这些数据块,并在必要时重新打包剩余数据以优化空间利用率

但由于 prune 需要遍历仓库索引,并对大量数据块进行读写、校验和重组,这一过程相对耗时且对存储 I/O 压力较大,尤其是在仓库规模已经不小、或使用对象存储的情况下更是如此。因此,Restic 官方文档并不建议在每次备份完成后都立刻执行 prune,而是推荐将其作为一项低频的维护任务,定期运行即可——例如每周一次,或每月执行一次清理。既能有效回收空间,又不会对日常备份造成额外负担。

Backrest 自然是将 prune 也做成了一个可视化的定时任务。

你只需要在初始化仓库时,完成三件事:

  1. 设置最大未使用比

    对应 Restic 的 --max-unused 参数,单位是百分比。

    顾名思义,这个数字代表允许仓库中未使用内容所占的最大百分比:

    • 数字越小:仓库残余无用数据块越少,但检索过程会更费时间、IO 占用也更高
    • 数字越大:清理动作更温和、更快,但仓库里会保留更多暂时用不到的数据块

    对个人 NAS 来说,保持默认的 10 一般已经足够。

  2. 选择 Schedule Type(何时执行)

    这决定 prune 自动运行的方式,有以下几种:

    • Disabled:禁用自动执行,只在手动点击「运行」时才会清理
    • Interval (Hours):每隔 N 小时跑一次
    • Interval (Days):每隔 N 天跑一次
    • Cron:使用标准 cron 表达式自定义时间点

    对个人 NAS 来说,更推荐选择 Interval (Days),每 7 天运行一次;或选择 Cron,设置每周/每月指定时间段(比如无人使用 NAS 的凌晨)运行。

  3. Cron Expression 与 Reference Clock

    当选择 Cron 时,会出现:

    • Cron Expression(Cron 表达式):

      • 界面里常见的 0 0 1 * *:表示「每月 1 日 0 点 0 分」
      • 想要「每周日凌晨 3 点」可以写 0 3 * * 0

      表达式可以直接询问 AI 获取。

    • Reference Clock(参考时间):

      • Local:按服务器本地时区
      • UTC:按 UTC
      • Last Run Time:多用于「间隔」调度,基于上一次实际运行的时间继续往后算

综合起来,一个相对通用、比较稳妥的配置是:

  • 最大未使用比:10(默认)
  • Schedule Type:Cron
  • Cron Expression:0 3 * * 0(每周日凌晨 3 点自动清理)
  • Reference Clock:Local

这样,Restic 就会在通常不怎么用机器的时候自动执行 prune,对仓库进行「垃圾回收 + 瘦身」。既不影响日常使用,又能长期把磁盘空间控制在一个相对合理的范围内。

数据验证

这一项对应的是 Restic 的 check 操作,用于验证仓库中数据的完整性

  • 验证数据占比
    这里填写的是一个百分比,用来控制每次检查时抽查多少数据

    • 100%:每次检查都会完整扫描并校验整个仓库,最安全,但耗时和带宽开销最大
    • 较小的数值(如 5%10%):只随机抽查一部分数据块,能在较低成本下发现大多数潜在问题

    对于已经稳定运行的仓库,一般不需要每次都全量校验,定期抽查即可;而在刚迁移仓库、存储后端不太可靠(比如部分对象存储)时,可以适当调高比例。

  • 调度方式(Schedule Type)
    可根据需求设置为 Interval 或 Cron。
  • Reference Clock
    用于指定调度时间的参考基准,也同上一环节的使用方法一致,一般保持默认。

高级设置

在大多数使用场景下,这一部分完全可以保持默认。Backrest 已经为常见环境选择了相对保守、稳定的配置,除非你对系统资源调度或自动化流程有明确需求,否则无需在这里折腾。

这里同样粘贴一段 AI 的见解,仅供参考。

高级设置可以为备份任务指定运行时的 IO 与 CPU 优先级,用于控制 Restic 在系统中的「存在感」。

硬件优先级

  • IO 优先级
    决定备份任务在磁盘读写层面的优先程度。
    默认的 IO_DEFAULT 表示不做额外干预,由操作系统自行调度。
  • CPU 优先级
    决定备份任务在 CPU 调度中的权重。
    CPU_DEFAULT 同样表示使用系统默认策略。

在以下场景中,这些选项才可能派上用场:

  • 备份任务与数据库、转码、下载等高 IO 负载服务共存
  • 希望备份「慢一点跑」,但不影响前台服务响应
  • 服务器性能有限,需要人为限制备份任务的资源占用

否则,保持默认,Restic 本身已经足够克制。

钩子

Hooks 用于在备份生命周期的特定阶段执行自定义脚本,例如:

  • 备份开始前 / 结束后执行脚本
  • 备份成功或失败时发送通知
  • 备份前暂停服务,结束后再恢复服务

典型使用场景包括:

  • 备份数据库前执行 dump
  • 备份完成后通过 Telegram / 邮件推送结果
  • prunecheck 前后执行清理或校验操作

对于只想安稳备份数据的用户来说,这一功能并非必需;但如果你有自动化流程需求,Hooks 提供了非常大的扩展空间。

配置示例

以创建一个本地仓库为例。

  • 仓库详情

    • 仓库名称:local
    • 仓库 URL:/vol1/1000/backup/mikusa
    • 密码:********(自动生成强密码)
    • 自动解锁:关闭(不选中选框)
  • Environment & Flags(参数变量)

    • 默认
  • Prune(定期修剪)

    • 最大未使用比:10(默认)
    • Schedule Type:Cron
    • Cron Expression:0 3 * * 0(每周日凌晨 3 点自动清理)
    • Reference Clock:Local
  • Check(数据校验)

    • 验证数据占比:10(视数据重要程度和体积而定)
    • Schedule Type:Cron
    • Cron Expression:0 2 1 * *(每月 1 日凌晨 2 点校验)
    • Reference Clock:Local
  • Advanced(高级设置)

    • 默认

计划

点击左侧的 「添加调度计划」 按钮,新建一个 Restic 计划。必须先配置好仓库,才能新建调度计划。

计划详情

这里需要注意的事项与创建 Restic 仓库时基本一致。例如,计划名称必须唯一,且创建后无法修改,主要用于区分不同的备份任务。此外,虽然可以直接选择已创建的仓库作为备份目标,但单个计划只能指定一个仓库;若需要同时备份至多个目标,则需分别创建多个计划。

不过,一个仓库是可以供多个备份计划使用的。在 Restic 中,多个备份计划共用一个仓库时,去重是以「数据内容」为单位进行的,而不是以「计划」或「路径」为单位。即使多个计划备份了相同的文件,这些文件的数据块在仓库中也只会存储一份。因此,即使不同计划备份了大量重叠数据,仓库体积也不会线性增长。

我一开始忽视了「数据去重」的含义,于是在同一存储库里按路径创建了很多仓库……

备份范围

你可以参考 Restic 文档 (英文) 了解更多信息。

  • 路径

    至少添加一个要备份的路径,可以是目录,也可以是文件。Restic 会把这些路径当作备份入口,在快照里保留原本的目录结构。路径也可以在界面中直接选择,避免手动输入错误。

  • 排除规则

    用来排除不需要备份的路径或文件模式,语法与 Restic 的 --exclude 相同。可以使用排除规则把缓存、临时文件、日志等剔出去,减小仓库体积、提升备份速度。

    例如:

    • *.tmp :排除所有临时文件
    • /home/*/.cache :排除用户缓存目录
    • /var/log :排除大量滚动日志

    这一栏是区分大小写的,在类 Unix 系统中需要特别注意这类问题。

  • 排除规则(不区分大小写)

    与上面类似,但匹配时不区分大小写,对 Windows 等环境更友好。比如填写 *.log 时,会同时匹配 .log, .LOG 等。

备份调度

视数据的重要程度,可手动、按每时/每天、或 Cron 设置备份频率,基本上与创建仓库 prune 时的原理一致,就不再赘述了。

如果没有特别复杂的需求,比较常见的配置是:

  • Schedule Type:Cron
  • Cron Expression:0 2 * * *(每天凌晨 2 点)
  • Reference Clock:Local
但这块的 UI 是不是有点问题?强迫症受不了。

保留策略

保留策略(Retention Policy)对应的是 Restic 的 forget 规则,用于控制旧快照的保留。

可选项有:

  • By Count:按数量保留,比如只保留最近 N 个快照。

  • By Time Period:按时间维度保留,是最常用、也最直观的一种。

    该模式下每个字段的意思是:

    • Hourly:每小时最多保留多少个快照
    • Daily:每天最多保留多少个快照
    • Weekly:每周最多保留多少个快照
    • Monthly:每月最多保留多少个快照
    • Yearly:每年最多保留多少个快照
    • Latest (Count):无论时间如何,始终至少保留最近 N 个快照

    因此,截图中示例的配置意为:

    • 小时级:最近 24 小时内,每个小时保留 1 个快照
    • 天级:最近 7 天内,每天至少保留 1 个快照
    • 周级:最近 4 周内,每周至少保留 1 个快照
    • 月级:最近 3 个月内,每月至少保留 1 个快照
    • 年级:不额外保留按年的快照
    • 最近:不额外强制保留「最近 N 个快照」,完全按上面的时间分组来决定
  • None:不做自动清理,所有快照都保留。适合短期测试,不推荐长期使用。

应该根据数据重要程度、存储空间和恢复需求调整保留策略,例如保留 7 天的日快照 + 6 个月的月快照就已足够日常使用。如果数据本就不是频繁备份,那么也就没必要设置太精细的策略。

高级设置

计划的「高级设置」也同仓库处一样,没有特殊需求的话可以保持默认

备份参数对应 Restic backup 命令的额外参数,用于调整备份行为或性能,例如:

  • --one-file-system:只备份当前文件系统,避免跨挂载点
  • --exclude-larger-than 500M:跳过超大文件
  • --compression max(Restic 0.16+):提高压缩等级,换取更小体积

脚本(Hooks)则可以在备份生命周期的不同阶段执行自定义脚本或通知,支持多种 hook 触发点(如 before-backup, after-backup, on-error 等),具体用法可以参考官方 hook 文档。例如:

  • 备份前暂停某个服务、锁库
  • 备份后重启服务
  • 发送任务状态通知

现版本的 Backrest 已原生支持 TG 通知,不用自行编写脚本。只需在 Hooks 处选择 Telegram、确定需要通知的环节,填入 Bot Token 和 Chat ID,再参考官方模板设置一下通知文本,就能知道备份有没有正常执行了。

我的模板如下:

📦 备份任务通知

任务名称:  .Task }}
执行时间( {{ .FormatTime .CurTime )
事件类型:  .EventName .Event }}
仓库 ID( {{ .Repo.Id )
计划 ID:  .Plan.Id }}

{{ if .Error -}}
❌ 任务执行失败

错误信息(
{{ .Error )

 else -}}
✅ 任务执行成功

{{ if .SnapshotStats -}}
📊 备份统计

• 新增数据( {{ .FormatSizeBytes .SnapshotStats.DataAdded )
• 处理文件数:  .SnapshotStats.TotalFilesProcessed }}
• 处理数据量( {{ .FormatSizeBytes .SnapshotStats.TotalBytesProcessed )
• 执行耗时: {{ printf "%.1f" .SnapshotStats.TotalDuration }} 秒

{{ end -}}
{{ end }}

From Backrest 自动通知

效果大概是这样:

📦 备份任务通知

任务名称: backup for plan "book"
执行时间: 2026-02-18T23:40:39+08:00
事件类型: snapshot end
仓库 ID: baidupan
计划 ID: book

✅ 任务执行成功

📊 备份统计

• 新增数据: 0.000 B
• 处理文件数: 4865
• 处理数据量: 533.452 GB
• 执行耗时: 13.3 秒

From Backrest 自动通知

栗子

让我再举个更实际的例子:使用 openlist 挂载百度网盘,并用 rclone 连接 openlist 提供的 webdav 服务。

先获取个人用户 UID/GID:

# mikusa @ truenas in ~ [11:45:35]
$ id
uid=1000(mikusa) gid=3000(mikusa)

准备 .env 环境变量文件,置于 compose.yml 同级路径,填入基础的公共变量:

PUID=1000
PGID=3000
TZ=Asia/Shanghai

安装 openlist:

services:

  openlist:
    image: openlistteam/openlist:latest
    container_name: openlist
    user: ${PUID}:${PGID}
    ports:
      - 5244:5244
    volumes:
      - ./openlist:/opt/openlist/data
    environment:
      - UMASK=022
      - TZ=${TZ}
    restart: always

启动后,打开 openlist 添加百度网盘存储,设置挂载路径,如 /baidupan ,并填写刷新令牌。

刷新令牌需在官方的 OpenList Token 获取工具 获取,找到「百度网盘 (OAuth2) 验证登录」并勾选「使用 OpenList 提供的参数」,点击「获取 Token」,将底下的刷新令牌粘贴至 openlist 对应位置即可。

确认当前用户具备 Webdav 相应权限。

在终端中连接 NAS,进入 Backrest 容器内部命令环境:

docker exec -it backrest /bin/sh

使用 rclone config 新建 rclone 配置, 添加 WebDav 存储,所需的信息有:

  • name 名称: openlist
  • url 地址:http://openlist:5244/dav
  • vendor 提供商:other
  • 用户名 user:mikusa
  • password 密码:mikusa

具体流程这里不做演示。

创建完成后,rclone 对应映射文件夹内的 rclone.conf 文件应当有如下内容:

[openlist]
type = webdav
url = http://openlist:5244/dav
vendor = other
user = mikusa
pass = mikusamikusamikusamikusamikusamikusa
密码部分是自动加密的,因此会与真实密码看起来不太一样。

使用 rclone ls openlist:// 测试配置是否有误,如有输出即表示正常。例如:

/ # rclone ls openlist://baidupan/mikusa
8753641238 零售机_游戏性能大横评_2026.mp4
842538736 琉璃的宝石/AICL-4796~7.rar
829036840 琉璃的宝石/VVCL-2788~9.rar

接着,打开 backrest 创建 Restic 仓库。

假设网盘挂载路径为/baidupan,计划备份到 mikusa/Backup/truenas 内,那就在仓库 URI 一栏填入:

rclone:openlist://baidupan/mikusa/Backup/truenas

如果是多个计划使用同一个仓库,可以考虑勾选自动解锁,否则修剪任务可能会失败。

保存仓库、测试连接成功后,即可新建计划任务使用这个仓库。

你可以先备份一些小文件进行测试,并尝试恢复云端备份文件到本地。待确定一切正常后,再执行大批量备份。

由于上传的都是些经过加密的文件块,体积不大。实测备份约 2T 数据,暂未遇到上传限制问题(具体情况可能因账户与网络环境而异)。唯一可能需要担心的,便是宽带运营商是否会因持续大量的上传而限速了。

最后

以上便是我关于 Backrest 的全部心得了。不同于 Restic 只是一个纯粹的命令行工具,Backrest 本质上是一个常驻运行的 Web 服务,拥有完整的 HTTP 端口监听、用户认证与任务调度能力。为了读取所有需要备份的目录,它在 Docker 中往往需要较高权限,甚至直接挂载宿主机的大量路径。这意味着,一旦 Backrest 本身存在安全漏洞——无论是认证绕过、未授权访问,还是远程代码执行——攻击者能够触及的范围,便远不止备份数据本身。

当然,这并不意味着 Backrest 不值得使用。对于大多数家庭 NAS 场景而言,只要运行在内网环境中,做好基本的隔离与访问控制,风险通常是可以接受的。若你对安全边界有更严格的要求,那么直接使用 Restic + cron 的纯命令行方案,会是更简洁、也更克制的选择。

参考

本文在摸索时期参考了少数派对「3-2-1 原则」的《不想被勒索软件毁掉数据,就按照「3-2-1 原则」来备份文件》,刘一树的《服务器备份 - backrest + rclone + oss》,试验成功、再加上官方推出了中文界面后,便大量依赖 ChatGPT、Gemini、Doubao 等一系列人工智能的解释了。

因而本文 AI 含量较高,如有顾虑,酌情阅读。


  1. 第一层备份,是「多层备份架构」中的术语。指的是将数据备份到本地或同一网络中的存储设备,作为最基础的备份层级。这一层的核心特点是:备份速度最快、恢复时间最短、成本最低。它通常是备份计划中最频繁执行的一部分,主要应对日常误操作、硬盘故障、系统崩溃等常见风险。尽管第一层备份提供了较高的恢复速度,但并不具备防范物理灾害、勒索病毒或盗窃的能力,因此需要配合其他层级的备份(如异地备份)来确保数据的长期安全。
🔲 ☆

敬,不美好的明天

我真是懒到一定地步了,本来想着年末最后一天写完的总结,竟然拖到了今天!让我看看今儿是几号了……唔,看不太清。总之,这篇文章什么时候发布,那就是什么时候写完的!(:з」∠)

下面开始正文!

先来点开场白

好久没写所谓的「年终总结」了,因为总觉得现在的我并没有什么值得拿出来「总结」一番的东西。每当启动 Typora、新建好 总结.md,准备在键盘上敲点什么的时候,就只会想起自己日渐隆起的啤酒肚、爬满额头的皱纹,以及不得不向年龄屈服的身体。再回过神来,原本打算记录的灵感火花,转瞬就变成了「工作依旧一事无成,学习仍然毫无建树」这句我连着用了好几年的废话,在文档上闪烁着光标。

唉,去他妈的。不就是没什么本事嘛,只要接受了自己的无能,那我就是无敌的!

姑且来看看博客近一年的成果吧。

再看博客近况

今年的博文,内容涵盖滥竽充数的服务器运维、含水量极高的日常记录,以及没有人看的新番点评。

由于几近提笔忘字,为了锻炼不曾有过的写作能力,今年我尝试记录了不少追番报告,但最终看来收效甚微。在反复的字句斟酌中,我的文字水平几乎没有任何提升,我仍旧不擅长这类文章。更糟糕的是,我还变得极度依赖起 AI 来。每写一段,就复制给 AI 检查。

  • 帮我看看有没有语病
  • 帮我查查看有没有逻辑问题
  • 怎么继续往下写,给点 idea
  • 你踏喵不要添油加醋啊 kora!
  • ……

不只是议论文,就连记录、技术相关的文章,甚至是日常水文,我也全部一股脑发送给 AI 勘误。就这样一来一回,我完成了今年的 17 篇文章。

虽然 AI 给的建议都很中肯,但修改之后,多少还是感觉有些丧失了属于自己的语言风格,即便那些句子确实有毛病……来年的话,要尽量多看几部文学作品,多刷一些鱼太的视频,以期在文字方面精进一点。

而落实到数据上,来看看今年的 Umami 报表。今年博客累计收获了 174K 的访客,带来 212K 的访问次数以及 331K 的浏览量。由于 2023 年没写总结,2024 年的 Umami 数据因为我瞎折腾把数据库炸了……所以,本年度访客和访问量,均较 2022 年有所增长。即便跳出率增加了 3%,但平均访问时长也增加了 5 秒,可喜可贺!

这倒也理所应当,因为这些成绩并不是今年的新文章造就的,而是去年、前年、乃至更早些时候的文章带来的访问量。

可见今年的内容,受欢迎程度甚至不及 3 年前。

必须承认,以前写的文章在现在看来也很有趣。我可真是个小天才!但身为作者,还是有点无法接受这一现实的。今年的水文怎么可能一点作用都没有!于是,我又非常不服输地让 AI 根据我的发文量和访问量绘制了张毫无相关性的图片,企图在两者之间找到一丝关联性。

AI真是太好用了
AI真是太好用了

图表出来,答案就非常明显了:博客访问量根本不受现在的我控制。

我更新,或者不更新

访问量就在那里

不增不减

这是不是意味着,即便未来我不再更新,也可以获得稳定的访问量?这就开始咕咕!

好吧,这个结果我其实也预见到了。早在我找到除「刷机」之外新的流量密码后,我就意识到,只要我继续讨论与「NAS」有关的话题,博客就能稳定地收获源源不断的点赞和访客。可现实情况是,我的 NAS 运行稳定,相关的需求已然饱和,我的空闲时间更不足以支撑我继续钻研有趣的工具……

所以,今年在 NAS 方面的文章,就较前两年少了很多。

但今年也久违地刷了次机。虽然只是参考我曾经的教程,可至少重温了刷机的乐趣,继而憋出另一篇《备用机的养成方法》来。也算是勉强用上了流量密码?

甚至选头图的时候看上了 Google Pixel 9,可后来连备用机都吃灰了,这个想法也便作罢。

再来看看谷歌这边。根据谷歌统计的数据显示,今年获得了约 130K 的新用户,约 230K 的浏览次数,与去年相比都呈下降趋势。但临近年末有一撮特别明显的高峰,不晓得是从哪儿来的。

而从 Search Console 的搜索曝光度来看,今年的点击量走势不错。也可以进一步确定,即便今年我没怎么更新,过往文章的质量还是获得了广大网友的肯定。

突然有点飘飘然起来是怎么回事,嘿嘿,嘿嘿嘿……

说罢博客,姑且来看看今年我都做了些啥。

我今年在干嘛

杭州三日游

2025年,在春光乍泄的二月初,为了去未曾谋面的网友家中蹭饭,我来到了杭州。我在下午两点左右到达杭州站,艰难地找到朋友、将行囊放在他的住处后,便直奔计划中的第一站:良渚古城遗址公园。我们原打算用相机记录公园的早春,但由于错估了时间,抵达时已临近闭馆(闭馆六点,但五点半就关门了),禁止入内;天公也不太作美,厚重的云层遮蔽了天空,透不出一丝阳光。我们只好顶着冷风在公园门前闲逛,拍了几张照片,就回去吃饭了。

不要问我为什么喜欢拍树杈,我也不知道,你权当那时的我脑壳有坑。

吃过晚饭,我和朋友商议,明天可千万不能像今天这样空手而归,一定要有所准备。简单讨论之后,决定第二天去动物园拍猴子和长颈鹿,并购入了门票。然后,我们碰上了单双号限行…… 这算是身为十八线小村民缺少的知识,极少进城的我并没有预料到会因为这个被警察蜀黍拦下。好在还有个备用方案。

我有着奇怪的执念,来到一座城市,一定要去这座城市的博物馆看看。最后在我的强烈建议下,我们改道去了最近的浙江省博之江馆区。之江馆可能没有主馆那么有名气,可胜在节假日期间免预约参观,展出的藏品也并非等闲之辈。虽然记不太清当时的心情,但就拍下的照片数量来看,也是度过了一段不错的时光。

之江馆边的图书馆
之江馆边的图书馆

第三天,我发誓再不能像前两天那样莽撞了,既然是旅行,那么基本的规划的还是要有的。于是,我把这次旅程的终点定在了西湖。作为杭州的名片之一,西湖的风景向来声名远扬,三潭映月、断桥残雪、苏堤春晓……既然难得来一次杭州,总归要亲眼见识一番。

我们从住所乘坐地铁直达西湖景区,刚出地铁站时还没什么人影,直到隐约望见湖面,才注意到湖畔早已围满了乌泱泱的人群。我顿时无端联想起「烂怂大雁塔」来——或许对本地市民而言早已司空见惯,但外地游客眼中却依旧新鲜得不行的景点,大多都是这副模样。

今天的天气要比前两天好上许多,万里无云,气温也恰到好处。我们扎进人群里,顺着湖岸慢慢走着,一边欣赏沿途的风景,一边随手拍照。清晨的春风拂过湖面,带着些许水汽,湖水在阳光下泛起细碎的光斑,几对鸳鸯正悠闲地戏水;对岸的山峰层层叠叠、远近错落,被薄薄的春雾勾勒出轮廓。看到这样的景象,我们便停下来多按了几次快门。就这样走走停停,拍拍看看,不知不觉间过去了大半天。

我们绕着西湖走了一圈,基本上把能免费逛的景点都走完了。本想着离开前上雷峰塔眺望一下全景,可人实在太多,只是站在远处看了一眼,便作罢了。

苏州漫步走

俗话说「上有天堂,下有苏杭」。于是在国庆前夕,我去了趟苏州。

原本我是打算去上海市区逛逛的,但一时间又想不出上海究竟有什么非去不可的地方。朋友提议,不如干脆去隔壁的苏州看看。我盘算了一下行程:从住处坐地铁到上海西站大约五十分钟,再从上海西站搭动车到苏州站,只要半小时。算下来花费的时间和去一趟上海市区其实也差不了多少——既然如此,似乎也没有不去一次苏州的理由了。

可这算是场说走就走的旅行,我几乎没有作任何规划,毕竟本来就只是想逃离工位、出门喘口气而已。于是,在十一点抵达苏州站的那一刻,我毫不意外地陷入了「我是谁,我在哪,我要做什么」的窘境。可也不能一直在站台上干愣着,踟蹰片刻,便赶忙跟着人流往外走。我是个实打实的路痴,属于跟着导航坐公交都能坐反的那种,当务之急还是先离开火车站,钻进地铁里再说。

许是国庆调休的缘故,苏州站的地铁里竟没什么乘客。难得能这样不慌不忙地坐着地铁,我掏出手机,向场外观众求助。不少当地群友纷纷献计:苏博、拙政园、观前街、平江路、金地广场、金鸡湖……苏博的门票预约已经排到了国庆,没戏;拙政园倒是不限人次,可以进去转转;观前街和平江路二选一的话,群友们一致推荐后者,据说那是条古色古香的老街,兴许能见识到不一样的韵味;金地广场有不少二次元谷子,我对这种周边没什么兴趣,但倒也能去凑凑二次元的热闹;至于金鸡湖,最适合的便是傍晚时分去看一场初秋的日落。

但还没等我进一步规划,肚子开始表态了,定是赶车起得太早的缘故。这下不用纠结了,先从苏博出发,穿过平江路的古巷,去群友力荐的凤游记面馆来一碗地道的苏式面供奉一下五脏庙,再折回来逛拙政园。

苏博门口除了聚集着来自五湖四海的游客,还有不少兜售不知名小白花环的大爷大妈。我对这花很是好奇,本想凑到跟前看个仔细,愣是没有一个人冲我叫卖。我只好悻悻地继续朝平江路走去。

在经历走错方向来回折返、被路人当成本地人问路后,我终于在十二点前找到了平江路北路口。身为南方人,对教科书上的「江南水乡」多少还是有些刻板印象的:白墙黑瓦、临水而居、垂柳拂岸,这些元素拼凑出人们心目中的江南。可即便是保护得再到位的古街,也很难完全抹去现代生活留下的痕迹。平江路同样如此。但仅仅只是江南的一角,也足够游客打卡留念了。街上随处可见穿各色汉服、旗袍的姑娘倚靠在护栏上拍照,我也是直到这时才懂得群友提到的「古风妹妹」是什么意思。

边走边拍、花了半个小时穿过平江路后,我找到了凤游记面馆,点了一份群友推荐的「焖肉面」套餐,滋溜下肚。又稍微歇息了片刻,便折返回去逛拙政园。

稍微点评下这面。饭前小吃是芋头丸子和一小盏茶,偏甜;面分量很足,但焖肉略油。整体不赖,苏沪一代似乎喜爱甜食,我大概是吃不惯……

返程是不怕迷路了。找到最近的地铁站,再看准「拙政园苏博」方向,一站便到。苏博光是围墙设计就让人眼前一亮,虽是立体的屋檐,黑白纵深却好似平面画。可惜没有提前预约,没法进去游览一番。

拙政园在苏博的另一头,成人门票80元。我对园林设计几乎没有概念,也说不出什么门道,但多少还是能从这些空间的具体用途里一窥园主的用意:赏景。无论是四面厅堂、跨溪廊桥,还是假山、荷花池,都是为了引导人的视线,或将目光送向远处的风景,或让亭台建筑本身成为取景框,通过窗户或门框切割画面,只留下其中最耐看的一处。

From未来的me:你懂个屁的园林,明明是看不懂找个地方蹲着刷了会视频才后知后觉的!

在园里又迷了会儿路,大致逛完已经快四点了,接下来该去见识下二次元了。

花了半个小时乘4号线坐到南门,又用了几分钟走到金地广场。我围着广场外边绕了好几圈,一度怀疑自己是不是来错地方了,毕竟我连个通往二次元的大门都没找着。可迎面走来的几个玩 Cosplay 的年轻人,又明示着这里的确有二次元在活动。兴许跟着他们就能找到入口,我急忙尾随了上去。

果不其然。在找到商场正门后,没几步便看到了各式各样的二次元潮玩店、谷子商铺。商场并不是很热闹,但现在应该是放学时间,随便走走就能看到围在墙角打 UNO、玩累了写两道题的学生。真青春啊,想想自己小时候只能玩泥巴,现在的孩子已经能玩 Cosplay 消磨时间了。不过我对吧唧、立牌这类周边兴趣不大,粗略看了看便离去了。

金鸡湖可以乘地铁直达,出站后朝着苏州文化艺术中心的方向走即可。只是等我走出地表,太阳已临近落山。大概是赶不上看日落了。我爬上地铁头上的小坡,坐在石阶上,望着远方泛黄的天空发了会儿呆。

月光码头就在艺术中心边上,但我已经走了大半天,再绕着金鸡湖徒步一圈是万万不可能了,逛逛月光码头便是我今天的极限。可在码头里又迷了路,我只好在湖岸找了块地吹晚风。湖水拍打着岸边,夹杂着一旁车水马龙的声音,甚是喧嚣。可我却觉得,若不是第二天还要赶回去上班,我可以就这么一直坐着,直到这座城市慢慢安静下来。

等到约好的朋友赶来面基,向他抱怨了工作上的烦心事、吃好饭,跟着他往大裤衩底下的商店逛了逛,便回去了。

国庆两开花

2025年,我去看了人生中的第一场交响音乐会,主题是我非常喜欢的动画《摇曳露营△》。早在看《四月是你的谎言》时,我就希望朝一日能去听一场主题音乐会,却始终没能如愿。如今有机会前往《摇曳露营△》十周年的首场海外交响音乐会,自然不想错过。

我第一时间联系了计划国庆来上海旅游的同学。我告诉他,这次音乐会,キミのね和亚咲花都会亲临舞台献声,立山秋航也会在现场亮相。亚咲花是《摇曳露营△》的老朋友了,キミのね虽然是新面孔,知名度也不及亚咲花,但就声线来看,百分百是辻诗音的马甲。同学虽然对亚咲花无感,但听闻有辻诗音,顿时起了兴趣。于是,他非常给力地帮我抢到票,还速刷了两季动画以便陪我一块看音乐会。可惜经验不足,我们并没有提前候在音乐厅门前,只是打算提前十分钟入场检票。结果便是,我们没有充足的时间,和聚集在门口的一众同好畅聊《摇曳露营△》,也没来得及多拍几张爱好者的痛车和露营道具,便匆匆进场了。

音乐会自然是非常地美妙。当各表演者依次落座,清脆的打火机声响起,伴随着木柴燃烧的爆裂声,一曲《ゆるキャン△のテーマ》为音乐会拉开了序幕。现场各声部发出的声音,确实和耳机里听到的音乐有着天壤之别,我几乎想跟着旋律一块吹起口哨来。但周围的人都正襟危坐,静静聆听。碍于音乐会这种特殊场合的严肃性,我也只能在心里手舞足蹈。甚至都不敢掏出手机拍照。

可随着气氛渐入佳境,情绪被不断推高,邻座的哥们已经按捺不住,开始左右摆手,前排角落的朋友也有备而来,跟随节奏挥舞起了绿色的荧光棒。更有后排的哥们在亚咲花登台的时候,大喊了声「お誕生日おめでとう!」,全场随之躁动起来。这等场合此举成何体统!可我实在忍不住,也跟着喊了一句。

原来今天还是亚咲花的生日,这我确实没有提前调查。

音乐会结束后,我们排队领取签名色纸。按主办方规定,是不允许同歌手合影拍照的,所以我以为只是领了色纸便结束了。可没想到,在领取色纸时,亚咲花不仅热情地向我们「ありがとう」,还在我因社恐领完便转身离去时喊住我,朝我挥手道别。没能向她致以同等的感谢,是我此次音乐会最大的遗憾。

第二天,我和同学逛了 B 站和海洋堂联合举办的 Wonder Festival 手办模型展。由于是 WF 的第二天也是最后一天,再加上入馆稍晚了些,我们放弃了做任务捡小垃圾,改为尽情地拍摄精美的塑料小人。

这些还只是场馆的冰山一角。我们从上午逛到临近闭馆,可能连企业馆的一半都没有走完。更要命的是,在最后的一小时里,我花了三十分钟去排了叔叔的残次手办专销的队,买了一个景品泡面压……于是,我们成功错过了个人展馆。

我们去个人馆的时候,大佬们的摊位已经收了个七七八八,剩下的也基本准备收摊了,打的灯都熄了个精光。个人馆虽然只有一个场馆,但胜在大佬们的 XP 千奇百怪,触手、多目怪,还有一些光是看着就让人掉 SAN 的奇特生物……即便我无法理解,但能在这里(公共场合)见识到这类手办,已经算是一件幸事了。

XP 太怪了,我甚至没敢拍(

唐突地结束吧

今年已经是我在上海的第二个年头了,我仍在日复一日、机械式地重复劳作。这份几乎没有休假的工作,既没有充足的时间让我去见识繁华的国际化大都市,也没有足够的工资支撑我在一线城市消费。好在,经历过去年初来乍到时的种种不适,今年的我至少入门了上班的诀窍:少做、少问、多摸鱼。可偶尔还是太过自觉,招揽了不属于自己的活;也时常和无理取闹的老板拌嘴,给自己徒增烦恼。来年的话,希望自己能灵活变通,不该管的事要当作没看见,好让这个班上起来没那么苦闷。

我也利用起短暂的下班时间,去尽可能做一些与众不同的事。我买了把电吉他,尝试学习喜欢的曲子。我开始走出门去,享受这座城市为每一位二次元爱好者提供的便利。而这些,都离不开 Connorjam 的帮助。是他不厌其烦地将我从无趣的生活中打捞起来,拉着我奔赴一场场次元之约;也是他每天盯着我练琴,不然这把电吉他怕是早就在三天打鱼两天晒网的节奏里落满了灰,即便我现在也只是会弹小星星。

我在初冬的晨光中漫过武康路的街衢,也在巡游的卡车间写下如我们所书的寄语。

我在暮色里仰望过萨姆机甲的炽翼,也在特展的光影里触摸翁法罗斯的余温。

我知道,明年的日子大概率还是会同现在一样循环反复。那些没解决的烦恼、没涨起来的工资、没改掉的毛病,或许还会一次次找上门来。可没关系,我已经学会在这样的日子里,为自己留下一点出口。哪怕只是下班后练几遍不成调的曲子,哪怕只是在人群中确认某些熟悉的热爱依然存在,也足以让我把今天过完。

那么就这样吧,我们明天再见。

🔲 ☆

Debian 13 新机初始化记录

最近购置了一台 1H1G、20G SSD 的虚拟服务器,为了便于使用,需要配置一下基础环境,例如新建用户、密钥登录、Docker 和 Zsh 等等。

年初参考过《Debian Server 初始化设置 SOP》的流程,也整理了一份《飞牛 fnOS 初始化配置记录》,但都不太适合直接套用到这台机子上。部分命令在 Debian 13 上也不再适用。于是,我决定按照自己的需求重新抄写一份,并用这篇笔记为今年的水文画上句号。

抄过来就是我自己的东西了!

系统

本节命令全部使用 root 用户执行。

更新与升级

官方预装的是 Debian 12,系统相当干净,没有多余的东西,因而无需重装系统。既然是新开通的实例,没有任何业务负担,那么可以放心地进行大版本升级。我决定趁现在就把它更新到最新的 Debian13,满足我积攒已久的升级欲望。

首先,升级系统到最新状态:

apt update
apt upgrade -y
apt full-upgrade -y
apt autoremove -y
apt autoclean

接着,将软件源切换到 Debian 13(trixie),使用以下命令一键修改 /etc/apt/sources.list

sed -i 's/bookworm/trixie/g' /etc/apt/sources.list /etc/apt/sources.list.d/*.{list,sources} 2>/dev/null

然后再次升级系统:

apt update
apt upgrade -y
apt full-upgrade -y
apt autoclean
apt autoremove -y

弹出的选框可以全部按默认选择,待更新完毕后,重启系统:

reboot

重启后,验证系统版本:

lsb_release -a

如果显示类似以下信息,说明已经升级成功:

No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 13 (trixie)
Release:        13
Codename:       trixie

本地化配置

设置时区与 NTP:

timedatectl set-timezone Asia/Hong_Kong
timedatectl set-ntp true
timedatectl status

输出类似:

System clock synchronized: yes
NTP service: active

配置语言环境:

sed -i 's/^# *zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen
sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen

输出类似:

Generating locales (this might take a while)...
  en_US.UTF-8... done
  zh_CN.UTF-8... done
  en_US.UTF-8... done
Generation complete.

安装常用工具

安装一些基础工具,涵盖编译环境、系统管理、网络下载、终端增强以及文件处理等我可能用得到但又用不到的功能:

apt install -y build-essential sudo vim curl wget ufw autojump git tmux zsh tree zstd zip unzip lsof fastfetch rsync

新建用户

先设置一个新用户的环境变量,例如 mikusa,用于后续复制粘贴快速执行命令:

USERNAME=mikusa

创建该用户,并将其添加到 sudo 用户组:

useradd -m $USERNAME
usermod -aG sudo $USERNAME

为用户设置本地密码(可选,以防特殊情况需要,密码尽量复杂):

passwd $USERNAME

设置该用户执行 sudo 命令时免密:

echo "$USERNAME ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/$USERNAME-nopasswd > /dev/null
chmod 440 /etc/sudoers.d/$USERNAME-nopasswd

为该用户配置密钥登录,我有现成的公钥,所以这里直接设置一个公钥变量

PUBKEY="ssh-ed25519 AAAABBBBBVVVVVVVVVVVVVVVVVVVV"

创建用户密钥文件夹并授权:

mkdir -p /home/$USERNAME/.ssh
chmod 700 /home/$USERNAME/.ssh

导入现有公钥:

echo "$PUBKEY" | sudo tee /home/$USERNAME/.ssh/authorized_keys > /dev/null

修改权限:

chmod 600 /home/$USERNAME/.ssh/authorized_keys
chown -R $USERNAME:$USERNAME /home/$USERNAME/.ssh

安装 Docker

使用官方命令一键安装:

curl -fsSL https://get.docker.com | bash -s docker

安装完毕后,使用以下命令验证 docker 和 docker compose:

docker info
docker compose version

再将个人用户添加到 docker 用户组:

usermod -aG docker $USERNAME
需用户退出当前终端、重新登录后,docker 组权限才会生效。

用户

切换到 mikusa 用户

su - mikusa

才能继续执行下列个人用户相关的配置。

Zsh 配置

使用脚本一键安装 Oh My Zsh :

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

安装后会询问是否切换 Shell 为 Zsh,按 y 同意。

安装刚需插件:

git clone https://github.com/zsh-users/zsh-autosuggestions.git ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting

修改主题为 ys

sed -i 's/^ZSH_THEME=".*"/ZSH_THEME="ys"/' ~/.zshrc

配置 Zsh,启用插件:

echo ". /usr/share/autojump/autojump.sh" >> ~/.zshrc
sed -i.bak 's/plugins=(\(.*\))/plugins=(\1 autojump zsh-autosuggestions zsh-syntax-highlighting z extract sudo cp aliases docker docker-compose)/' ~/.zshrc

添加一些自用 docker 别名:

cat <<'EOF' >> ~/.zshrc

# Docker aliases
alias dcu="docker compose up -d --remove-orphans"     ## 启动服务并移除多余容器
alias dcd="docker compose down"                        ## 停止并删除服务容器
alias dcp="docker compose pull"                        ## 拉取镜像
alias dps="docker ps"                                   ## 查看运行中的容器
alias dlogs="docker logs --follow -n 15"               ## 查看容器日志,跟随最新 15 条
alias dclean="docker image prune -a"                   ## 清理未使用镜像
alias acme.sh="docker exec acme.sh acme.sh"            ## 在 acme.sh 容器中执行 acme.sh
alias nginx="docker exec nginx nginx"                  ## 在 nginx 容器中执行 nginx 命令
alias caddy-reload="docker exec -w /etc/caddy caddy sh -c 'caddy fmt --overwrite && caddy reload'"  ## 格式化 Caddyfile 并重载 Caddy
alias caddy="docker exec caddy caddy"                  ## 在 Caddy 容器中执行 caddy 命令

EOF

再重载 Zsh:

source ~/.zshrc

其他

可以安装一个 trash-cli 作回收站用:

sudo apt install -y trash-cli

追加别名:

cat <<'EOF' >> ~/.zshrc

# trash-cli aliases
alias rm='trash-put'        ## trash-put 将文件或目录移入回收站
alias rmclean='trash-empty' ## trash-empty 清空回收站
alias rmrest='trash-restore' ## trash-restore 还原回收站中的文件
alias rmlist='trash-list'   ## trash-list 列出回收站中的文件
alias rmrm='trash-rm'       ## trash-rm 删除回收站中的单个文件

EOF

后续使用 rm 命令,便无须担心误操作删除文件了。

最后

使用新用户通过密钥连接上实例,并确认一切正常后,再修改 SSH 配置,避免因误操作而失联

先修改默认的 SSH 端口。设置一个端口变量,例如:

PORT=22033

一键替换当前端口:

sudo sed -i "s/^#\?Port .*/Port $PORT/" /etc/ssh/sshd_config

验证 /etc/ssh/sshd_config 配置是否有误:

sshd -t

没有报错的话,重启 SSH:

systemctl reload sshd

测试是否已成功绑定到新端口:

ss -tlnp | grep ssh

有类似以下输出,即表示修改完毕:

root@Reze:~# ss -tlnp | grep ssh
LISTEN 0      128          0.0.0.0:22033      0.0.0.0:*    users:(("sshd",pid=733,fd=6))
LISTEN 0      128             [::]:22033         [::]:*    users:(("sshd",pid=733,fd=7))

再考虑禁用 root 登录:

sudo sed -i 's/^#\?PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config

也可以禁用密码登录:

sudo sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config

使用以下命令查看是否已成功修改:

sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication'

若输出:

permitrootlogin no
passwordauthentication no

即表示禁止 root 登录,也禁止密码登录生效。

随后重载 SSH:

systemctl reload sshd
使用 reload 而非 restart,可避免因配置错误断开当前连接。

最后,在确认新端口和密钥登录都可用后,再断开当前连接。

参考

🔲 ☆

关于我收集二次元插画这档事

我喜欢收集插图。

小的时候,我会把卡通图画剪下来贴进本子里,做成小手账;再大些有了零花钱,我便攒着买来一本本畅销的画册;等到有了智能手机、接触到广阔的互联网世界,可收藏的东西一下子变得多了起来:从同人插图到概念设定,从商业插画到独立艺术家作品……我乐此不疲地保存这些图片,一张又一张,一个文件夹接着一个文件夹。

这大概是我坚持最久、也最投入的一个爱好。

年轻时的XP
年轻时的XP

可随着时间的推移,收集的作品越来越多,面对电脑中杂乱无章的图库,我才意识到一个问题:该如何为这些收藏进行分类?

分类

相比影音或图书领域有成熟的管理程序和外部数据库可供整理,电子图片往往只是作者在插画网站上的一次更新,或社交平台上的一条动态,本身并没有过于明确的分类依据。我曾考虑使用标签的方式分类,但这么做大概需要一个形似 Yande.re 的图片程序记录标签,并在收集的时候为每张图片打上标签。新增图片尚可如此处理,存量图片就要麻烦得多——以我的技术能力还无法做到自动为每张图片打上标签。而网上搜索到的图片整理软件大多面向专业设计师,对于个人的插画收集而言,又有些大材小用。

况且,在使用 fnOS 相册、immich 这些带 AI 索引功能的相册程序之后,标签可能就没有存在的必要了。想要什么风格(标签)的图片,只需几个形容词简单描述,就可以快速检索到位。

飞牛相册的AI搜索功能
飞牛相册的AI搜索功能

因此,与其折腾复杂的分类系统,不如把精力放在图片本身的保存上。我决定只做最基本的「来源 + 作者」分类:

  1. 从 Pixiv 上下载的图片可以轻松找到作者,因此按作者细分;
  2. Yande、Konachan、Danbooru 等类 Danbooru 图站的图片均为爱好者上传自互联网,可能源自 Pixiv1,也可能是 Twitter,甚至可能是未在网络上公开过的实体画集扫图。没有具体的作者,直接保存在来源文件夹;
  3. Twitter 上右键另存为的图片寻找作者亦较为繁琐,也同 Yande 一样处理。

可是,在准备整理这些图片时,眼前的问题又让我犯了难。

整理

由于没有电脑,最初的收集工作是在手机上完成的,自然没有、也不可能意识到未来会有分类的需求。所有图片都通过 Pixiv APP 保存在固定的文件夹里,再由相册的云服务功能同步到云端。这样做固然方便,更换手机也不怕图片丢失,但缺点同样明显:一旦保存时的命名格式发生变化,就很容易重复保存同一张图片。

例如,Pixiv 官方 APP 当前保存图片的命名格式是 illust_id_save_data.jpg,会生成诸如 illust_112251893_20251123_202020.jpg 这种带没有必要的 illust 前缀和具体下载日期的又臭又长的非常不 Elegant 的文件名;第三方 APP 则可以自行设置不同的命名格式,比如 112251893_p0.jpg 。当同一作品在不同时间、以不同文件名被保存时,就难以仅凭手机相册的时间轴判断是否重复收藏,当时的相册 APP 也缺乏扫描重复图片的功能。

▼ 不同时期的文件命名格式不同,我也是近些年才有所规范。

此外,早期的 Pixiv 还能下载到作者上传的未经去除 Exif 信息的原始图片,且图片格式可能是 jpgpngbmp 2,导致获取的同一作品甚至连文件体积也可能不一致。

所以,我决定先从「去重」这一步入手。针对完全一致的 MD5、相同的文件名,以及相近的文件大小,可以利用重复文件清理工具「Duplicate Cleaner」快速清理掉这部分特征明显的图片。而内容完全一致、只有文件格式或体积有区别的图片,可以在后续通过 immich 的去重功能筛选出来。

利用工具快速去重
利用工具快速去重

移动」方面,由于几乎所有的 Pixiv 图片都包含一串作品 ID,处理起来相对方便。我的思路是利用 PID 拼接链接访问作品页面、解析作者的 UID 后,按 UID 建立作者文件夹移动图片。我把这份工作交给了 AI,编写了脚本批量移动。

▼ 其实最初我是按 UID(NAME) 的格式命名作者文件夹的,但总有作者爱改昵称,于是我又用 AI 糊了个脚本,把文件夹中昵称的部分统统删除了。

至于 PID 异常或是已被删除无法解析链接的作品,可以通过 SauceNao 溯源,再移动到对应文件夹。

最终,仅是 Pixiv 3就整理出了 12172 张(可能)4不重复的图片,这些图片来自 2750 位(可能)5不同的插画师。

总计约30GB
总计约30GB

至此,对存量图片的处理工作算是告一段落。接下来就可以把整理后的图片上传 NAS,并将其挂载到 immich 作为外部图库以便随时浏览和检索。

备份

我的计划是,既然收图的重心正逐渐从手机转移到电脑,而电脑上又有各种现成的工具可以快速从 Pixiv 下载图片(例如:PixivBatchDownloader),那不妨就直接利用这些工具,按自己喜欢的规则生成文件夹和文件名。这样浏览插图时既可以顺手下载,又能通过工具保存下载记录,有效避免了重复下载。

接着,我只需把插图保存在 OneDrive 的同步目录里,就能自动同步到云端,再配置好群晖 DSM 的「CloudSync」套件,就能定时将插图从 OneDrive 拉取更新到 NAS。这样一来,我不仅可以随时在电脑上下载新的插图,还能通过 immich 集中统一管理,可谓一举多得。

唯一的问题是,CloudSync 偶尔会抽风。有时无法及时从云端拉取文件,有时又把我从 immich 中删除的图片同步回来……这些都还算能接受,直到后来出了点小意外。

意外

2025 年初,闲来无事的我准备清理一下 DSM 中无用的数据,决定卸载一个用不到的第三方社群的 Python 套件,并在卸载时并勾选了「清理数据」。万万没想到,这个套件的数据路径竟指向了 NAS 的磁盘根目录,于是它在清理自身残留文件的同时,也顺便删掉了目录下其他的文件。

等我意识到不对劲紧急关机,满满 14T 硬盘的收藏已经被删得只剩下一半。

好在丢的都是些不算特别重要的影视剧和动画,有空再下回来就是。真正让我恼火的是不知何时还会出岔子的 DSM,尽管责任在我,是我自己安装非官方认证的第三方套件导致的。

我本就不太喜欢 DSM,既然事情已经发展到这一步,索性连同 NAS 系统一并更换,试试近期大火的 fnOS。

只是,此时的 fnOS 尚处在测试阶段,还未开发类似 CloudSync 的同步工具,无法直接从 Onedrive 同步文件,原本依赖 OneDrive 的备份方案就这样中道崩殂,插图收藏也因此停滞。我只得另寻其他既能兼顾下载、又能便于同步收藏的方法。

工具

归根结底,我的需求无非就是先获取插图再保存到 NAS,仅此而已。既然终点是 NAS,为何不跳过这些弯弯绕绕,直接一步到位?在互联网上寻找了一番,我决定使用 Nazurin 实现这一思路。

Nazurin 是一个基于 Telegram 的图片收藏工具,支持从 各种网站 浏览和下载图片。在配置好 Telegram bot 和目标 存储源 后,只需给 TG 机器人发送图片源链接,便可直接将图片保存至目的地。

其实前面的内容都是为了引出 Nazurin 的废话,写着写着写跑题了……

使用

在以往的存图流程中,遇到需要保存的图片后,我必须:

浏览 图片 ➡️ 下载 图片 ➡️ 上传 NAS

中间省略了可能需要执行的分类和去重工作。

而在使用 Nazurin、将存储目的地直接设置为 NAS 本地后、并配置好各图片源的存储规则后,这个流程就被简化成了:

浏览 图片 ➡️ 分享(发送) 源链接至 TG 机器人

只需两步,图片便直接被保存到 NAS 中了,可谓一劳永逸!

而在换用 Nazurin 后,我仍可以在 PC 端保持原有的浏览习惯,像平常一样寻找插图,遇到喜欢的图片一键保存。仅仅只是「更换」了一个「下载」按钮:

不仅自动下载了图片,还为这张图片标记了 ❤
不仅自动下载了图片,还为这张图片标记了 ❤

也可以在移动设备上,将图片的源链接分享给 TG 机器人:

这或许相比直接使用 APP 一键保存略微曲折了些,但至少我不用担心图片的分类问题,也不用再烦恼如何把它们备份到 NAS 了。

按来源分类
按来源分类

按作者细分
按作者细分

另外,通过订阅各图站的 RSS 服务,我还可以在繁忙的时候第一时间在 TG 内获取时下热门图片,再择一收入囊中。代价是插图收集也因此失去了些许个性。

安装

虽然官方演示是搭建在 Fly.io 上,但我有 NAS,所以直接部署在本地就好了!

安装 Nazurin 非常简单,使用 docker compose 即可一键部署:

services:
  nazurin:
    image: yyoung01/nazurin:latest
    container_name: nazurin
    user: 1000:1001 # 实测需指定用户运行
    volumes:
      - ./data:/app/data
      - /vol1/1000/photos/Nazurin:/Nazurin # 替换为你的存储路径
    # ports:
    #   - 8080:8080
    environment:
     # ---------- 必填项 ----------
      - TOKEN=123456 # 替换为你的 Telegram 机器人 Token
      - ENV=development
     # - WEBHOOK_URL=http://127.0.0.1:8080
     # - HOST=0.0.0.0
     # - PORT=8080
      - STORAGE=Local,Telegram
      - DATABASE=Local
      - ADMIN_ID=123456 # 替换为你的 Telegram 用户 ID
      - ALBUM_ID=-10012345 # 替换为你的 Telegram 频道 ID
      - STORAGE_DIR=/Nazurin # 需与挂载的内部存储路径一致
     # ---------- 可选项 ----------
      - TZ=Asia/Shanghai
      - IS_PUBLIC=false
      - RETRIES=8
      - MAX_PARALLEL_DOWNLOAD=6
      - HTTP_PROXY=http://mihomo:7890 # 替换为你的代理地址
      - HTTPS_PROXY=http://mihomo:7890 # 替换为你的代理地址
      - CLEANUP_INTERVAL=7
      - FEEDBACK_TYPE=reply
      - BILIBILI_FILE_PATH=Bilibili/{user[mid]}
      - PIXIV_TOKEN=123456 # 替换为你的 Pixiv Token
      - PIXIV_TRANSLATION=zh-CN
      - PIXIV_FILE_PATH=Pixiv/{user[id]}
      - PIXIV_FILE_NAME={filename}
      - TWITTER_AUTH_TOKEN=123456 # 替换为你的 Bearer Token
      - TWITTER_FILE_PATH=Twitter/{user[id_str]}
    restart: always

这里与官方提供的 docker-compose.yml 不同的是,官方指定了一个 env_file 配置,里面包含了完整的环境变量,即便绝大多数都可以保持默认,但数量仍多得吓人。所以我稍微精简了下,仅保留了部分自定义的内容。

在这个配置的环境变量中,可选项自定义的功能如下:

  • 同时存储于本地和 Telegram
  • 私有 bot
  • 最大并行下载数量为 6
  • 使用代理。官方文档只写了需要 HTTP_PROXY ,但其实 HTTPS_PROXY 也是必须的
  • 反馈方式设置为在原消息上添加表情回应
  • 可下载 Pixiv、Twitter、Bilibili 图片至指定作者文件夹

    • Pixiv 下载路径设置为 Pixiv/{user[id]},会在 Pixiv 文件夹内建立 作者 ID 文件夹;文件名格式设置为纯数字 ID
    • Twitter 下载路径设置为 Twitter/{user[id_str]} ,会在 Twitter 文件夹内建立 作者的 用户id 文件夹,例如 1507705239507329034。你可以使用这个链接 https://x.com/intent/user?user_id={user_id} 一键跳转到对应用户,如:https://x.com/intent/user?user_id=1507705239507329034

      • 如果你更喜欢使用 @用户名 文件夹,例如 https://x.com/xinzoruo 里的 xinzoruo ,那么可以使用 {user[screen_name]} 来设置作者文件夹 6
    • Bilibili 下载路径设置为 Bilibili/{user[mid]} ,会在 Bilibili 文件夹内建立作者的 uid 文件夹
  • 临时目录设置为 7 天后自动清理

可以具体参考下节的配置说明。

配置

这里详细说明几处需要额外注意的配置。

  • user: 实测需指定用户运行,懒得找可以直接使用 1000:1001,否则登录 SSH 后使用 id 获取你自己的 id

    $ id
    uid=1000(mikusa) gid=1001(Users) groups=1001(Users),994(docker),1000(Administrators)
  • TOKEN:机器人的 API 密钥,可从 @BotFather 获取
  • ADMIN_ID:管理员用户的 Telegram 用户 ID,可从 @userinfobot 获取
  • ALBUM_ID:用于存储图片的频道 ID,可通过网页 TG Web 获取,格式为 -100xxxxxx
  • Env:如果你需要在网页端快捷分享图片至 TG,需要将运行环境设置为 production ,即 Webhook 模式,并指定挂载端口,才能搭配官方的 Nazurin 浏览器扩展 一键分享。否则,使用 development 模式,让机器人轮询访问 TG 即可。
  • 关于 Webhook 模式,官方的说明是:

    发送到 Telegram 服务器的 Webhook URL,机器人的服务器应能通过此 URL 访问,应以 / 结尾,例如 https://xxx.fly.dev/

    也就是说,这个 URL 需要被公开,且必须能被 Telegram 服务器访问,才能正常使用

    这也意味着如果 Nazurin 是运行在家里的 NAS 上,为了正常使用官方的 浏览器扩展必须将 Nazurin 反代至公网

  • HTTP_PROXY:代理,需要一并填写 HTTPS_PROXY ,否则无法正常下载 Pixiv 图片
  • FEEDBACK_TYPE:收藏图片成功后的反馈方式,可选值如下:

    • reply:回复原消息
    • reaction: 在原消息上添加表情回应
    • both:回复并添加表情回应
      回复原消息
      回复原消息
  • PIXIV_TOKEN:可以使用第三方 APP 快捷获取,例如 Pixez。登录后在账户信息页可以一键导出 Token

如果你没有太多图站需要收集,那么便无需使用完整的 .env 文件,只需在环境变量中填写基本的 TG 配置,外加一条 Pixiv Token,就足够启动了。

当然,如果你想要更精细些的分类、更多的图站支持,或者是其它配置,例如外部数据库、云端存储、自定义其他图站的保存路径,就需要自行详细阅读官方文档后,再额外添加了。

完整的环境变量配置如下,注释部分已使用 AI 进行翻译:

[details sum="点击展开完整环境变量"]

# 这是一个 .env 配置文件示例
# 更多信息请参阅:https://nazurin.readthedocs.io/getting-started/configuration/
# 修改数值并取消相应行的注释后,将文件重命名为 .env

# ---------- 必填项 ----------
# Telegram Bot 的令牌(token)
# TOKEN =

# 运行环境
# production: Webhook 模式;development: 轮询(Polling)模式
ENV = production

# Webhook 地址,例如:https://xxx.fly.dev/,必须以 '/' 结尾
# 使用 Webhook 模式时必填
# WEBHOOK_URL =

# 监听的主机地址,如果使用反向代理请设为 127.0.0.1
# 使用 Webhook 模式时必填
# HOST = 0.0.0.0

# 监听端口,如果部署在 Heroku 或 fly.io 上请注释掉本行
# 使用 Webhook 模式时必填
# PORT =

# 存储类型,用逗号分隔
STORAGE = Local

# 数据库类型
DATABASE = Local

# Telegram 图库频道 ID,可选(官方文档写的是 GALLERY_ID,这是个过时的变量,这里已经更改)
# ALBUM_ID =

# 管理员用户 ID
# ADMIN_ID =

# ---------- 可选项 ----------
# 存储目录路径
# STORAGE_DIR = Pictures

# 是否将此 Bot 设置为公开
# IS_PUBLIC = false

# 如果 IS_PUBLIC 为 True,则以下配置项将被忽略
# 允许的用户 ID(可多个)
# ALLOW_ID =

# 允许的用户名(可多个)
# ALLOW_USERNAME =

# 允许的群组 ID(可多个)
# ALLOW_GROUP =

# 重试次数
# RETRIES = 5

# 请求超时时间
# TIMEOUT = 20

# 下载文件时写入的分块大小(字节)
# DOWNLOAD_CHUNK_SIZE = 4096

# 最大并行下载数量
# MAX_PARALLEL_DOWNLOAD = 5

# 最大并行上传数量
# MAX_PARALLEL_UPLOAD = 5

# 网络请求的代理 URL,默认为系统环境设置
# HTTP_PROXY = http://127.0.0.1:7890

# 在图像说明(caption)中忽略的内容
# CAPTION_IGNORE =

# 临时目录清理间隔(天)
# CLEANUP_INTERVAL = 7

# 日志等级,参考:https://docs.python.org/3/howto/logging.html#logging-levels
# LOG_LEVEL = INFO

# 收藏图片成功后的反馈方式
# FEEDBACK_TYPE = reply

# ----- Google 服务 -----
# Firebase & Google Drive 的 API 凭证
# GOOGLE_APPLICATION_CREDENTIALS =

# ---------- 网站相关 ----------
# ----- Artstation -----
# 文件目录
# ARTSTATION_FILE_PATH = Artstation

# 文件名
# ARTSTATION_FILE_NAME = {title} ({hash_id}) - {filename}

# ----- Bilibili -----
# 文件目录
# BILIBILI_FILE_PATH = Bilibili

# 文件名
# BILIBILI_FILE_NAME = {id_str}_{index} - {user[name]}({user[mid]})

# ----- Bluesky -----
# 文件目录
# BLUESKY_FILE_PATH = Bluesky

# 文件名
# BLUESKY_FILE_NAME = {rkey}_{index} - {user[display_name]}({user[handle]})

# ----- Danbooru -----
# 文件目录
# DANBOORU_FILE_PATH = Danbooru

# 文件名
# DANBOORU_FILE_NAME = {id} - {filename}

# ----- DeviantArt -----
# 文件目录
# DEVIANT_ART_FILE_PATH = DeviantArt

# 文件名
# DEVIANT_ART_FILE_NAME = {title} - {deviationId}

# 下载文件的命名
# DEVIANT_ART_DOWNLOAD_NAME = {title} - {deviationId} - {prettyName}

# ----- Gelbooru -----
# 文件目录
# GELBOORU_FILE_PATH = Gelbooru

# 文件名
# GELBOORU_FILE_NAME = {id}

# ----- Kemono -----
# 文件目录
# KEMONO_FILE_PATH = Kemono

# 文件名
# KEMONO_FILE_NAME = {pretty_name}

# ----- Lofter -----
# 文件目录
# LOFTER_FILE_PATH = Lofter

# 文件名
# LOFTER_FILE_NAME = {id}_{index} - {nickName}({blogName})

# ----- Moebooru -----
# 文件目录
# {site_name} -> Yandere,{site_url} -> 'yande.re'
# MOEBOORU_FILE_PATH = {site_name}

# 文件名
# MOEBOORU_FILE_NAME = {filename}

# ----- Pixiv -----
# Refresh Token
# PIXIV_TOKEN =

# 图片镜像,可选
# PIXIV_MIRROR = i.pximg.net

# 标签翻译,可选
# PIXIV_TRANSLATION =

# 收藏隐私(public/private),可选
# PIXIV_BOOKMARK_PRIVACY = public

# 文件目录
# PIXIV_FILE_PATH = Pixiv

# 文件名
# PIXIV_FILE_NAME = {filename} - {title} - {user[name]}({user[id]})

# ----- Twitter -----
# API 选择,可选
# TWITTER_API = web

# Web API 的 Auth Token,可选
# TWITTER_AUTH_TOKEN =

# 文件目录
# TWITTER_FILE_PATH = Twitter

# 文件名
# TWITTER_FILE_NAME = {id_str}_{index} - {user[name]}({user[id_str]})

# ----- Wallhaven -----
# API Key,可选
# WALLHAVEN_API_KEY =

# 文件目录
# WALLHAVEN_FILE_PATH = Wallhaven

# 文件名
# WALLHAVEN_FILE_NAME = {id}

# ----- Weibo -----
# 文件目录
# WEIBO_FILE_PATH = Weibo

# 文件名
# WEIBO_FILE_NAME = {mid}_{index} - {user[screen_name]}({user[id]})

# ----- Zerochan -----
# 文件目录
# ZEROCHAN_FILE_PATH = Zerochan

# 文件名
# ZEROCHAN_FILE_NAME = {id} - {name}

# ---------- 数据库 ----------
# ----- MongoDB -----
# MONGO_URI = mongodb://localhost:27017/nazurin

# ----- Cloudant -----
# CLOUDANT_USER =
# CLOUDANT_APIKEY =
# CLOUDANT_DB = nazurin

# ---------- 存储 ----------
# ----- Telegram -----
# 图库频道 ID
# ALBUM_ID =

# ----- MEGA -----
# MEGA_USER =
# MEGA_PASS =

# ----- Google Drive -----
# 文件夹 ID
# GD_FOLDER =

# ----- OneDrive -----
# 应用(客户端)ID
# OD_CLIENT =

# Refresh Token
# OD_RF_TOKEN =

# 客户端密钥
# OD_SECRET =

# ----- S3 -----
# 终端节点(Endpoint)
# S3_ENDPOINT = s3.amazonaws.com

# Access Key
# S3_ACCESS_KEY =

# Secret Key
# S3_SECRET_KEY =

# 是否使用 SSL
# S3_SECURE = True

# 区域
# S3_REGION =

# Bucket 名称
# S3_BUCKET = nazurin

[/details]

其他

到这里,这篇文章就算彻底结束了。本来只是想简单写写 Nazurin 的用法,结果在开头卡了很久……于是就写成这样了。

总之,如果你同我一样有收集插图的爱好,且拥有一台 NAS,或是一台小服务器,希望能随时随地地保存那些美好的图片,那么 Nazurin 会是个很不错的小助手。

参考


  1. 如果是源自 Pixiv 的图片,我也会优先从 Pixiv 下载,除非这些图片已经被作者从 Pixiv 上删除。所以 Yande 上下载的图片一般不会出现与 Pixiv 重复的情况。
  2. bmp 格式不确定是不是 Pixiv 自带的,只是我整理时发现有保存这个格式的图片。
  3. Pixiv 以外的图片,由于下载时有注意区分,几乎不用处理。
  4. 毕竟数量巨大,难免会有漏网之鱼,还需未来进一步整理。
  5. 存在有些画师账号被封另起炉灶、或是起了个小号偷偷画涩图的情况。
  6. 理论上,将 Twitter 的作者文件夹设置为 id_str 最为稳妥,这串数字是固定不变且唯一的,但 screen_name 又更加便于搜索,只需与 https://x.com/ 拼接即可。你可以按需求选择设置。赌的就是作者不会轻易更换这个 id
🔲 ☆

2025年7月新番食用报告

对于今年七月新番的阵容与质量,我的心中充满感恩,只有两个字可以形容我的心情:至福(しふく)

上个季度我只看了4部动画。周二晚上追一集《末日后酒店》,周日一觉睡醒看《PA饭》和《mono》,其余的夜晚就……不记得在做什么了。确实是想不起来,应该不会是被路过的JK吸血鬼相中而带着满城市夜游。但七月就不一样了!这个七月无敌豪华的续作阵容,加上厨力爆棚的漫改、轻改新作,长草期?不存在的,从周一到周日,我一口气追了十二部(除了周二实在无从下手)

首先是热门续作。潜水搞笑番《碧蓝之海》第二季回归,依然是那个熟悉的「酒精与大海」配方;王道热血番《胆大党》第二季继续高能,《彻夜之歌》第二季和《更衣人偶坠入爱河》第二季也如约而至;就连扳机社十年前的无节操作品《吊带袜天使》也出了新系列。

新作同样精彩纷呈。《琉璃的宝石》刷新了大家对这类偏科普作品的认知,京阿尼出品的《小城日常》延续了其一贯的高质量风格。甜到发齁的《薰香花朵凛然绽放》和百合后宫番《我怎么可能成为你的恋人,不行不行!(※不是不可能!?)》则极大程度地满足了不同口味的恋爱党需求,屑粉毛 suki!

不过在这些新作中,有两部漫改作品略有争议。虽然它们制作精良,称得上是业界顶尖水平,却在剧情上败絮其中。一部是《章鱼噼的原罪》,另一部是《光死去的夏天》。这次的新番吐槽,就先讲讲这两部。

章鱼噼的原罪

「原罪」是源自基督教神学的宗教术语,指的是「人类自出生起便带有的罪性」。据《圣经》记载,上帝创造了亚当和夏娃,并将他们安置在伊甸园中。园中有一棵「分辨善恶树」,上帝命令他们不可吃其果实,而二人却在蛇的引诱下违背了命令,吃下了禁果。这被视为人类第一次悖逆上帝的行为。于是,二人受到惩罚:必须承受生老病死、劳作痛苦等诅咒。从此,「原罪」进入了人类世界。

在《章鱼噼》的设定中,「Happy 星球」便是伊甸园,「妈妈」则是上帝,「章鱼星人」是上帝创世后未尝禁果的亚当与夏娃,它们纯洁、透明,不带一丝罪意。显然,《章鱼噼》标题所指的「原罪」并不能同上述宗教定义的「原罪」画上等号。来自 Happy 星球的章鱼噼善良、乐于助人、不求回报,只为博得他人的笑容,他并不存在这种与生俱来的罪。

「分辨善恶树」上的果实象征着道德认知、自我意识与自由意志的觉醒,章鱼噼并不拥有这些能力,而这才是章鱼噼的原罪。章鱼噼「无罪」的前提,是必须身处 Happy 星球,只有这样,他才能不因无知而伤人。一旦章鱼噼来到一个与 Happy 星截然不同的社会时,他的行为本身就构成了他的原罪。

因此,他无法理解静香身上的淤青,将真理奈的暴力当作是朋友之间的争吵,面对落在脸上的拳头也不会下意识躲避,甚至是亲临死亡也不会感到恐惧。


他无意中将自己那套简洁纯粹的逻辑强加于这个复杂多变的世界,以为这些问题只需一个 Happy 道具就能轻松解决,而一切烦恼只需要双方最终是 Happy 的就已足够。章鱼噼的一次次尝试,非但没能解决双方的矛盾,还将自己推向了罪恶的深渊。他没有意识到他人痛苦的根源,也没有发现他的善意对别人来说是一种暴力。哪怕经历无数轮回,他仍未能真正理解事件的本质。他落下的眼泪,也不过是因未能完成妈妈赋予的使命,仅此而已。

可这能全怪章鱼噼吗?不能。因为他面对的是连人类自己都可能无法根治的难题——校园霸凌。动画中的霸凌更是出于剧情的需要,被安置在了一个将现实放大了的极端残酷荒诞的世界,一个家长、学校乃至社会都对此无动于衷的环境。如果说东直树和真理奈受到的是源自家庭的暴力而难以被发现还情有可原,但是对静香在课堂上遭受到的最直接的暴力都视而不见,这种没有任何外部力量干预的霸凌,如此不切实际的背景,在现实生活中几乎不可能存在。

也就是说,作者根本就没打算往一个能彻底解决三个家庭矛盾的方向叙事,自然也就无法指望一个不谙世事的外星章鱼能在处理这些事上起到多大的作用。

所以故事的最后,章鱼噼付出了自己的生命,最后使用了一次 Happy 道具,让时间回到照相的前一刻,创造出了一条继「真理奈线」和「静香线」外,章鱼噼不存在的世界线。这条世界线的真理奈和静香依旧没能摆脱双方既有的命运,却因章鱼噼曾在另外两条线出现过,不经意间影响到了这条线的二人,从而团结在一起互相对抗命运的不公……即便我确实心有不甘,也只能默默接受这个结果。

我编不下去了,越想越气。太烂了这个结局,不会写就不要写

光死去的夏天

《光死去的夏天》初看有很重的 BL 元素,这劝退了很多路人观众。其实随着剧情的发展,两位主角之间的情感实际上更接近兄弟情,中后期则像是父子关系。但动画在二人情感的刻画上、尤其是非人的「光」对佳纪谜一般的感情的深度描写,在故事前期有大量篇幅都着墨于此,导致这部动画实际上看起来就是很……BL。

不过,BL 并不影响我观看,这充其量算是引人注目的噱头,真正吸引我的是 CygamesPictures 高规格的制作水准。光是极其华丽且诡异的片头曲画面就已经值得我驻步,更不用说全片精心构筑的演出与光影,在贴合故事氛围时带来的视觉冲击。这让我得以将更多注意力集中在动画对「光」的由来、神秘的「取脑大人」以及乡村灵异怪谈这一系列故事主线的铺陈。

可真正开始往主线的方向叙事,已经是第 7 话的事了。渐渐地,那些原本用于营造恐怖氛围的光影和镜头设计,还没来得及给我带来预期的惊悚感,就随着第一季的结束被我遗忘了。反倒是动画对小镇夏日的描绘更让我印象深刻——聒噪的蝉鸣、蔚蓝的天空和远处绿色的山峰……我一定是牛马牛出问题了才会怀念这个。这或许暴露了动画本身可能存在的问题:悬疑与情感两条主线太过割裂。

这种割裂感,很大程度源自动画与原作连载方式的差异。漫画自 2021 年 8 月连载至今不过 31 话,更新周期缓慢,「光」身上的悬念与过去被当作长期伏笔埋在叙事深处,在月更节奏下反而不显得突兀。但改成动画后,这条悬疑线被前置为阶段性矛盾,却没有迎来匹配的阶段性解法,在短短 12 集的体量中,它既未推动剧情,又压住了情感线的自然生长,于是观感上的断层就格外明显。如果是 24 集的半年番,节奏会更从容,情感与悬疑能在相对合理的节点上交替发力,高潮也能在合适的时机爆发。但当故事在 12 集刚展开便戛然而止,原本应逐步累积的张力来不及释放,体验自然大打折扣。

等到我看过漫画再回过头看动画,这一问题更为突出。因为即便抽离那些悬疑背景,主角二人的心理变化与关系推进依然成立——动画真正的核心始终在于探讨类似「沼泽人」的身份同一性问题:

若一个在外貌、记忆、行为上皆与你熟悉之人无异的存在出现在面前,那你所认定的那个人仍然成立吗?当认知依附于记忆,情感依附于回忆,而记忆与回忆都已被复制、再现、模拟,那自我究竟指向肉体、意识,还是他者眼中被构造的形象?

以至于「光」究竟是谁,似乎并没有那么重要。

好在官方已经宣布制作第二季,届时将两季连起来看,体验应该会有所改善。只是对于愿意追番的观众来说,这漫长的等待未免有些煎熬。

🔲 ☆

2025年4月春季新番吐槽

一月看得有些杂了,我决定静下心来,快快乐乐地看些日常番。

今年四月对我来说算是近几年比较差的,因为在这个多达 66 部番剧的春季档里,我只看完了两部原创动画、一部漫改动画和一部泡面,总计4部新番。《末日后酒店》、《时光流逝,饭菜依旧美味》均属本赛季十分亮眼的黑马,《mono女孩》稍显逊色但也不容小觑,《莉可丽丝:友谊是时间的窃贼》则是专属于粉丝的福利番外,短小精悍。

若是把两部看了一半的半年番《魔女与使魔》、《Summer Pockets》,也算上的话,四月总共是看了6部新番。

你可能会有疑问,「那不是还有《Re:从零开始的异世界生活 第三季 反击篇》这部强势续作吗?」诚然如此,这个四月还有口碑好转的《小市民系列 第二季》、庵野秀明的老朋友鹤卷和哉整的新活《机动战士高达 GQuuuuuuX》以及赛马娘新作《赛马娘 芦毛灰姑娘》,都是不可忽视的存在。可惜我连前作都没看过/完,新/续作肯定无法一拍脑门就囫囵吞枣看下去的。因此,这些已经是我所能选择的极限了。

没片看也没其他办法,说不定七月就有一堆新番等着我呢?

末日后酒店

丛生的树枝从写字楼顶层钻出,枝叶顺着破碎的玻璃幕墙垂挂而下,藤蔓悄无声息地蔓延开来,把街道织成了一座绿色的迷宫。曾经人头攒动的商业街,如今只剩下塌陷的人行道、倒伏的广告牌和被锈蚀封死的店铺大门。阳光洒在褪色的招牌上,不知何处传来几声清脆的鸟鸣,显得格外寂静。

不知名病毒肆虐全球,人类相继逃往外太空,地球文明在顷刻间停止了运转。然而,街口尽头的银河楼酒店仿佛与这一切无关。门前的地面干净如常,墙面光滑整洁,楼顶的钟塔正有条不紊地向前迈进着每一格,仿佛末日未曾来临。酒店沉重的金属门缓缓开启,礼宾机器人如往昔般迎接崭新的一天。只是下一秒,它的机体便猛地一震,随即轰然倒地。蒸汽从关节缝隙中喷出,白色的烟雾在空气中弥散。伴随着低回的爵士乐,《末日后酒店》的故事就此拉开了序幕。

初见「末日后酒店」这个片名,我以为会是个类似《少女终末旅行》那样「苦中作乐」的悲剧故事,或如《末日列车去哪里?》般满载充满脑洞的异想天开。当一群机器人只是为了等候人类回归,而苦苦维护着一栋没有客人的酒店长达数百年,这怎么看都不会是一件快乐的事。谁料,这部作品却走上了《太空丹迪》式的荒诞之路。

既然酒店的存在是为了服务顾客,老板也未曾指定顾客必须得是地球人,那么在这个没有人类存在的世界,为了遵守向老板许下的承诺,服务外星人自然也是理所当然。我们因此得以见到形形色色的外星生物,以及落户地球、陪伴孤独的八千代度过漫长年月的狸猫星人家族。在他们的帮助下,酒店渐渐恢复了往日的生机。不仅修缮了基础设施,开创了特色料理,还陆续增设了温泉、酿酒工坊,顾客越来越多。在星际间打出名声后,甚至建立起面向全地球的防御系统——这些都在不经意间同酒店的运营宗旨相契合。

很难说《末日后酒店》是否正是从这「十律」出发,才延展出如此多姿的故事线的。

与「十律」环环相扣的标题
与「十律」环环相扣的标题

而作为一台依据程序设定运行的机器人,八千代理应按部就班地执行命令,履行代经理的代理的义务,向每位顾客致以最诚挚的笑容。但当她因不可抗力向狸猫星人一家施以正义的铁拳,触发了老板留在她体内的第一个彩蛋之后,她的轨迹便开始偏离原定的路线。从喝酒体验「大人的感觉」而获得性感身材、到被不可名状的心情影响得热水盈口;从意外迷失在外太空,到重返地球发现自己派不上用场,迎来青春期和叛逆期……酒店在成长,八千代亦在进化,她逐渐获得了那些原本只属于人类的「七情六欲」。

于是,当被迫休假的八千代第一次以一位普通的旅客的身份漫步在这座无人的城市,将目光从酒店从转移到这个荒芜的世界,时间的痕迹开始显现。散发着热气的温泉澡堂、金黄色的麦田、高耸的火箭发射场、郁郁葱葱的沙漠绿洲、爬满藤蔓的残垣断壁……看着眼前的这一切,她发出来自内心深处的感慨:「体验到了活着的感觉」。

这一刻,八千代俨然成了这个星球上全新的「人类」。

只可惜,偏向单元剧的结构让人几乎忘记《末日后酒店》终有落幕的一天。可它还是如钟塔那样准时,在第 12 集画下句点——伴随着人类的归来与离去。好在经历了这一切风风雨雨,八千代早已不再是那台只会机械微笑的机器人。她学会了思考和犹豫,理解了愤怒与感动,也懂得了「等待」并不只是程序设定的循环,而是一种源于心底的期盼。或许人类终究不会再回来,但她依然会在这栋酒店中等待着下一位旅客的到来。

时光流逝,饭菜依旧美味

P.A.WORKS 这些年一直在制作一些以少女为主的原创动画,但都收效甚微。例如 2017 年的《樱花任务》、2021年的《白沙的水族馆》,评价都较为一般。可能是因为把握不住半年番的时间跨度(这俩我都没看过,这段话是我瞎说的)。去年的《亦叶亦花》在 Bangumi 上只有凄惨的 4.95分,不过同期的《深夜重拳》还算说得过去,拿下了 7.34 分,可题材特殊,受众面太窄,只能沦为冷门佳作。本季的《PA 饭》1不仅讨论热度高,在日常氛围的营造上更是力压隔壁《mono 女孩》一筹,评分高达惊人的 7.7 分,仅次于《末日后酒店》。

这个成绩倒并不让人惊讶。PA 本来就是日常系动画的好手,只是最近做得烂了些,并不代表人家做不出来好看的动画。《PA 饭》和《mono 女孩》比,也就是制作精度差了点,人物崩坏现象频发了些。在高完成度的故事面前,这些都是瑕不掩瑜的小问题。毕竟《PA 饭》对「吃饭」与「生活」的结合与协调处理得恰到好处,故事节奏和剧情推动也十分舒适,加之各色的角色形象以及丰富的日常互动,构建出的一系列平淡却充满惊喜的放学后生活。这些都是大家有目共睹的。

以上图片来自 维基萌 。各位若有兴趣,不妨前往大佬博客阅览关于本作的圣地巡礼:《圣地巡礼之高尾与西八王子》、《吃动画中的巨无霸汉堡》、《联动女仆咖啡》。

更重要的是,《PA 饭》并非将重心全部都放在做饭、吃饭或是日常唠嗑上,而是借由「饭菜」这一载体,将五位角色紧密地联系在一起。例如第二集,面对空有名头的「饮食文化研究部」感到心灰意冷的真子,在和大活一块打扫活动室、指导员突袭检查活动状况后,萌生出想要做饭的念头,在同伴们的协作下完成一餐,继而收获以往独自一人所没有的喜悦;第三集相约爬山,与相识不久的美虹相遇聊天变得熟络,与大家一起登上梦寐以求的山顶,饱餐后欣赏与所不同的风景;第四集星奈奈的加入虽然充满尴尬的空气,但只一顿饭后,真子与她渐渐成为了无话不谈的好伙伴……

就这样,《PA 饭》每一集都在上演着值得回忆的美妙场景。正如同标题写的那样,重要的从来都不是饭菜,而是大家互相陪伴下缓缓流逝的时光。

mono女孩

《mono 女孩》是《摇曳露营》作者 あfろ 的另一力作,与《摇曳露营》属同一世界观。

只是,在因《摇曳露营》第三季大换血导致原本的空气感消失得不能说是一干二净也得是所剩无几的这个大前提下,接过「神之动画」接力棒的重担就落在了师出同门的《mono》身上。可谁知一季播毕,《摇曳露营》一转身,背后压根没人!再定睛一看,原来《mono》气喘吁吁地跑在另一条赛道。诶不是,你怎么跑得比《PA 饭》还慢?

说笑归说笑,但《mono》确实是远不及《摇曳露营》,甚至还有些落后《PA 饭》的。虽然动画在制作规格上诚意满满,但是对于「日常系动画」十分重要的氛围把控与立意表达上,制作组还是略显青涩。以至于观众看不明白《mono》想要讲述的究竟是一个怎样的故事,是美少女JK聚在一起玩摄影,还是春乃老师 2 和漫画家同伴们借取材之名公费旅游,还是大家闲暇时一起满地图闲逛打卡,还是三者皆有之的日常大杂烩。

摄影也好旅游也罢,亦或是网红景点打卡,我对《mono》的这一套连招并不反感。身经百战的我已经可以主动从夹角和缝隙中摄取美少女养分,再配合少女们纯真的笑容抚慰我疲惫的心灵。我无法忍受的是故事后半段频频出现的乡村灵异怪谈,即便它确实有趣。这段故事和先前轻松的日常相比,主角直接从三小只变成了漫画家社畜不说,原本溢出屏幕的青春还变得只剩下工作饭后的杂谈,有着非常明显的割裂感,实在是让人难以接受。

《mono》最终呈现的效果更像是 あfろ 老师在画《摇曳露营》时四处取材而积攒的素材库,并不只有摄影,而是镜头下包揽世间的万物(もの),这可能才是作品真正的立意。如果大家更倾向《mono》会和《摇曳露营》一样,单纯地借「摄影」为由铺展开来一段段充满趣味与烟火气息的故事,那为何不直接再看一遍《摇曳露营》呢?

莉可丽丝:友谊是时间的窃贼

石蒜今年推出的三分钟的日常番外篇泡面——完全不够看啊!求求你再让我看一集吧,我什么都愿意做的!

要我说第二季干脆别惦记你那烂怂电波塔了,多写点美少女贴贴千束和泷奈的打情骂俏日常,然后在欢声笑语中再次迎来《花之塔》的吉他声响起。你开心,我开心,大家都开心,多么美妙的一件事!

最后

OK,以上就是今年份的4月新番吐槽。虽然只有三部,还磕磕绊绊憋到了10月才写完,但写出来了总归是好的!这三部动画都是本季新番中的精品,如果你有时间亦有兴趣,不妨将这几部动画全部看完,说不定会收获和我完全不一样的体验。

还请期待下次的新番吐槽~


  1. 既然是和做饭有关的动画,又是由 P.A.WORKS 制作,联想到去年的《迷宫饭》,这部《时光流逝,饭菜依旧美味》就被简称为「PA 饭」。
  2. あfろ在剧中的化身
🔲 ☆

《东岛丹三郎想成为假面骑士》End Card 及作者介绍

《东岛丹三郎想成为假面骑士》是由 柴田ヨクサル 原作、池添隆博 导演、ライデンフィルム(LIDENFILMS) 制作的 TV 动画,于今年10月播出。故事讲述从小到大一直仰慕假面骑士的男主东岛丹三郎,因无法成为真正的假面骑士而郁郁不得志。在年过40准备放弃这一梦想时,却遇到了梦寐以求的敌人,借「修卡」名义打砸祭典摊贩的小混混。挺身而出的他,人生轨迹就此发生转变……

这个十月实在是没什么看头,续作不够看,异世界烂片又满天飞,无奈只能试试这部特摄题材的漫改。不过,本作不一定能继续维持现阶段的评价(截止2025年10月14日,BGM 上的评分为 7.39),原因有三。其一,原作漫画画风、画工均一言难尽,故事编排也非常糟糕;其二,制作公司 LIDENFILMS 的制作水平忽高忽低,动画质量完全看监督人脉及其导演水平;其三,本作导演池添隆博的履历看起来比较一般,因此,对接下来的走势需持保守态度。

但监督深耕机甲类番剧数十载,就前两集的观感来看,第一集燃得莫名其妙,第二集也还不错。说不定子供向动画的制作经验,用在这部面向青少年乃至成人的动画上,能因此发生其妙的化学反应,继而让原作改头换面,也不无可能。

第一话 池添隆博

本作监督。池添隆博 为原画师出身,后来也绘制分镜,曾在《钢之炼金术师 FULLMETAL ALCHEMIST》中担任OP・ED 的演出和分镜。于 2009 年任监督工作,导演第一部作品《 阿拉德战记 Slap-up Party》,近些年涉猎的题材则主要集中在子供向机甲类动画上。

第二话 マスクド

マスクド 是位专攻假面骑士、奥特曼等特摄题材的插画师,可以前往他的 Pixiv 主页 欣赏他的画作。我本以为没有关注过他,却意外发现以前点赞过一张平成年间历代假面骑士的插图

他本人也有在 X 上发布这张插画的动态。

本话主要以电波人塔克尔为主,具体角色介绍可以参见B站:https://www.bilibili.com/video/BV1m84y1L777/

第三话 沢 真

沢 真的个人信息在中文网络上基本为零,著有漫画《镜像杀手HITS》,但是目前暂停连载;正在连载的漫画《となりのダイダイ》,能搜索到的中文信息很少……

本话结束卡推特动态:https://x.com/sawa_makoto/status/1979595943805935634

第四话 安國一将

安國一将 是位插画师,绘画风格偏美漫,为宝可梦、数码宝贝、奥特曼、高达等卡牌游戏绘制插图。

本话结束卡推特动态:https://x.com/Kazumasa_yskn/status/1982114428511240413

第五话 K-SuKe

K-SuKe 是一名插画师,为一些特摄作品设计角色和怪物。

本话结束卡推特动态:https://x.com/KSuKe37957985/status/1984651490603254069

第六话 横井孝二

横井孝二闻名于《SD 高达》的角色设计。

本话结束卡推特动态:https://x.com/nisegundam/status/1987194676374610335

🔲 ☆

个性化 Typecho 后台

Typecho 的管理后台相当简洁,整体以淡雅的灰白色为主色调。没有花哨的图表,也没有多余的视觉干扰,三栏式的布局关键功能、信息层次分明,使用起来非常高效。

但是,官方博客部署在国外,并使用了 Cloudflare 的 CDN。如果一时访问不了,最右边的「官方最新日志」在进入后台的时候就会卡顿一下,非常影响加载速度。

为了保证稳定,一般我们可能好几年都不会更新一次 Typecho 主程序;再者,官方正式版本迭代速度非常缓慢,这个日志也没什么大用。所以,可以考虑直接干掉这块内容,这样访问后台的时候能稍微提提速。

首先,移除「官方最新日志」栏目。打开 admin/index.php 文件,找到并删除以下代码:

<div class="col-mb-12 col-tb-4" role="complementary">
    <section class="latest-link">
        <h3><?php _e('官方最新日志'); ?></h3>
        <div id="typecho-message">
            <ul>
                <li><?php _e('读取中...'); ?></li>
            </ul>
        </div>
    </section>
</div>

接着移除获取官方日志的 JavaScript 代码。在文件末尾的 <script> 标签中,找到并删除以下代码:

if (!!html) {
    ul.html(html);
} else {
    html = '';
    $.get('<?php $options->index('/action/ajax?do=feed'); ?>', function (o) {
        for (var i = 0; i < o.length; i++) {
            var item = o[i];
            html += '<li><span>' + item.date + '</span> <a href="' + item.link + '" target="_blank">' + item.title
                + '</a></li>';
        }

        ul.html(html);
        cache.setItem('feed', html);
    }, 'json');
}

刷新一下,后台就变成了这样:

但这么一删,原本布局就显得有些不对称了。我倒是有个点子,把Z酱的动态塞到这边上,这样就可以及时注意到更新,然后第一时间转载,机智如我!可以找到「最近发布的文章」栏目的代码块,将 col-tb-4 修改为 col-tb-6

修改前:

<div class="col-mb-12 col-tb-4" role="complementary">
    <section class="latest-link">
        <h3><?php _e('最近发布的文章'); ?></h3>

修改后:

<div class="col-mb-12 col-tb-6" role="complementary">
    <section class="latest-link">
        <h3><?php _e('最近发布的文章'); ?></h3>

「最近得到的回复」栏目的代码块也同样如法炮制,将 col-tb-4 修改为 col-tb-6 即可。整体就会看起来稍微居中一些,就像这样:

至于获取 Typecho 官方博客 RSS 内容的代码,既然不会被引用到就不去动它了。

本来这篇水文到这里就该结束了。但我灵机一动,要不底下垫张壁纸试试?于是就变成了这样:

方法也很简单,塞个样式就行。在 admin/header.php 文件中 </head> 前加入以下代码,效果是会在所有后台背景页面都显示这张壁纸:

<style>
    body {
        /* 1. 在最底层设置背景图 */
        background-image: url('https://your-image-url.com/background.jpg'); /* 替换为你的背景图片 URL */
        background-position: center;
        background-size: cover;
        background-repeat: no-repeat;
        background-attachment: fixed;
        position: relative;
        z-index: 0;
    }

    /* 2. 在背景图上、内容下,加一层半透明白色遮罩,让图片“变淡” */
    body::before {
        content: '';
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(255, 255, 255, 0.9); /* 90%不透明的白色,可调整 0.9 改变图片清晰度 */
        z-index: -1; /* 把这层放在所有内容的后面 */
    }

    /* 3. 把原来包裹内容的灰色背景去掉,好让我们的淡图片背景透出来 */
    .typecho-body {
        background: transparent !important;
    }
</style>
以上代码由 Gemini 提供。

就是太二次元了,一点也不习惯。原本为了加速访问删除的日志,反倒因此变得更卡了,有点本末倒置。用了几天就删掉了(

🔲 ☆

《琉璃的宝石》Eye Catch

《琉璃的宝石》是由渋谷圭一郎原作的 TV 动画,讲述女子高中生谷川琉璃与主修矿物学的女研究生荒砥凪等人在寻找矿石的同时,探究矿石背后所蕴含的历史与文化的故事。

剧中用于分割前后半部分的「凪の小憩(凪的小憩)」环节,取自原作漫画每话结束的科普。这种画面在日本电视动画中有专门指代的术语:Eye Catch(アイキャッチ)

アイキャッチ 是指插入正片前后半部分交界、用于广告时段的短暂画面或影像,其作用不仅在于提示观众「这里进入或结束广告」,还常常通过作品标题、标志性元素、角色互动或资料卡等形式强化作品辨识度与氛围。有些作品甚至借此补充设定信息(例如《进击的巨人》的「現在公開可能な情報」)。因此它既是一种实用的分割提示,也逐渐成为动画演出风格和作品特色的一部分。

而本作的 Eye Catch 正是起到知识点汇总和补充的作用,因此我将这部分内容摘抄了出来,供有需要的朋友使用。

本文图片均截取自喵萌奶茶屋字幕组翻译的画面。

第一话

有一种矿物名为石英,它在世间被人们亲切地称为水晶。在英语中,水晶被称为 Rock Crystal,石英则称为 Quartz,这些都是它比较常见的名称。

石英的化学成分为二氧化硅。由于其组成元素是构成地壳元素排行榜第1位的氧和第2位的硅,所以可以说石英是最常见的矿物之一。

石英是由岩浆或受其热量影响的热液冷却时结晶形成的矿物,因此,在有火山分布的地域附近,通常都能找到它。一提到水晶,人们可能认为只有在特殊地带才能开采到,但在作为火山大国的日本,无论在哪个地区都有可能采到。

顺带一提,构成地壳的元素排名依次为:氧、硅、铝、铁、钙、钠、钾、镁。

Vol.01
Vol.01

石榴石除了作为宝石,也因其硬度一直以来被用作研磨剂。

现在市面上销售的棕色砂纸,如果背面标有“G”,即表示「研磨剂为石榴石」。仔细观察其表面,会发现含有颜色不同的杂质,这正是天然石榴石的证明。

直到近年,日本的砂纸都还使用国产石榴石。群马县产的石榴石中含有黑色角闪石等杂质,奈良县产的则偶尔会含有蓝宝石。这大概是因为密度接近而无法完全分离的缘故。

即便是普通的砂纸,换个角度看,也能从中窥见产地的特性。大家下次去家居卖场时,不妨看看去砂纸区看看。

Vol.02
Vol.02

第二话

人们往往认为具有相同成分的矿物,其结晶形状也应该是相同的。但事实上,由于其形成条件的不同,结晶形状也有所差异。

石墨(铅笔芯)和钻石都是由纯碳构成的矿物,但由于温度和压力的不同,它们的外观和性质也截然不同。由此深入观察,便能看到更加精彩的结晶世界。以黄铁矿为例,其代表性的晶体形状有立方体、八面体、五角十二面体这三种。

但实际上,在这三种中,根据条件不同,还存在所谓「过渡形态」的晶体。它们成分相同,但因结晶形成时条件的不同才产生了形状上的差异。

有很多成分相同但结晶形态不同的矿物,不难想象沉迷其中的收藏家们要耗费多大的精力。

Vol.03
Vol.03

人们常认为黄金只能在特定场所开采,但事实并非如此。作为火山大国的日本矿产资源丰富,以前全日本都能采到黄金。如今虽已无开采痕迹,但留下了许多小规模开采记录,在其周边河川中也能淘到砂金。

既然全日本遍布小型金矿,自然也有不少「虽能采金但因产量不具经济价值而从未正式开采」的小型矿点。这些地点因完全未留记录,也不曾公开说明能淘金,但既然未被开采,至今仍可能有砂金流入附近河流中。

说不定各位家附近的山里也能淘到砂金。不过产量也顶多达到「拥有专业淘金技术的人努力一天才可能发现一粒]的程度。若非过分执着,还是建议去知名采金地尝试。

Vol.04
Vol.04

第三话

大学研究室里每位成员往往都会被分配一张作为工位的书桌。

在研究室待得时间越久,个人工位越为个性化,使其状态变为使用者理想的样子。

建立在精妙平衡上的空间里根本不存在无用之物。无论别人如何议论,没有就是没有。

Vol.05
Vol.05

某些矿物在受到紫外线照射时会发出光芒。这种发光现象最初在萤石中发现,故被命名为「荧光」。但萤石的发光现象不止于此。当矿物受热时,还会产生一种名为「热释光」的现象。

想安全观察到热释光,需要将萤石放入试管后,再用火加热试管。为使现象清晰可见,需要保持房间光线较暗。

随着加热持续,萤石会发出声响进裂,此时可观测到比萤石荧光更显青白的光芒。不过通过加热仅能看到一次热释光。

萤石价格低廉,矿物专卖店大多有卖。欢迎大家买来尝试。

Vol.06
Vol.06

第四话

蓝宝石是非常有名的宝石,作为矿物的名称是刚玉。构成刚玉的元素是氧和铝,并不包含特别稀少的元素。漂亮的蓝宝石只在国外产,但如果不看质量,日本也有几处产地。

大分县的木浦矿山曾经是日本商业开采刚玉的唯一矿山。但木浦矿山开采的基本不具有宝石质量,仅用于工业。

刚玉具有仅次于金刚石的硬度,并具有很高的耐火性和磨耗性,因而被用于研磨剂、耐火砖和路面防滑等。身边的一个例子是指甲护理用的砂纸指甲锉,其主要成分就是刚玉。「emery,(刚玉砂)」是以刚玉为主要成分的岩石名称。

Vol.07
Vol.07

水晶这些引人注目的石头有着各自的名字。那么,随处可见的所谓「普通的石头」被叫作什么呢?

石头根据成因可以分为两种岩石。一种是岩浆冷凝形成的「火成岩」,另一种是土沙等在水中沉积固结形成的「沉积岩」。「普通的石头」一般是这两种。

在岩浆冷却形成火成岩的过程中,有可能会在内部生长宝石。土沙堆积形成沉积岩的过程中,有可能会将混入的东西作为化石保留下来。

即使是普通的石头,也会经历不普通的过程,具有不普通的可能性。

Vol.08
Vol.08

第五话

玛瑙与水晶相同,是在日本产地分布广泛的矿物。

日本人与玛瑙历史渊源悠久,日本神话中出现的「三神器」之一的八尺琼勾玉,就被认为是由红玛瑙所制。玛瑙的色彩、花纹、以及透明感所带来的美感,似乎从古至今都让人感受到某种超越人类智慧的力量。

由于在历史上的记载中常被写作「玉」,因此与玛瑙相关的地方,很多地名中都带有「玉」字。岛根县松江市的玉汤町、以及流经茨城县的玉川等,都因实际盛产玛瑙而闻名。

此外,在其他能采到漂亮石头的地方,地名中也有使用「玉」字的情况。如果您居住的附近有这样的地名,试着探究一下其由来,或许会有新的发现。

Vol.09
Vol.09

在构成地球的所有岩石中,人类能够看到的不过是极其表面的部分,其体积甚至不到总量的1%。而占据80%以上比例的,是分布在其下方、广阔的地幔层。

准确地说,地幔可以分为「上地幔」和「下地幔」两层。其中,位于较浅处的上地幔,其主要成分是一种名为橄榄石的矿物。

橄榄石是一种绿色矿物,它的宝石名 Peridot 更为人熟知。在上地幔中,它以与其他岩石混合的形式存在,被称为橄榄岩。这种橄榄岩,根据所含橄榄石的含量,也可能呈现出漂亮的绿色。

人类目前尚无法直接看到地幔。但如果能够看到,那里也许正密密麻麻地塞满了绿色的宝石。虽说从宇宙中看到的地球被称为蓝色的星球,但若是剥掉这层名为地壳的薄皮,或许会发现它是一颗绿色的星球。

Vol.10
Vol.10

第六话

蓝宝石是一种蓝色的矿物,由原本纯净无色的刚玉含有作为杂质的铁和钛形成。「蓝宝石 Sapphire」这一名称源于拉丁语「蓝色 Sapphirus」,在日语中也称为「青玉」或「苍玉」。提起蓝宝石,人们往往会想到蓝色,但实际上,因所含杂质不同,它也会呈现橙色、绿色、黄色、粉色等多种颜色,而这些也全都属于蓝宝石。其称呼方式基本为「颜色+蓝宝石」,如「绿色蓝宝石GreenSapphire」等。

但有个例外,当含有铬作为杂质而呈现深红色时,则被称为「红宝石 Ruby」。这源于历史上曾经使用的宝石分类方法。在矿物学兴起发达的近代初期以前,宝石是根据颜色分类的。蓝色的宝石称为「蓝宝石」,红色的宝石称为「红宝石」。因此,蓝色的青金石也曾被称为蓝宝石,鲜红色的石榴石也曾被称为红宝石。

所以,在历史记载中即使出现了「蓝宝石」或「红宝石」的名称,也未必指代与现代相同的宝石。

Vol.11
Vol.11

过去由于没有研究机构,小镇和村庄中掌管「知识」的场所往往是神社佛阁。因此当地人发现奇特的物品或知识(如算额)便会送往这些地方。

某座寺庙就供奉着如此得来的「龙骨」与「天狗的爪子」。但后来才证实所谓的龙骨实为水生生物骨骼,天狗爪子实为鲨鱼的牙齿。二者虽实为当地化石,但当时的人们难以理解山中为何会出现巨型水生生物,便将其解释为龙或天狗。

这类传说的形成以及神社石碑的建立,都蕴含着古人的认知逻辑。尤其涉及化石时,多会归类于幻想生物。

若将民间故事简单归为荒诞不经实属可惜。理解古人的思维模式,再以现代视角重新解读,或许能有新的发现。

Vol.12
Vol.12

第七话

玻璃制品有青、绿、紫等颜色。但这些并非简单地将颜料滴入玻璃就能制成,而是通过反复试验进行化学反应才成功得到的产物。

若需蓝色则添加氧化钴,绿色则用氧化铬,紫色则需氧化锰。啤酒瓶等常见的茶色玻璃,是通过使氧化铁与硫磺、碳发生反应制成的。

在过去的日本,乳白色不透明玻璃是混入牛骨烧制而成的骨灰制作的。如今虽仍有使用骨灰的工艺,但也有混入萤石或水晶的制作方法。值得一提的是,乳白色玻璃具有优秀的遮光性。

同样,茶色玻璃也具备高遮光性,尤其能有效阻隔短波紫外线。而蓝色玻璃几乎无法阻隔紫外线。玻璃的特性会因其颜色而产生差异。

环顾我们四周,各种色彩的玻璃制品无处不在。它们被赋予特定颜色,并不只是因为设计需求。

Vol.13
Vol.13

有一种爱好叫作拾瓶(Bottle digging),主要是在海边或山上捡拾过去被丢弃的瓶子。

这些瓶子与现代玻璃制品不同,其独特的复古质感受许多人喜爱,优质的瓶子有时能卖出高价。

这种拾瓶爱好看似是近年诞生的现代人专属的乐趣,但实则不然。江户时代,欧洲已实现玻璃制品的大规模生产,但在日本仍是珍惜之物。

因此在长崎出现了专门捡拾荷兰人丢弃的酒瓶贩卖的人。据说一个瓶子可值一枚小判。甚至有因争抢瓶子而引发的杀伤事件,导致长崎最终禁止了拾瓶行为。

到了现代,随着塑料制品的普及,玻璃瓶逐渐减少。加之回收利用的推广,人们不再随意丢弃瓶子。于是现代的玻璃瓶很少留存。或许在遥远的未来,现在的瓶子会拥有超越一枚小判的价值。

Vol.14
Vol.14

第八话

大多数情况,日本的水都属于软水。所谓软水,是指钙与镁含量较低的水。而在欧洲等大陆地区,多数区域的水质则属于硬水。

这是为什么呢?其中一个原因在于地质中石灰岩的含量。与石灰岩地层较多的欧洲相比,日本的石灰岩含量不足0.5%,占比极低。实际上,广泛分布有由珊瑚礁形成的石灰岩的日本冲绳本岛南部地区,水的硬度就相对较高。

此外还存在另一个原因。日本国土狭小却多山,因此河流流域短,水流速度快。这导致岩石中的成分溶于水的时间缩短。所以与流速缓慢且较长的大陆河川相比,日本的水硬度也相对较低。

Vol.15
Vol.15

矿山资源自古以来就是人类生活中不可或缺的存在,但同时它也对周边环境有广泛且长期的影响,存在一定问题。

硫磺矿山中常存在大量黄铁矿,暴露在外的黄铁矿与水、氧气反应后会生成硫酸。这些硫酸流入下游河流会导致水体酸化,使生物无法生存。

此外,镉这种对人体有害的金属常存在于锌矿石中,在锌冶炼过程中的排水和废弃矿渣中仍有残留,因此锌矿周边需格外警惕污染问题。

矿山开采形成的地下空洞可能引发的周边地面塌陷等物理性危害也不容忽视。

日本历史上也曾因这些矿产资源引发过多起环境问题。但通过常年扎实的调查与研究,人们对污染问题的认知不断深化,如今已逐步采取了相应的治理措施。

Vol.16
Vol.16

第九话

蛋白石是有名的非晶态宝石,其化学式为SiO2·nH2O(n根据情况数值不同)。它是一种拥有「含有少量水分子(H2O)」罕见特性的矿石。这一特性使得蛋白石呈现出迷人的红绿光彩。

然而,蛋白石中的含水量会随着周围环境变化而增减。浸入水中会吸收水分增加含量,置于干燥环境则会蒸发减少。因此有些在原产地晶莹璀璨的蛋白石,被运往环境差异较大的地区后,会因含水量变化而失去光彩。若感觉蛋白石光泽暗淡,可以尝试将其浸入水中恢复。

值得一提的是,作为干燥剂使用的硅胶,其化学式同样可表示为 SiO2·nH2O,具有非晶态结构和水分易于进出的特性。若要为硅胶取个矿物名称,称之为「人工蛋白石」恰如其分。

Vol.17
Vol.17

人类建造的最大规模的建筑——水坝。

远离都市的自然环境中突然出现的水坝,其雄姿令人震撼。因其无可比拟的存在感,水坝本身成为观光景点也不足为奇。

而这样的水坝也有使命完结的一天。多数废弃水坝会被直接闲置,或被上游冲刷而来的泥沙掩埋,或被开洞泄水而化作墙体,静静度过余生。

相较于竣工时的盛况,水坝的退役往往悄无声息。再加之其往往位于难以探访且无人管理的深山,人们鲜少有机会目睹退役水坝的样貌。但也有像山口县红砖建造的「旧桂谷贮水池水坝」这样,作为有形文化遗产获得保存的特例。

当水坝结束使命逐渐回归自然,我们反而能从中感受到不同于服役时期的别样魅力。

Vol.18
Vol.18

第十话

关于铁路的铺路石,并没有规定必须使用特定种类或产地的石材。只要满足不会引发矿害、硬度高、耐磨性强、不易吸水等条件即可使用。常用的主要是安山岩和玄武岩等由岩浆冷却凝固形成、能大量采集的岩石。

那么这些铺路石产自何处呢?以JR东日本公司为例,其原料来源于自有采石场,分别位于茨城县的西金站、群马县的小野上站以及山梨县的初狩站附近。

由于石材运输难度较大,各家铁路公司普遍就近采用。比如,西金站附近开采的石材就被应用于茨城县与干叶县境内的线路。

因此,这些铺路石也能体现沿线地区独特的地质特征。

Vol.19
Vol.19

自古以来,矿物就被用于各种用途,其中一项重要用途便是制作颜料。蓝色和蓝绿色虽由矿物制成,但黑色颜料通常并非采用矿物,而是多使用木炭。通过简单燃烧木材即可获得的木炭,自远古时代起就作为黑色颜料被广泛应用于世界各地的壁画中。

然而也存在特例,那就是拉斯科洞穴的壁画。它并未使用木炭,而是特意采用从遥远的产地运输而来的二氧化锰。

木炭与二氧化锰呈现的色差微乎其微。但特意使用较难获取的二氧化锰,令人不禁推测创作者怀有特殊意图。最新科学分析显示,壁画中存在部分未使用二氧化锰的区域,取而代之的是洞穴内即可获取的「黑色土壤」。

必须需借助现代科技才能发现这一点,可见在当时,使用二氧化锰也是低效之举。但就结果而言,这反而让创作者作画的用心,甚至是其中蕴藏的某种人性特质流传至今。

Vol.20
Vol.20

第十一话

石头一般都给人很坚硬的印象。但其实很多石头都具有根据施加力的方向不同,很容易被劈开的特性。这种特性被称为「解理」。

具有该特性的代表性矿物是云母。云母具有极强的解理性,甚至有着「千层剥」的别名,用手都能将其薄薄地剥开。

方解石也很有名。它有一种晶体呈平行四边形,如果敲开的话,其碎片也会是同样的平行四边形。这就是「易于沿特定方向破裂」的特性导致的。

黄玉、钻石等被用作宝石的矿物也具有解理性。当它们被制成耳环、项链等首饰时,一般无需过于担心;但如果作为戒指佩戴,碰撞时破损的风险会较高。在购买镶有矿物的戒指时最好事先询问是否具有解理性。

Vol.21
Vol.21

凝固成形的石头(只要未熔化)通常给人一种不会变化的感觉。事实上,石头内部确实可能存留数十亿年前的物质,堪称永恒的象征。

然而,石头并非永远一成不变。当受到热和压力的影响时,石头未熔化也会改变形态,转变为所谓的「变质岩」。

例如因压力拉伸,呈条纹状变化的片麻岩;因受热由石灰岩转变而成的结晶质石灰岩(俗称大理石)等。

石头如今的形态,正诉说着其产地曾经所受的热与压力。

Vol.22
Vol.22

第十二话

要用矿石收音机收听声音,需要将电信号转换为声音的耳机。曾经制作其材料需要一种名为罗谢尔盐的物质。

日本为了获取这种罗谢尔盐,在战争期间曾有过鼓励葡萄酒生产的历史。葡萄中含有一种名为酒石酸的物质,会以「酒石」的形式出现在葡萄酒酿造过程中的沉淀物或储存用的木桶上。

当时正是以这种酒石为原料制作罗谢尔盐。不过,当时其主要用途并非收音机,而是通信设备或潜艇上配备的听音器。

为此,战争期间的葡萄酒生产受到国家鼓励,在其他酒类因统制政策而产量减少的情况下,葡萄酒的产量却增至战前的3倍。

如今,酒石结晶常被误认为是碎裂的玻璃混入物,因此在批量生产的产品中会被去除,但也有很多酒庄以不对原料进行加工为由特意保留,所以经常能在这类产品的软木塞或瓶底看到它们。由于酒石的成分也被用作食品添加剂,即使发现酒石也不必担心。

Vol.23
Vol.23

在广播爱好者中,有一种东西被称为Veri卡。

Veri卡是「Verification Card」的简称。当听众向广播电台寄送收听报告证明「在该地点收听到节目」后,便能受到来自各电台独家设计的收听确认卡,也就是Veri卡。

虽然部分电台因需求减少已停止发行Veri卡,但这些收听报告至今仍是掌握广播覆盖范围的重要资料。若有兴趣不妨尝试寄送报告。若成功接收到海外广播,说不定还能获得国外的Veri卡。

需要注意的是报告必须基于实际无线电接收机的收听记录,而非网络广播。此外,收听报告会涉及若干专业术语,建议事先通过网络等方式了解书写格式。

Vol.24
Vol.24

第十三话

日本火山众多,因此温泉资源也十分丰富。全国温泉泉源数量约达2万8千处,位居世界前列。日本也因此存在着许多珍稀的温泉。

其中于1960年代被发现并勘测的「十胜岳新喷火口温泉」是全世界罕见的特殊温泉。

此处涌出的温泉乍看是无色透明的普通热水,实则每升含有约600克沉淀物,属于超高浓度温泉。

当水温下降时,沉淀现象便开始产生。这种沉淀过程会吸收水分并急速固化,曾有「舀取作为样本的温泉在移至保存容器前已完全凝固」的报告。这处世间罕有的「固体温泉」虽然奇特,但浓度如此之高,恐怕难以用于沐浴。

Vol.25
Vol.25

在微小的尘埃世界里,往往留存着人们日常难以察觉的丰富信息。

例如烟花燃尽后,若收集现场细微的尘埃置于显微镜下观察,常能发现多彩的球状颗粒。那正是约烂火花燃烧留下的残影。即便肉眼不可见,微观世界却依然留存着烟花绽放的痕迹。

微小的人造物多在工业生产中进发火花时产生,即便是打火机点火的瞬间也会形成微型球体。

自十八世纪后半叶工业革命爆发以来,便能在全球地层中大量发现它们的痕迹。历史的转折点,在地层深处都留有证据。

反过来说,严格意义上未受人造颗粒影响的区域,如今仅剩极地、沙漠腹地等极少数地带。即便表面看似原始自然,人类活动的印记实则早已悄然留存。

Vol.26
Vol.26

🔲 ☆

Docker 部署备忘录

记录一下在用或用过的一些 Docker 程序,方便下次需要的时候复制粘贴。

这里把重复使用的变量写成了 .env 配置项,将其放在 compose.yml 同路径即可生效,可以在需要变更的时候批量修改。

PUID=1000
PGID=1000
TZ=Asia/Shanghai
NETWORK=caddy

另外,使用 Caddy 或 Nginx 配置反向代理时,可以直接利用 docker 内网寻址的方式获取容器地址。因此默认注释掉了端口映射,有需要的话可以自行开启。

外部网络使用以下命令创建,例如创建一个名为 caddy 的网络:

docker network create caddy

然后需要在 compose.yml 文件末尾指定这个网络,例如:

networks:
  caddy: 
    external: true

文件

h5ai

h5ai 是个我年轻的时候比较流行的 PHP 文件列表程序,现在有容器版本倒是方便了不少,因为 h5ai 需要自行安装许多 PHP 扩展。

似乎现在比较少见到大家用 h5ai 了,以前还有过内置 Dplayer 的版本,可以当作网盘用。

h5ai is a modern file indexer for HTTP web servers with focus on your files. Directories are displayed in a appealing way and browsing them is enhanced by different views, a breadcrumb and a tree overview. Initially h5ai was an acronym for HTML5 Apache Index but now it supports other web servers too.

h5ai 是一款现代化的 HTTP Web 服务器文件索引器,专注于您的文件。目录以美观的方式呈现,并通过不同的视图、面包屑导航和树形概览增强了浏览体验。h5ai 最初是 HTML5 Apache Index 的缩写,但现在也支持其他 Web 服务器。

另外 h5ai 还有很多可自定义的配置项,有需要请参考:《H5AI 部署、配置与美化》。

services:

  h5ai:
    image: awesometic/h5ai
    container_name: h5ai
    # ports:
    #   - 80:80
    environment:
      - TZ=${TZ}
      - PUID=${PUID}
      - PGID=${PGID}
    volumes:
      - ./h5ai:/config
      - /your/shared/dir:/h5ai
    restart: unless-stopped
    networks:
      - ${NETWORK}

File Browser

File Browser 要比 h5ai 强悍许多,不仅可以直接编辑文件、创建分享链接,还支持多用户。功能上更接近「个人云盘」,因此可以当作轻量云盘使用。

File Browser provides a file managing interface within a specified directory and it can be used to upload, delete, preview and edit your files. It is a create-your-own-cloud-kind of software where you can just install it on your server, direct it to a path and access your files through a nice web interface.

File Browser 可以在指定目录中提供文件管理界面,支持上传、删除、预览和编辑文件。它是一款类似于 “个人云盘” 的软件,您只需将其安装在服务器上,指定路径,然后即可通过美观的 Web 界面访问文件。

我这里的配置和官方有些许不同。官方需要挂载两处文件,而我统一挂载为 /config

services:

  filebrowser:
    image: filebrowser/filebrowser:s6
    container_name: filebrowser
    volumes:
      - ./config:/config
      - ./srv:/srv
    environment:
      - TZ=${TZ}
      - PUID=${PUID}
      - PGID=${PGID}
    # ports:
    #   - "80:80"
    restart: unless-stopped
    networks:
      - ${NETWORK}

只是需要在 /config 文件夹中手动创建两个文件。一个是数据库文件 filebrowser.db ,另一个则是 settings.json

{
  "port": 80,
  "baseURL": "",
  "address": "",
  "log": "stdout",
  "database": "/config/filebrowser.db",
  "root": "/srv"
} 

如果对 root 根目录的路径有自定义的需求,可以在 settings.json 中修改,只是相应地也要修改挂载的路径,不要忘了。

Cloudreve

Cloudreve 和前面俩不一样,是真正意义上的云盘,可以通过多种存储策略构建兼备自用或公用的网盘服务。存在付费功能,但个人版够用。

  • ☁️ 支持本机、从机、七牛、阿里云 OSS、腾讯云 COS、华为云 OBS、又拍云、OneDrive (包括世纪互联版) 、S3 兼容协议 作为存储端
  • 📤 上传/下载 支持客户端直传,支持下载限速
  • 💾 可对接 Aria2 离线下载,可使用多个从机节点分担下载任务
  • 📚 在线 压缩/解压缩、多文件打包下载
  • 💻 覆盖全部存储策略的 WebDAV 协议支持
  • 👩‍👧‍👦 多用户、用户组、多存储策略
  • 🔗 创建文件、目录的分享链接,可设定自动过期
  • 🌈 ... ...

在没有配置数据库的情况下,Cloudreve 会使用 SQLite 存储数据。但 SQLite 不支持高并发,官方建议在生产环境中使用其他数据库。

同时,Cloudreve 支持使用 Redis 作为键值缓存,但 Redis 不是必须的,在没有配置 Redis 的情况下,Cloudreve 会使用内存缓存,并在正常退出前将内存缓存的数据持久化到 cache_persist.bin 文件中。

另外,虽然官方文档写着「其中默认包含并启用了 Aria2」,但我没找到 Aria2 相关的配置,比如后台秘钥。

因此,下面的 compose.yml 增加了 Aria2 容器,配置了 PostgreSQL 数据库,并将 Cloudreve 和 Aria2 的下载目录映射至同一路径。

services:
  cloudreve:
    image: cloudreve/cloudreve:latest
    container_name: cloudreve
    depends_on:
      - postgresql
      - redis
    # ports:
    #   - 5212:5212
    environment:
      - CR_CONF_Database.Type=postgres
      - CR_CONF_Database.Host=postgresql
      - CR_CONF_Database.User=cloudreve
      - CR_CONF_Database.Name=cloudreve
      - CR_CONF_Database.Port=5432
      - CR_CONF_Redis.Server=redis:6379
    volumes:
      - ./cloudreve:/cloudreve/data
      - ./downloads:/downloads
    restart: unless-stopped
    networks:
      - ${NETWORK}


  postgresql:
    image: postgres:latest
    container_name: postgresql
    environment:
      - POSTGRES_USER=cloudreve
      - POSTGRES_DB=cloudreve
      - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
      - ./postgresql:/var/lib/postgresql/data
    restart: unless-stopped
    networks:
      - ${NETWORK}
      
  aria2:
    image: p3terx/aria2-pro
    container_name: aria2
    # ports:
    #   - 6888:6888
    #   - 6888:6888/udp
    volumes:
      - ./downloads:/downloads
      - ./aria2:/config
    environment:
      - TZ=${TZ}
      - PUID=${PUID}
      - PGID=${PGID}
      - RPC_SECRET=your_secret_here
    restart: unless-stopped
    networks:
      - ${NETWORK}
      

  redis:
    image: redis:latest
    container_name: redis
    volumes:
      - ./redis:/data
    restart: unless-stopped
    networks:
      - ${NETWORK}   

Zfile

ZFile 是一个基于 Java 的在线网盘程序,功能上大体和 Cloudreve 类似,不过不支持离线下载。

  • 支持 S3 协议, 阿里云 OSS, FTP, 华为云 OBS, 本地存储, MINIO, OneDrive 国际/家庭/个人版/世纪互联版/SharePoint, , 七牛云 KODO, 腾讯云 COS, 又拍云 USS.
  • 支持文件操作:上传、下载、重命名、删除、新建文件夹等
  • 图片画廊模式,且支持自定义列数,间距等信息。
  • 文件/文件夹加密、隐藏
  • 目录 readme 文档
  • 支持在线浏览文本文件、PDF、图片、音乐、视频(支持 mp4、flv、hls)
  • 文件直链和二维码
  • 同时挂载多个存储策略
services:

  zfile:
    image: zhaojun1998/zfile:latest
    # ports:
    #   - 8080:8080
    volumes:
      - ./db:/root/.zfile-v4/db
      - ./logs:/root/.zfile-v4/logs
    restart: unless-stopped
    container_name: zfile
    networks:
      - ${NETWORK}

Caddy

Caddy 也能快速搭建一个文件管理列表出来,对于只是浏览和下载来说非常方便,界面也比较美观。

只需要在反代的时候加上 file_server browse,例如:

example.com {
    encode zstd gzip
    root * /home/wwwroot/file
    file_server browse
}

同步

Syncthing

Syncthing 是一个全平台的同步工具,可以在两台或多台设备之间即时同步文件,保证文件的一致性。

Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers. We strive to fulfill the goals below. The goals are listed in order of importance, the most important ones first.

Syncthing 是一款持续文件同步程序 。它可以在两台或多台计算机之间同步文件。我们致力于实现以下目标。这些目标按重要性排序,最重要的放在最前面。

这里使用的是 LinuxServer.io⁠ 的镜像。另外由于 Syncthing 连接的需求,需要映射 22000 端口。

services:
  syncthing:
    image: linuxserver/syncthing:latest
    container_name: syncthing
    # hostname: syncthing #optional
    environment:
      - TZ=${TZ}
      - PUID=${PUID}
      - PGID=${PGID}
    volumes:
      - ./config:/config
      - ./data:/data
    ports:
    #   - 8384:8384
      - 22000:22000/tcp
      - 22000:22000/udp
      - 21027:21027/udp
    restart: unless-stopped
    networks:
      - ${NETWORK} 

监控

Uptime Kuma

Uptime Kuma 是一款易于使用的自托管监控工具。

Github:https://github.com/louislam/uptime-kuma

  • Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / HTTP(s) Json Query / Ping / DNS Record / Push / Steam Game Server / Docker Containers

    监控 HTTP(s) / TCP / HTTP(s) 关键字 / HTTP(s) Json 查询 / Ping / DNS 记录 / 推送 / Steam 游戏服务器 / Docker 容器的正常运行时间

  • Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and 90+ notification services, click here for the full list
    通过 Telegram、Discord、Gotify、Slack、Pushover、电子邮件 (SMTP) 和 90 多种通知服务发送通知
  • Multi Languages

    多语言

  • Multiple status pages

    多个状态页面

不过 uptime-kuma 包含 Chromium,不仅占空间,还吃内存,需要一定配置的设备运行。

services:

  uptimekuma:
    image: louislam/uptime-kuma:beta
    container_name: uptimekuma
    volumes:
      - ./uptimekuma:/app/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TZ=${TZ}
    restart: unless-stopped
    networks:
      - ${NETWORK} 

Nezha

开源、轻量、易用的服务器监控与运维工具。

一键安装:支持一键安装面板和监控服务,操作便捷。兼容主流系统,包括 Linux、Windows、macOS、OpenWRT 以及群晖。

实时监控:支持同时监控多个服务器的状态,提供历史网络状态和延迟图表,监控网页、端口可用性和 SSL 证书状态。支持故障和流量等状态告警,可通过 Telegram、邮件、微信等多种方式提醒。

轻松运维:提供 API 获取服务器状态,支持WebSSH、DDNS 和流量监控。可设置定时和触发任务,并批量执行服务器任务。

可以不用脚本安装面板端:

  nezha:
    image: ghcr.io/nezhahq/nezha
    container_name: nezha
    volumes:
      - ./nezha:/dashboard/data
    environment:
      - TZ=${TZ}
    # ports:
    #   - "8008:8008"
    restart: unless-stopped
    networks:
      - ${NETWORK} 

参考官方文档使用 Nginx 反代即可用 443 端口连接。需要在面板后台设置中勾选「Agent 使用 TLS 连接」,「Agent对接地址」填写「域名:端口」,例如 example.com:443 ,然后就可以在安装监控端的时候正常连接了。

信息

RSSTT

基于 Telegram 机器人创建的 RSS 通知。利用通讯工具的即时属性,可以用来订阅某些重要的通知。

Github:https://github.com/Rongronggg9/RSS-to-Telegram-Bot

这里只列出了必须的环境变量,其他变量如有需求请自行参考官方文档。

services:

  rsstt:
    image: rongronggg9/rss-to-telegram:dev  # stable image: rongronggg9/rss-to-telegram
    container_name: rsstt
    volumes:
      - ./data:/app/config
    environment:
      - TOKEN=xxxx  #访问 @BotFather 获取
      - MANAGER=xxxx  # 访问 @userinfobot 获取,使用;分隔多用户。例如 1234567890;987654321
# ↓------ To disable sending via Telegraph, comment out this area ------↓ #
# 访问以下链接获取 Telegraph API access tokens: https://api.telegra.ph/createAccount?short_name=RSStT&author_name=Generated%20by%20RSStT&author_url=https%3A%2F%2Fgithub.com%2FRongronggg9%2FRSS-to-Telegram-Bot
# 刷新页面即可获取新的 token,如果你有一堆订阅,请至少使用5个token 
#                            ↓ Replace with your access tokens ↓
      - TELEGRAPH_TOKEN=
        xxx
        xxx
      - MULTIUSER=1  #多用户模式,0关闭,1启用,默认为1
      - TZ=${TZ}
    restart: unless-stopped
    networks:
      - ${NETWORK} 

RSSHub

万物皆可 RSS,世界上最大的 RSS 网络。

由于配置了 browserless,因此 RSSHub 非常吃配置,需要部署在稍微给力点的机子上。

services:

  rsshub:
    image: diygod/rsshub:latest
    container_name: rsshub
    # ports:
    #   - '1200:1200'
    environment:
      - NODE_ENV=production
      - CACHE_TYPE=redis
      - REDIS_URL=redis://redis:6379/
      # - PIXIV_REFRESHTOKEN=
      # - PIXIV_IMG_PROXY=
      # - PIXIV_BYPASS_DOH=https://dns.google/dns-query
      # - PIXIV_BYPASS_CDN=true
      - PUPPETEER_WS_ENDPOINT=ws://browserless:3000  # marked
      # - PROXY_URI='socks5h://warp-socks:9091'
    restart: unless-stopped
    depends_on:
    #   - redis
      - browserless
    networks:
      - ${NETWORK} 

  browserless:  # marked
    image: browserless/chrome  # marked
    container_name: browserless
    restart: unless-stopped  # marked
    ulimits:  # marked
      core:  # marked
        hard: 0  # marked
        soft: 0  # marked
    networks:
      - ${NETWORK} 

FreshRSS

自托管 RSS 信息流聚合器。

这里使用的是 LinuxServer.io⁠ 的镜像,另外配置了两个插件。时间久远我也不记得这俩咋用了,如有需要请自行研究。

services:

  freshrss:
    image: linuxserver/freshrss:latest
    container_name: freshrss
    # ports:
    #   - 80:80
    volumes:
      - ./config:/config
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - CRON_MIN=*/20
    logging:
      options:
        max-size: 10m
    restart: unless-stopped
    networks:
      - ${NETWORK} 

  read:
    image: phpdockerio/readability-js-server
    container_name: read
    restart: unless-stopped
    networks:
      - ${NETWORK} 

  merc:
    image: wangqiru/mercury-parser-api
    container_name: merc
    restart: unless-stopped
    networks:
      - ${NETWORK} 

工具

Vaultwarden

自托管密码管理器。

services:

  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    volumes:
      - ./data:/data
    environment:
      - WEBSOCKET_ENABLED=true
      # - ADMIN_TOKEN=
      - SMTP_HOST=
      - SMTP_FROM=
      - SMTP_PORT=
      - SMTP_SECURITY=starttls
      - SMTP_USERNAME=
      - SMTP_PASSWORD=
      - SMTP_AUTH_MECHANISM="Login"
      - DOMAIN=https://
      - SIGNUPS_ALLOWED=false
    restart: unless-stopped
    networks:
      - ${NETWORK} 

Nazurin

帮助你从各种网站收集 ACG 插画的 Telegram 机器人。

这个工具对于我这种收图党来说非常好用,有空水一下用法。

services:
  nazurin:
    image: ghcr.io/y-young/nazurin:latest
    container_name: nazurin
    user: ${PUID}:${PGID}
    # build: .
    env_file:
      - ".env"
    volumes:
      - ./data:/app/data
      - ./Nazurin:/Nazurin
    # ports:
    #   - "80"
    environment:
      - TZ=${TZ}
    restart: unless-stopped
    networks:
      - ${NETWORK} 

由于包含了所有存储配置,默认的环境变量非常长。如果你像我一样也将图片保存在本地,且服务不想对外,可以试试这么写:

# 访问以下链接获取更多信息:https://nazurin.readthedocs.io/getting-started/configuration/

# ---------- Required ----------
# Telegram bot token
TOKEN = xx

# 默认选项(production)使用 Webhook 模式,你可以设置为 development 以使用轮询模式。
ENV = development

# 发送到 Telegram 服务器的 Webhook URL,机器人的服务器应能通过此 URL 访问,应以 / 结尾,例如 https://xxx.fly.dev/。
# 如使用 Webhook 模式则必需
# WEBHOOK_URL = 

# 要绑定到的主机地址,默认为 0.0.0.0。使用反向代理时请设置为 127.0.0.1。
# 如使用 Webhook 模式则必需
HOST = 0.0.0.0

# Webhook 端口,使用 Heroku 时自动设定。
# 如使用 Webhook 模式则必需
PORT = 8080

# 存储类型
STORAGE = Local

# 数据库类型
DATABASE = Local

# 管理员用户的 Telegram 用户 ID(不是用户名),机器人的一些功能仅限管理员用户使用。
ADMIN_ID = xx

# ---------- Optional ----------
# 本地或远程存储的目录路径,如不存在则自动创建。
STORAGE_DIR = /Nazurin

# 同时下载的最大文件数。
MAX_PARALLEL_DOWNLOAD = 6

# 临时目录清理的间隔时间,单位为天。每次清理时将删除访问时间在一天前的文件。设置为 0 时将禁用自动清理。
CLEANUP_INTERVAL = 7

# 图片收藏成功后的反馈方式,可选值如下:
# reply:回复原消息
# reaction: 在原消息上添加表情回应
# both:回复并添加表情回应
FEEDBACK_TYPE = reaction

# ---------- 站点 ----------

# ----- Bilibili -----
# 存储路径
BILIBILI_FILE_PATH = Bilibili

# 文件名称
# BILIBILI_FILE_NAME = {id_str}_{index} - {user[name]}({user[mid]}) 


# ----- Pixiv 配置项-----
# Refresh token
PIXIV_TOKEN = xx

# 浏览作品时的图片代理,用于帮助 Telegram 服务器更稳定地获取图片,下载作品时仍然会使用 Pixiv 服务器。
# PIXIV_MIRROR = 

# 标签的显示语言,如果没有可用翻译则使用日语原文。默认使用原文,配置值将设置在 HTTP Header 的 Accept-Language 选项中。例如:zh-CN, en-US
PIXIV_TRANSLATION = zh-CN

# 收藏可见性 (public/private)
# PIXIV_BOOKMARK_PRIVACY = public

# 存储路径
PIXIV_FILE_PATH = Pixiv/{user[id]}

# 文件名称
PIXIV_FILE_NAME = {filename}

# ----- Twitter 配置项-----
# 要使用的 API,web 或 syndication。Web API 来自网页应用,而 Syndication API 来自 https://publish.twitter.com。
# Syndication API 无法获取被标记为敏感内容的推文,但如果设置了 TWITTER_AUTH_TOKEN,则可以通过 Web API 获取。
# 当 Web API 不可用时,你可以选择切换到 Syndication API。
TWITTER_API = web

# Web API 的授权令牌
TWITTER_AUTH_TOKEN = xx

# 存储路径
TWITTER_FILE_PATH = Twitter

# 文件名称
TWITTER_FILE_NAME = {id_str}_{index} - {user[name]}({user[id_str]})

其他

gh-proxy-go

gh-proxy 的 Go 语言版本。

Github:https://github.com/Xm798/gh-proxy-go

默认的配置文件 config.json 支持自定义以下选项,挂载 /app/config 即可:

  • host: 默认 "0.0.0.0",服务器监听地址
  • port: 默认 8080,服务器监听端口
  • whiteList: 允许访问的 GitHub 用户/组织列表,配置后将拒绝所有未在列表中的请求
  • blackList: 禁止访问的 GitHub 用户/组织列表,配置后将拒绝所有在列表中的请求
  • forceEnUSForRaw: 默认 false,是否强制使用 en-US 语言访问 raw.githubusercontent.com,以规避中文用户可能的 429 错误
  • sizeLimit: 默认 10240,文件大小限制(单位:MB)

注意:hostport 配置不支持热重载,需要重启服务才能生效。其他配置项支持热重载。

services:
  ghproxy:
    image: xm798/gh-proxy-go:latest
    container_name: ghproxy
    # ports:
    #   - 8080:8080
    # volumes:
    #   - ./ghproxy:/app/config
    environment:
      - TZ=${TZ}
    restart: unless-stopped
    networks:
      - ${NETWORK} 

hubproxy

一个轻量级、高性能的多功能镜像服务。

Github:https://github.com/sky22333/hubproxy

services:

  hubproxy:
    container_name: hubproxy
    image: ghcr.io/sky22333/hubproxy
    restart: always
    # ports:
    #   - 5000:5000
    networks:
      - ${NETWORK} 

最后

还有啥等想起来再加进来。

🔲 ⭐

关于 Caddy

最近试着用了下 Caddy,自动申请证书确实非常方便,就是中文文档少了点。官方文档虽然写得很详细,社区里提问也基本有问必答,奈何都是英文。而 I english was very bad,所以这里记录一些使用过程中遇到的问题,以便以后忘了怎么用的时候,不必再跟 AI 大战三百回合。

安装

直接参考 Cyrus 佬的 docker-caddy 就行。如果需要其他模块,参照仓库内的 Dockerfile 修改一下,自己编译一个镜像。

伪静态

Typecho 是 PHP 驱动的博客程序,但可以启用伪静态以优化 URL,不过需要在 Web 服务器上处理一下。如果是 Nginx 的话,是这么写的:

try_files $uri $uri/ /index.html;

而 Caddy 可以直接通过 php_fastcgi 实现 Nginx 的 try_files 效果,不过根据官方文档对于 php_fastcgi 的解释,其实是这个指令自带了一些默认写法,包括 try_files。例如:

    @indexFiles file {
        try_files {path} {path}/index.php index.php
        try_policy first_exist_fallback
        split_path .php
    }

因此配置一个 Typecho 博客的时候,伪静态就可以直接这么写:

example.com {
    encode zstd gzip
    root * /srv/example.com
    file_server
    php_fastcgi php:9000
    header {
        -Server
        -X-Powered-By
    }
}

但是二级目录就行不通了,得手动写一下 try_files 指令。Nginx 是这么写的:

    location /blog/ {
        if (!-e $request_filename) {
            rewrite ^(.*)$ /blog/index.php$1 last;
        }
    }

Caddy 也差不多。假设你的一级域名博客 example.com 放在了 /srv/example.com 这个文件夹中,而这个文件夹里又有个 blog 文件夹打算放另一个博客,即:

example.com {
    encode zstd gzip
    root * /srv/example.com
    file_server
    php_fastcgi php:9000
    handle /blog/* {
        try_files {path} /blog/index.php?{query}
    }
}    

注意要保证 /blog/index.php?{query} 前面的 blog 和文件夹名称一致。

然后伪静态就能正常工作了。

日志

参考《一行代码快速配置 Caddy 站点日志——复用 Caddy 配置段》,可以实现使用 Caddy 的代码段(Snippet)和占位符,为每个站点更优雅地配置日志。

但有一个问题是, 对于习惯了 Nginx 简短的一行日志的我来说,Caddy 的 JSON 日志并不优雅:

{"level":"info","ts":1753359481.0373497,"logger":"http.log.access.log19","msg":"handled request","request":{"remote_ip":"3.4.5.6","remote_port":"51262","client_ip":"1.2.3.4","proto":"HTTP/2.0","method":"GET","host":"example.com","uri":"/vcb-s","headers":{"Cf-Ipcountry":["US"],"Cdn-Loop":["cloudflare; loops=1"],"Accept":["application/rss+xml, application/rdf+xml, application/atom+xml, application/xml;q=0.9, text/xml;q=0.8, text/*;q=0.7, application/*;q=0.6"],"X-Forwarded-Proto":["https"],"Cf-Connecting-Ip":["1.2.3.4"],"If-None-Match":["\"1138a-MnD/hvrofjXPGRMPTdAYBESwO4U\""],"If-Modified-Since":["Thu, 24 Jul 2025 04:34:01 +0000"],"Cf-Ray":["96435b8e8b406fd7-IAD"],"X-Forwarded-For":["1.2.3.4"],"User-Agent":["RSStT/2.10.0 RSS Reader (+https://git.io/RSStT)"],"Accept-Encoding":["gzip, br"],"Cf-Visitor":["{\"scheme\":\"https\"}"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"example.com"}},"bytes_read":0,"user_id":"","duration":0.926949864,"size":0,"status":304,"resp_headers":{"Via":["1.1 Caddy"],"Access-Control-Allow-Methods":["GET"],"X-Content-Type-Options":["nosniff"],"Date":["Thu, 24 Jul 2025 12:18:01 GMT"],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Access-Control-Allow-Origin":["example.com"],"Cache-Control":["public, max-age=300"],"Content-Type":["application/xml; charset=utf-8"],"Etag":["\"1138a-MnD/hvrofjXPGRMPTdAYBESwO4U\""],"X-Rsshub-Route":["/vcb-s"]}}

至少不是人能看的。

我只是写写博客玩,用不上这么详细的日志。一番搜索,发现 Nginx 的日志样式叫做 Common Log Format (通用日志格式),而 Caddy 有一个模块可以实现这种日志样式。可以在构建镜像的时候自己塞进去:

FROM caddy:builder AS builder

RUN apk add --no-cache tzdata

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    # --with github.com/caddy-dns/dnspod \
    # --with github.com/caddy-dns/alidns \
    --with github.com/caddy-dns/tencentcloud \
    # Extended plugins
    --with github.com/mholt/caddy-dynamicdns \
    --with github.com/caddyserver/replace-response \
    # 加入日志模块
    --with github.com/caddyserver/transform-encoder

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

然后在原日志代码段加上日志格式化配置:

(log) {
    log {
        output file /log/{args[0]}/access.log {
            roll_size 5MiB
            roll_local_time
            roll_keep 10
            roll_keep_for 2160h
        }
        format transform `{request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` {
            time_format "2006-01-02 15:04:05"
            time_local
        }
    }
}

日志就会变成这样:

4.5.6.7 - - [2025-07-25 10:11:37] "GET / HTTP/2.0" 200 1901 "-" "Uptime-Kuma/2.0.0-beta.3"

舒服多了。

只是这个模块被官方遗弃了,官方觉得 JSON 日志好使。

CA

Caddy 默认的 CA(证书颁发机构)是 Let's Encrypt,但也支持使用其他 CA,比如 ZeroSSL。而我们还可以通过在 Caddyfile 中使用 acme_ca 指令来指定其他 CA,比如 Google。

参考《使用 acme.sh 申请 Google 的免费 SSL 证书》获取 EAB 密钥后,在 Caddyfile 顶部加入以下配置:

    acme_ca https://dv.acme-v02.api.pki.goog/directory
    acme_eab {
        key_id xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        mac_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    }
    email your@mail.com

随后添加域名,重载 Caddy 配置,尝试申请证书。

如果 Caddy 内的 /data/caddy/acme/ 文件夹出现了名为 acme/dv.acme-v02.api.pki.goog-directory 的文件夹,且该文件夹内包含上面注册 ACME 账户时使用的邮箱,即表示账户注册成功。

随后要观察 /data/caddy/certificates/ 是否文件夹出现了 dv.acme-v02.api.pki.goog-directory 文件夹。若该文件夹内部有域名证书生成,则表示证书申请成功。

文件结构大概是像这样:

data
└── caddy
    ├── acme
    │   ├── acme-v02.api.letsencrypt.org-directory
    │   │   └── users
    │   │       └── your@mail.com
    │   ├── acme.zerossl.com-v2-dv90
    │   │   └── users
    │   │       └── your@mail.com
    │   └── dv.acme-v02.api.pki.goog-directory
    │       └── users
    │           └── your@mail.com
    ├── certificates
    │   ├── acme-v02.api.letsencrypt.org-directory
    │   │   └── example.com
    │   │       ├── example.com.crt
    │   │       ├── example.com.json
    │   │       └── example.com.key
    │   └── dv.acme-v02.api.pki.goog-directory
    │       └── example.com
    │           ├── example.com.crt
    │           ├── example.com.json
    │           └── example.com.key
    ├── instance.uuid
    ├── last_clean.json
    ├── locks
    └── ocsp

不过这个 EAB 密钥使用一次后便会自动失效,不使用也会在七天内失效。但使用 EAB 密钥注册的 ACME 帐户没有过期时间,对证书续期没有影响。

所以待 ACME 账户注册成功后,就可以注释或删除相关秘钥,仅保留 acme_ca 配置:

    acme_ca https://dv.acme-v02.api.pki.goog/directory
    email your@mail.com

但使用 Google CA 除了能表明证书来自谷歌之外,没有其他优势。追求新鲜玩玩就够了,平常还是用 Let's Encrypt 或 ZeroSSL 比较合适。

再说 https://dv.acme-v02.api.pki.goog/directory 也被墙了,国内机子根本没法申请。

缓存

Caddy 并不能像 Nginx 一样在反代网站的时候配置缓存,需要手动安装一些模块。比如 cache-handler,一个分布式 HTTP 缓存模块,构建镜像的时候加上它:

FROM caddy:builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    # --with github.com/caddy-dns/dnspod \
    --with github.com/caddy-dns/alidns \
    --with github.com/caddy-dns/tencentcloud \
    # Extended plugins
    --with github.com/mholt/caddy-dynamicdns \
    --with github.com/caddyserver/replace-response \
    # 分布式 HTTP 缓存模块
    --with github.com/caddyserver/cache-handler

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

使用倒是很简单:

{
    cache
}

example.com {
    cache
    reverse_proxy your-app:8080
}

具体用法还是得查查相关文档,我不太会用。

但我在琢磨缓存的时候搜到了另一个模块,叫做 cdp-cache。它与 cache-handler 的区别在于,这个模块更容易理解和配置。

一样先构建镜像:

FROM caddy:builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/caddy-dns/tencentcloud \
    # Extended plugins
    --with github.com/mholt/caddy-dynamicdns \
    --with github.com/caddyserver/replace-response \
    # 第三方缓存模块
    --with github.com/sillygod/cdp-cache

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

然后在网站配置中启用:

proxy.example.com {
    reverse_proxy https://example.com
    http_cache {
        cache_type file
        path /cache
        match_path /images
        default_max_age 24h
    }
}

配置项不少,具体得参考官方文档。

我玩不来,用了一两天就删掉了。

robots.txt

如果有些网站你并不想被爬虫抓取,就可以使用 robots.txt,立下君子协定:

User-agent: *
Disallow: /

这玩意本质上就是个 txt 文本,所以可以在 Web 服务器端直接配置,例如 Nginx:

    location = /robots.txt {
        default_type text/html;
        add_header Content-Type "text/plain; charset=UTF-8";
        return 200 "User-Agent: *\nDisallow: /";
    }

Caddy 也可以这么做,直接在 Caddyfile 中加入 robot_txt 配置块:

(robots_txt) {
    respond /robots.txt 200 {
        body "User-agent: *
Disallow: /"
    }
}

然后在网站配置文件中导入该块:

example.com {
    encode zstd gzip
    root * /srv/blog
    file_server
    import robot_txt
}

IPv6

现在越来越多的服务器厂商开始支持 IPv6,我的服务器也提供了开启 IPv6 的选项。但 IPv6 其实坑不少,至少我不太会用。就比如我一昧追新开启了 IPv6 后,发现博客后台获取不到正确的访客 IP 地址,全部都是内网地址。

我甚至以为是 Nginx 的问题,查了半天,才发觉可能是 IPv6 的锅。

总之,AI 是这么解释的:

此问题的根源在于 Docker 守护进程未启用 IPv6,导致容器本身不具备原生的 IPv6 网络栈和地址。当外部 IPv6 流量通过端口映射到达宿主机时,Docker 的网络代理层(通常是 docker-proxy 进程或 iptables 规则)必须执行跨协议转发。在此过程中,它会终止原始的 TCPv6 连接,然后以 Docker 网桥的 IPv4 地址(如 172.17.0.1)为源地址,向容器的私有 IPv4 地址发起一个全新的 TCPv4 连接。最终,容器内的 Nginx 服务只能感知到这个新连接的直接来源,即作为代理的 Docker 网关,从而丢失了真实的客户端 IPv6 源地址信息。

所以,如果你的服务器也启用了 IPv6,为了让容器能正常识别来自 IPv6 的访问,请开启 Docker 的 IPv6 支持,并创建一个支持 IPv6 的 Docker 网络。

编辑 Docker 的配置文件(通常位于 /etc/docker/daemon.json),加入或合并以下内容以开启 Docker 的 IPv6 支持:

{
  "ipv6": true,
  "fixed-cidr-v6": "fd00:dead:beef::/48"
}

操作命令:

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<EOF
{
  "ipv6": true,
  "fixed-cidr-v6": "fd00:dead:beef::/48"
}
EOF

重启 Docker 服务以应用更改:

sudo systemctl restart docker

随后创建一个支持 IPv6 的 Docker 网络:

docker network create \
  --driver bridge \
  --ipv6 \
  --subnet fd00:3939::/64 \
  caddy

再把容器加入到这个新建的 caddy 网络即可。

文件访问

在 Nginx 中通常会加上这么个配置:

    location ~ /.well-known {
        allow all;
    }

    location ~ /\. {
        deny all;
    }

前者用于允许路径 /.well-known 下内容的访问1,后者则是禁止访问以 点号开头的隐藏文件(比如 .git.htaccess.env 等)。

Caddy 默认会对 /.well-known/acme-challenge 这类路径自动处理,而对于以点开头的隐藏文件,使用 @hiddenFiles 匹配所有路径以 / + 点号 开头的请求,再 respond 返回 HTTP 403 禁止访问即可。

@hiddenFiles {
    path_regexp hiddenFiles ^/\..*
}
respond @hiddenFiles 403

塞到网站配置中就能用了:

example.com {
    encode zstd gzip
    root * /srv/blog
    file_server

    @hiddenFiles {
        path_regexp hiddenFiles ^/\..*
    }
    respond @hiddenFiles 403
}

  1. 这是一个标准路径,通常用来放置一些公开的、需要被外部访问的文件或信息,比如 Let's Encrypt 证书验证文件(ACME Challenge)、安全政策文件(如 security.txt)、其他协议定义的公开信息文件。
🔲 ☆

我从来没有觉得看《Ave Mujica》快乐过

这次也尝试水一下今年看过的一月番剧的简单吐槽。

BanG Dream! Ave Mujica

还记得两年前的7月,那时《BanG Dream! It's MyGO!!!!!》正热火朝天,可惜我本就对《BanG Dream!》系列一点都不感兴趣,更不用说这《MyGO!!!!!》了。什么「重女」 ,什么「地雷」,通通与我毫无干系。我只是因为刷到了泛式的视频,再加上大家的口口相传,闲来无事才想着看一两集试试水的。至于用一个下午的时间一口气看到了 10 话,完全是意料之外。虽然不得不承认这种欲罢不能的感觉十分奇妙,就像是便秘了 10 天一口气排干净那般畅快。以至于我不禁怀疑是不是年龄来到了 30 岁之后,连追番取向都发生了改变。

总之,正是因为《MyGO!!!!!》,我才对这类题材番剧的刻板印象有所改观,继而能接受除《轻音少女》和《孤独摇滚》以外的乐队番。后来我又看了《夜晚的水母不会游泳》和《少女乐队的呐喊》,以及《MyGO!!!!!》的续篇:《BanG Dream! Ave Mujica》。

在《MyGO!!!!!》还在热映的时候我便已经非常好奇,丰川祥子退出 CRYCHIC 的原因能否得到完美的解释,三角初华对祥子的不离不弃又是出自何种原因,高松灯和祥子是否能够再续前缘……当然最重要的,还是《MyGO!!!!!》最后一集登台的 Ave Mujica 乐队,会为这个系列带来一段如何「黑深残」的故事。而现在,距离动画完结已经过去了三个多月,我的疑惑也确实得到了「完美」的解答。

祥子退队的原因不难解释。一直接受精英教育的她在经历母亲离世、父亲因故被逐出家门等等一系列变故后,经济和心境上都陷入了空前的困境。曾经优渥的生活和如今挤在三坪小房间里的窘境带来的反差,她已经无法再像往日一样游闲地同乐队伙伴畅谈音乐理想。白天上学、晚上兼职、回家还要面对父亲的冷暴力。这突如其来的一切使她迫不得已选择用最极端、最决绝的方式来斩断过去的生活,包括与 CRYCHIC 的联系。

这也许是她面对巨大生活压力和内心挣扎的一种扭曲的自我保护机制。祥子一直以来都是一个骄傲且自尊心极强的人,即便身边知情的朋友愿意提供帮助,比如比如睦、初华——甚至是下跪的素世,她还是选择独自承受一切,不愿将自己软弱的一面示人。

只不过祥子家庭上的变故早在《MyGO!!!!!》中就已见端倪,多少能从剧情上看出一些猫腻,《Ave Mujica》仅仅是将这些背后的故事搬到台面上了而已。我本以为会有其他出乎意料的解释,没想到是如此现实的原因。于是我又有了新的困惑:既然祥子是因为自身原因才从 CRYCHIC 退队的,那睦为什么要说出「我从来没有觉得玩乐队开心过」这句话呢?

彼时的睦作为配角戏份并不多,她带给观众的一直都是沉默寡言、情绪不外露的文静形象。即便她的艺人家庭背景有稍微展露,也从未提到过她有演戏的才能。反倒是她的「无口」性格可能是由于缺少父母的陪伴、生活氛围过分压抑等家庭因素所导致的。如果连她都觉得在 CRYCHIC 的活动十分压抑,那乐队内部可能早已病入膏肓,分崩离析自然是迟早的事。

单凭这些,我还是觉得无法直接将睦的言行同「演技」挂钩,可编剧就是这么做了。在《Ave Mujica》中我们可以清楚地得知,由于受到艺人家庭的影响,儿童时期的睦就已经表现出远超常人理解的演技天赋。她可以在随时随刻转换情绪,自由地将大家希望看到的一面通过「演技」展示出来。

没有特别复杂的原因,睦就是为了袒护突然退队的祥子,才故意演了一出「好戏」的,只是这么解释明显背离了《MyGO!!!!!》时睦的人设,所以为了合理化这一行为,编剧还强行安排上所谓「双重人格」的设定,以便让一切设定合理自洽。

只是如此一来,CRYCHIC 解散的「病根」就显得有些荒谬了。如果一切都是睦为了给突然退队的祥子而演的一场戏,如果《MyGO!!!!!》中睦的言行完全都是演技,那么 CRYCHIC 的解散就不再是成员之间的真实矛盾,而更像一场人为的闹剧。高松灯所有的挣扎和成长,就都建立在了这样一个虚假的基石之上。

这么做除了把睦的角色搞得更加面目全非、给本就雪上加霜的剧情再浇一盆冷水、使粉丝失望、让路人贻笑大方以外,没有其他意义。

这还不算恶心人,《Ave Mujica》对初华角色的补全,是另一层面的匪夷所思。初华在《MyGO!!!!!》中虽然戏份不多,也算是比较重要的角色。不仅在祥子失落的时候陪伴着她、给予她无微不至的关怀,还在祥子提议组建新乐队的时候积极参与——这是一种近乎无条件的忠诚与爱意。我本以为会深入探讨她与祥子之间那份超越表象的情感,或是揭示她为何能如此坚定地站在祥子身边。但仿佛是要扼杀这一角色一般,编剧全盘否定了初华自身的努力,将她置于一个非常低贱的地位,把她的所作所为全部归结为对祥子的「报恩」。

故事讲到这里,我已经不再关心《Ave Mujica》能否再续《MyGO!!!!!》的高光,也不在意祥子会不会在 Ave Mujica 中解开心结,更无所谓「黑深残」会发展成什么样的结局。因为真正在意这个「过家家乐队」生死存亡的,只有不知为何将自己绑定在 Ave Mujica 的海铃,和想借机炒红自己的瞄梦。

祥子应该好好想想自己是怎么对待素世的
祥子应该好好想想自己是怎么对待素世的

这下我是真的没有觉得看《Ave Mujica》快乐过了。

在冲绳喜欢上的女孩方言讲得太过令人困扰

去年有部叫做《北海道辣妹贼拉可爱》(以下简称《北海道辣妹》)的冬季新番,试着追了两集,主要讲述因父母工作而从东京来到北海道上学的「京爷」邂逅本地辣妹的故事。可惜剧中的辣妹既不可爱,剧情也不过是老套的「党争」——京爷被一群辣妹包围,唯一的新鲜感不过是这群辣妹来自北海道。结果就是,《北海道辣妹》压根没吸引到我不说,反而让我对此类标题带地域标签的动画产生了条件反射式的排斥。于是在看到《在冲绳喜欢上的女孩方言讲得太过令人困扰》(以下简称《冲绳妹》)的时候,才直接给它打上了「烂片预定」的标签。幸亏忙里偷闲看了两集,才没有错过这部带有浓厚地域文化的恋爱喜剧。

虽然《冲绳妹》也是以东京男主角跟随父亲转学到小岛冲绳恋上本地黑皮妹而展开的故事,但和《北海道辣妹》有本质上的不同,《冲绳妹》的故事核心在于介绍冲绳独特的地方风俗和民族文化。诙谐的单元叙事结构加上可爱的双女主,通过角色日常生活的方方面面以及各种小细节,配合穿插的各种 neta,自然地融入那些冲绳本地人习以为常、但是外来人从没接触过的梗和笑料,使得这部「地方宣传片」在观感上极为舒适。

不过我最关注的当然还是恋爱。男主角「中村照秋」在开学第一天就被冲绳土著「喜屋武飞夏」搭话,阳光开朗的飞夏在他眼里闪闪发光,照秋因此对飞夏一见钟情。但照秋听不懂冲绳方言,飞夏也不会说标准日语。两难之际,飞夏的闺蜜「比嘉夏菜」及时站了出来,充当翻译将飞夏说的话转述给了照秋。此后每逢飞夏与照秋对话,夏菜都在场担任双方的「传话筒」。

其实早在男主初次来到冲绳的时候就遇见了夏菜,夏菜因为男主小小的帮助而对他暗生情愫,在见到照秋转学来冲绳更是高兴的不行。没错!我爱看的就是这种三角结构!照秋希望能多了解些冲绳文化好接近飞夏,大大咧咧的飞夏对此浑然不知,于是三角的重心就集中到了内敛的夏菜身上。那压抑不住的情感常常溢于言表,厚重的刘海和假装冷静的外表下遮掩不住的是只有男主才发现不了的炽热少女心。尤其是黝黑的皮肤透出的点点红晕,配合青春期少女娇羞的小心思,冲着这画面我能吃下三碗饭!

续集

其他续集就不知道怎么写了。这个一月还看了《超超超超超喜欢你的100个女朋友 第二季》,《我独自升级》两季,加上上面两部,总共看了4部新番。《100女友》可能没有第一季那么让人眼前一亮,却也是非常棒的后宫作品;《我独自升级》属于快节奏的装逼系列,因为 A1 制作给力,我甚至把漫画也看了个七七八八,可能没什么营养,但胜在快乐。

🔲 ☆

备用机的养成方法

备用机」,顾名思义,是指以备不时之需的手机。它可以是一部只能收发短信、拨打电话、玩玩贪吃蛇的诺基亚,也可以是陪你走过青春岁月的老朋友小米 6;它可以是功能齐全但电池健康度只剩 50%、Face ID 罢工的 iPhone X,也可以是一部崭新出厂、配备 1TB 存储的豪华版小米 15S Pro。

但又不可与「副机」混为一谈。有些人本就习惯双持,一部工作用,一部生活用,或者 iPhone 和 Android 各一部。这类情况当属日常配置,不能算作备用机的范畴。

如果你正好有一部闲置的手机,那它自然可以充当「备用机」的角色。可更多时候,备用机是自己或家人换下来的老旧机型。就比如我目前使用的是一部搭载火龙 888 处理器、由雷军亲手焊接的 迟迟没有烧掉 WiFi 的小米 11。

可假如你就只有一部手机,需要另一部手机备用,该怎么选择才合适呢?或者说,合格的备用机该具备哪些品质,才能担起「临危受命」的重任呢?

挑选

根据个人财力和需求上的不同,备用机的选择自然是五花八门。对我来说,最实用的备用机必须是一部能解锁 Bootloader 的安卓机器。因为解锁之后,它就可以做到:

  • 获取 ROOT 权限:任意安装各种各样功能强悍的应用,Magisk、Shizuku、Lsposed、GKD……
  • 掌控应用权限:比手机管家更激进的权限管控,再也不怕 APP 到处拉屎;
  • 精简系统应用:无用应用全部冻结,垃圾软件统统干掉。

甚至还能当一台 Always Online 的小型 Linux 终端。如果机身存储空间够大,把它当作影音服务器也不是不可能1。就是这么玩就有点太硬核了,毕竟不是所有人都知道「解锁」是个什么概念,也不是所有人都会这么压榨手机,更不会把心思都花在备用机上。

因此我们还是回到常规角度,从备用机的自身建设和基础功能等方面出发。我认为有以下几点:

  • 稳定

    备用机,说白了就是需要的时候能派上用场,其余的时间都在隐秘的角落独自落泪。因此哪怕不是最新最强,也得是台「关键时刻不掉链子」的机器:能正常开机、联网,不会间歇性重启,就已经赢了一半;若是有还算能看的流畅度,那就赢下了另一半。

    如果还能玩玩原神星铁,那就可以顺理成章地坐上副机的宝座。

  • 续航

    虽然大部分时间都躺在抽屉里吃灰,但真到要用的时候,总不能刚点亮屏幕就提醒你「电量不足,还有 30S 关机」吧?因此电池健康度方面最好别太离谱。

    可话又说回来,如果你跟我一样,愿意让备用机全年 365 天 24 小时插着电,那电池拉胯也就不是什么问题了。我的小米 11 尽管电池健康已经跌到 60%,日常使用都力不从心,可它早已退居二线,干脆当成「插电设备」使,也算是变废为宝,顺便还能省下一笔换电池的钱。

  • 便宜

    毕竟是备用机,不追求极致体验,但求在预算范围内堪用。

    但如果你的预算高到了一定程度,稳定和续航就不是问题了。所谓「三千预算进卡吧,加钱加到九万八」,在手机这边也是相同的道理。

显然,功能机在这方面有天生的优势,但只能打电话发短信的话,这备用机未免太鸡肋了点。另外,iPhone 也不作详细推荐。虽然每一代 iPhone 都具备成为优秀备用机的潜力,但其选择逻辑和安卓不尽相同。简单来说,如果需要双卡双待,选择 iPhone 11 或更新的 国行 版本;如果偏爱小屏,iPhone 13 mini 是个不错的选择。再加上 iOS 系统的封闭性,其可定制和折腾的空间相对较小,iPhone 的使用寿命也比 Android 要长得多。因此,本文的备用机选择主要还是针对安卓设备展开。

所以,在处理器方面,尽量避开某些采用高发热处理器的设备,例如 骁龙 888 和 骁龙 8 Gen 1。因此,可以优先考虑 2022 年之后发布、且搭载了后续型号处理器的手机。如果考虑更早发布的旧款型号,骁龙 865 是个不错的选择;假如预算更低,骁龙 845 也可以考虑,但性能和功耗表现会逊色一些。此外,联发科近两年的天玑系列,如天玑 8100、天玑 9000 等,也是公认的「口碑之选」。

对于操作系统,国内安卓设备无非就是 HMOV。是选择华为、小米、OPPO 还是 vivo,主要取决于个人偏好。根据我的 个人经验,华为(包括荣耀)的内置广告和预装软件最多,OPPO(我只用过一加)也有不少广告,且不支持关闭;小米虽然系统广告大多可以手动关闭,但其 HyperOS(或 MIUI)有时会遇到一些难以预料的小问题。

至于屏幕、摄像头、存储容量等等就看个人需求了。实在拿不定主意,再买一台和主力机一模一样的机器也不是不行。

养成

OK。假设你已经按照上面的思路,挑选到了一台心仪的备用机。但这还只是开始,接下来该才是本文的重点:备用机的养成

关闭系统更新

就像 Windows 会在不经意间自动更新你的电脑,手机也会悄悄地自动更新系统。并且无论是全新设备还是二手机器,系统更新带来的不一定都是正向优化,尤其是跨 Android 版本的系统更新。因此,新到手的设备要做的第一步,便是关闭系统自动更新。

除了关闭自动更新,还可以在开发者选项里彻底关闭【系统自动更新】的后台服务,以绝后患。

当然,已经退出系统维护名单的设备不存在这方面的烦恼。

卸载无用程序

不像 iPhone 预装的都是果子自家的 APP,安卓设备都会预装一些不管你用不用得上的第三方 APP。这些 APP 如果从来没打开过还好,一旦启动,即便只是失手点开从未浏览过任何内容,它也会孜孜不倦地向你推送通知——大多数都是垃圾消息。

所以这第二步,就是一定及时要卸载手机中不使用的 APP。

例如,各家都可能预装的微信支付宝抖音,如果不使用的话请及时卸载。

ColorOS 预装
ColorOS 预装

锁屏画报也卸载掉,没什么大用还费电。

输入法可能预装了不止一种,可能会同时预装搜狗、百度乃至讯飞。选择你习惯使用的,其余的都卸载掉。

地图也可能预装了不止一个。想来备用机也没有查地图的必要,择一或全部卸载。

若是既无法卸载也不能停用,那么只能可以考虑禁用相关权限了。

关闭系统广告

卸载完 APP 还不够,系统内置的广告和内容推荐才是养成的大头。

系统个性化广告/推荐:在系统设置里搜索「广告」或「推荐」,找到「个性化广告服务」、「用户体验计划」、「智能服务」等选项,全部关闭。

小米支持一键关闭部分系统广告。

ColorOS 只支持关闭跟踪标识,并不支持关闭广告。

天气/日历/下载管理器/手机管家:进入这些 APP 的设置,关闭里面的「内容推广」或「信息流」选项。

ColorOS 需要在各 APP 的「隐私」设置中关闭推荐。

小米的广告开关则在「用户体验计划」或「信息流」中。

负一屏(智能助手):负一屏也是广告重灾区。如果用得到,就只保留需要的功能;如果不用,可以直接关闭这个功能。

小米
小米
一加
一加

文件夹推荐:部分系统会在桌面文件夹里推荐应用,需要长按文件夹,关闭「今日推荐」。

小米
小米

应用商店:进入手机自带的应用商店或软件中心,在「设置」里关闭所有的「消息推送」和「内容推荐」。

ColorOS
ColorOS
ColorOS
ColorOS
可惜应用商店不能卸载也不能停用,不然卸载了之最为妥当。然后装个酷安。至少酷安不会推送广告。

浏览器:系统自带浏览器也是广告重灾区,一般不可禁用、卸载,但可以考虑禁用通知或联网权限,并用其他第三方浏览器替代。

限制应用权限

如果某些 APP 并不经常使用,那么建议限制这些应用的部分权限。

例如:

  • 自启动:除了关闭自启动,更要管理好「关联启动」,防止「一人得道,全家升天」。
  • 后台活动:进入每个应用的「电池」或「电量」设置,将不常用的 APP 后台活动设置为「禁止」或「严格限制」。
  • 网络权限:对于某些单机应用或你不想让它联网的应用,可以在设置中关闭其使用移动网络和 WLAN 的权限。
  • 通知权限:除了禁用通知,还可以利用系统的「通知分类」功能,只保留最核心的通知。

优化电池续航

  • 关闭非必要连接:关闭蓝牙、NFC、定位服务(GPS)、用户体验计划等。需要时再手动开启。
  • 关闭息屏显示(AOD):除非有特殊用途,否则建议关闭息屏显示。

进阶

理论上来说,上面这些操作已经足够提升备用机的使用体验了。但假如你愿意再深入调教一番,下面这些小工具能让你的备用机体验更上一层楼。

高级权限

如果你的备用机可以很方便地解锁 BL,那么相应地获取 root 权限也便非常简单。但假如是不能解锁 BL 的机器,则可以考虑通过其他方式获取稍微高级一些的 ADB(Android Debug Bridge)权限,比如使用 Shizuku

我是用root权限启动的Shizuku
我是用root权限启动的Shizuku

Shizuku 在通过 ADB 获得权限后,允许其他程序通过 Shizuku 便捷地调用系统 API,从而实现更强大的功能。

安装 Shizuku 非常简单,Android 11 及以上版本通过无线调试便可直接启动,无需连接电脑。唯一不足的是由于系统限制,每次手机启动后都需要再次进行启动步骤。

  • 开启开发者模式

    打开设置,找到「关于本机」,点击「版本信息」,连续点击「版本号」7 次,开启「开发者选项」。

  • 启用无线调试

    找到「开发者选项」,并开启「USB 调试」、「无线调试」。无线调试需要连接到 WLAN 后方可使用。

  • 在无线调试中点击「使用配对码配对设备」。

  • 随后在 Shizuku 中按照步骤配对并启动应用。

就可以使用 Shizuku 了。

冻结应用

安装过 Shizuku 后,就可以方便地使用 冰箱 的冻结功能。

冰箱可以通过 Shizuku 获取提升权限,从而冻结任意软件,包括系统软件。就比如上面提到的锁屏画报,或是系统浏览器,甚至是系统自带的视频、音乐等等你用不到的不可卸载程序,都可以冻结。

但万万不可冻结应用商店、应用安装器,可能会导致手机无法开机。建议在冻结之前,使用「机型 + 冰箱 + 冻结」等关键词在酷安搜索前人的经验。

短信转发

如果你的备用机插了电话卡,电话卡绑定了某些 APP,那么使用 短信转发器 可以方便地将验证码自动转发到你的主力机上。

安装短信转发器后,按照操作流程授予读取短信、通知类短信权限,关闭验证码保护,并设置自启动,禁用电池限制。选择一个合适的发送通道(比如 iOS 上推荐使用 bark),再设置好转发规则,测试成功后,就可以彻底将备用机丢进抽屉里了!

你可以参考少数派上《巧用开源方案,零成本实现验证码短信转发》关于短信转发器的详细说明。

最后

到这里,你的备用机应该可以彻底焕发新生,重回第二春了。至于使用 GKD 跳过广告、通知滤盒 收纳无用通知、R-安装组件 替换系统自带安装器、Lspatch 安装内嵌模块的应用等等备用机上可能用不太到的东西,这里就不细说了。诸位有兴趣可以自行搜索尝试。

写着写着越发感觉偏离主题,这哪是备用机养成,分明就是主力机的优化指南嘛!再说,都叫备用机了,买回来往抽屉里一丢,也不是不行……

嘛,说到底,折腾备用机的乐趣,或许并不在于结果,而在于「养成」本身。毕竟这么一番折腾下来,不觉得很酷吗?作为一名理工男我觉得这太酷了,很符合我对未来生活的想象,科技并带着趣味。


  1. 之前有在酷安看到安装 Terminal APP 使用 Linux 环境的帖子,但是这次搜索的时候,看到《Trying Out the New Android Linux Terminal》这篇提到,Google 已经在 Android 15 上提供了原生的 Linux 开发环境。不过似乎仅限 Pixel 用户尝鲜。
🔲 ☆

2024 下半年新番吐槽

最近都看了些什么新番呢?在工位上摸鱼的时候,我突然想起了这件事。

翻了翻新番列表,好像已经有很长一阵子没能花上一整个下午的时间,专心致志地观看一部动画了。掰着指头数了数,大概是来这里牛马之后,就不再有过。每天都在上班,下班晚饭过后的一点点时间,都不够用来刷沙雕图、打游戏、熬夜。即便时间(没有什么事情要做)、空间(找到一个舒服的看番姿势)、番剧(确实有现在就想看的番剧)俱备,足够我正儿八经地看一集新番。可下一秒拿起手机,就放不下了。

所以挤出时间来——准确地说是全神贯注地看完一集 24 分钟的动画,是不太可能的。

可是,再不看动画片的话,感觉自己都快要越过那道门槛,转职成为正统的魔法使了。等到了那时,就真的没啥时间看这些「小孩子家家」的东西了。我甚至能想象得出,当我胡子拉碴、呆毛丛生、津津有味地享受一部后宫番的时候,路过的同事 / 长辈 / 路人甲会是什么样的反应。他们可能只是好奇地看了一眼我的屏幕,随即投来惊讶的目光;语气可能不会太过鄙夷,但一定会说出「你竟然还在看动画片?」这句话。我大概率会无所谓地回复「是啊,怎么了?」,然后继续对着屏幕傻笑。也可能会羞愧难当地关掉动画片,转而刷起短视频。

搞不好我还会因此开启无职的异世界生活。

总而言之,为了自己定下的目标 1,为了不让这个系列就此终结,多少还是花了些时间看番。可随之而来的是另一个问题:这下半年能看的新番,似乎有点太少了。

比如作画炸裂的《擅长逃跑的殿下》,剧情和风格上没能支撑我继续追下去,看了一集便搁置了,即便它是由创造出《更衣人偶坠入爱河》和《孤独摇滚!》的梅原 P 企划的。再如讨论度极高的《不时轻声地以俄语遮羞的邻座艾莉同学》,若不是一开始有腹黑的妹妹,我也不可能看得下去六集。后期不仅妹妹变得没意思了,剧情还离谱得让人大跌眼镜,简直是在摧残我所剩无几的智商,幸亏忙得没时间看及时止损。最让人惋惜的是《小市民系列》,本该最让人有所期待的剧情,做着做着就只剩下 2.39:1 的画面还称得上是「电影感」。至于《【我推的孩子】 第二季》,我想找个空闲的时间一口气看完,《物语系列 外传季&怪物季》则是前几部没补完没法续上剧情看不成。因此,7 月番就只看完了《败犬女主太多了!》、《义妹生活》以及《魔法少女与邪恶曾经敌对。》。

我遂将希望寄托在 10 月,可亦是一言难尽。疯房子不知道发什么疯,在《地。 ―关于地球的运动―》里大搞闪光弹偷袭,还算优秀的原作遇上脑子有洞的改编也是倒霉;《青之箱》只有霸权社制作的动画 OP 以及前 6 集还有点看头,一到真枪实弹的运动场景,作画便崩坏得难以下咽。更何况大多分镜几乎照搬漫画,算不上是优秀的改编。最终,也只看完了《胆大党》和《悲喜渔生》。 如果把《英雄联盟:双城之战 第二季》也算上的话,倒是和 7 月持平。但《双城之战2》除了画面,剧情深度比第一季要浅得多,远不如第一季好看。

那就不难下结论了:7 月有话题度的原创太少,10 月续作太多,能看的异世界又趋于零,下半年除去续作,就是没啥好看的新番。

两个季度的量加起来还不及 4 月看过的新番的一半,我只能把 2013 年的《冰菓》,2017 年的《游戏人生 零》,2021 年的《剃须。然后捡到女高中生。》和《漂流少年》翻出来补上一补。

错的不是我,是这个世界。

没有素材,我怎么可能写得出追番报告?所以,我把这些碎碎念搬了上来,加起来足足有一千个字!(啊不是)

咳咳,言归正传,让我们开始这一期的新番吐槽吧!

败犬女主太多了!

「败犬」一词,通常用于多女主角的恋爱喜剧中,指代那些未能赢得男主角青睐、却又故作坚强的女性角色。这类角色往往在主角感情确立后逐渐退居二线,直至成为成长回忆的一部分。但从角色塑造来看,「败犬们」大多具备较鲜明的人物特征,情感爆发也比女主角更激烈、更显戏剧化,只是受限于既定的结局走向而被迫退场——正因如此,不少作品中的败犬角色比女主角更受观众喜爱。我永远喜欢泽村·斯潘塞·英梨梨。

《败犬女主太多了!》的革新,恰恰就在于颠覆了这一传统。它不再执着于讲述「成功人士」的青春,而是将「败犬」置于舞台中央,将叙事权交还给这些「落败者」。由此,我们才得以看到这些性格各异却又充满感染力的角色,以及失败的她们所展现出来的青春:运动系的烧盐柠檬表现出青春期特有的微妙距离感,内向文学少女小鞠知花则细腻地展现了憧憬与现实错位后的自我修复过程。至于外向活泼的八奈见杏菜,她的恋情虽然略显「草率」,却也在分享喜好与情感的过程中,展现了青春期直率而热烈的一面。

本作另一巧妙之处,在于男主角温水和彦这一「背景系」角色的独特视角。某种意义上,他的存在使本作更接近群像剧的结构,而非标准的恋爱胜负游戏。

温水几乎不主动处理角色间的矛盾纠纷,却又不会拒绝参与这些事端。这种若即若离的态度使他与角色们保持了一定距离,却也因此达成了「理想的倾诉对象」的必要条件。他不仅为每位女主留下足够的表达空间,更让她们得以按照自己的节奏修复情感创伤。即便他声称自己不会被青春期阴晴不定的恋爱关系扰乱内心,却仍无可救药地成为了「恋爱」的局中人。

可以说,《败犬女主太多了!》并没有真正突破青春恋爱番的叙事边界,但它尝试以「败犬」作为引子,呈现出更多元的青春视角。在胜者逻辑横行、异世界题材层出不穷的当下,这种平静却充满自省的校园叙事,反而为青春题材注入了难得的真实与珍贵。

义妹生活

但凡看过一些稍微正常的动画,都会在第一眼见到《义妹生活》的时候就把它划分到「便宜动画」的阵营——色彩、场景乃至分镜都透露着一股穷酸味,你很难在这部动画中见到一丁点显经费的地方。就连本作唯一显眼的画风精美的人设,绝大多数时候都是用远机位拍摄人物全身,几乎不存在细节特写。看看隔壁《艾莉同学》,几乎是把能展示角色魅力的招数全都使了出来。

可就是这样一部动画,在这等有限的制作资源的前提下,通过大量值得推敲的演出、细腻而富有深意的叙事结构,展现出与作画严重不符的高水平制作。

偷感十足的拍摄机位
偷感十足的拍摄机位

可能是能截得到的为数不多女主的精美特写
可能是能截得到的为数不多女主的精美特写

空旷的房间和昏暗的光线,是从环境上侧写角色的心理;频繁出现的远景以及奇怪的机位,除了省钱外,也能很好地展示角色的内心活动;就连多到数不清的长镜头,也是用以突出角色的沉默。这直接使得本作的画面虽然廉价,却在整体视觉反馈上表现出非凡的「电影感」。

背景可以说是能糊弄就尽量糊弄,有就行
背景可以说是能糊弄就尽量糊弄,有就行

把光影用到极致了的日记回
把光影用到极致了的日记回

除了摄影,本作的音乐和配音方面也同样表现出色,配音演员的表现亦为角色增添了层次感 。可以说正是因为演出和音乐的互相配合,才得以让贫瘠的画面表现没那么惹眼,使得观众更能沉浸到故事中去。

不知道是什么原因,动画 OST 迟迟没有公布,极有可能会和第二季一样成为有生之年。不过在 B站 找到了 BD 附带的三首 OST:https://www.bilibili.com/video/BV1EKcteqEGX

不过很遗憾,我仍旧不擅长从制作、摄影等更深层次的角度对作品做阅读理解,因此只能写到这里了。如果你也对这部动画感兴趣的话,可以在看过动画之后,浏览下面这些文章或视频,加深对动画的理解。

bangumi 番组计划:

哔哩哔哩:

至于要不要因此去看原作小说,这个就没法建议了。毕竟日轻和动画是两种载体,本作原作小说的 评分 到后期也在逐渐走低……

魔法少女与邪恶曾经敌对。

《魔法少女与邪恶曾经敌对。》其实算不上多么优秀的作品,但其本身承载的重量,使得这部由十年前的漫画而改编的番剧变得「不那么一般」。

作为 藤原可可亚 的遗作,漫画连载至第 20 话时,因作者病逝而戛然而止。彼时,米拉与白夜之间的关系刚从「敌人」悄然转向「朋友」,魔法少女与邪恶组织的背景也才揭开冰山的一角。邪恶组织的成员看似荒诞却各有伏笔,魔法少女阵营也开始露出不那么光鲜的另一面。这些设定和角色还远未被充分展开,便因为作者的离世永远停在了起点上。

这像是一个永远说不完的故事,你隐约知道后面可能会发生些什么,却再也看不到、也听不到那一句真正从角色口中说出的「我喜欢你」。

这份遗憾,就像片头曲的歌词所写的那样,「保持尚未完结就好,这份情感无需谢幕」。也许作品的动画化,就是对作者最好的追思吧。

胆大党

《胆大党》是少年 Jump 王道漫画的一大代表,因此依旧逃脱不了这类漫画的基本要素:「友情」、「热血」和「胜利」。但是,本作由汤浅政明创立的 サイエンスSARU 制作,尽管其本人已退休,但他的个人风格明显已经成为该工作室的视觉语言之一。原作中外星人与超能力的设定,经由那种夸张变形的动作表现与强烈的节奏感,两者的融合带来了远超原作的视听张力,使这部作品在延续王道精神的同时,也展现出动画作为独立媒介的表现优势。

回想起了乒乓
回想起了乒乓

即便你对这类传统 Jump 套路已感疲乏,本作依然可以凭借其独特的演出风格与节奏控制,重新唤起你对这类作品的观看兴趣。

第一集的战斗部分十分精彩
第一集的战斗部分十分精彩

悲喜渔生

在《悲喜渔生》中你可以看到:

  • 让你恨得咬牙切齿的低能男主角
  • 一群热心肠钓鱼佬2
  • 空军
  • 六十年历史背书

《悲喜渔生》带给了我太多出乎意料的惊喜,即便它的 OP 带电,即便男主的性格惹人生厌,即便它的故事并不惊艳,它仍旧是这个10月最值得一看的原创番剧。

从钓具的挑选、下窝的策略,到抛线的时机、收杆的节奏,与钓鱼相关的每一个细节都被刻画得细腻真实。而配乐更是《悲喜渔生》的灵魂——伴随着抑扬顿挫的旋律,钓鱼的节奏与情绪被巧妙烘托,即使是从未垂钓过的人,也能凭借音乐感受到那种或舒缓、或紧张、或充满期待的情绪波动,进而不知不觉沉浸于故事之中。以至于身为观众的我,也有想要放下一切负担,独自一人乘着海风随心所欲地钓上一杆的冲动。

但钓鱼之外,是作品对「钓鱼即是人生」这一主旨的始终贯彻。在《悲喜渔生》中,钓鱼不仅是角色们的共同爱好,更是一种生活方式。男主常宏原本对钓鱼毫无兴趣,甚至感到厌烦,但在与便利店同伴们的相处中,他逐渐体会到钓鱼的魅力。钓鱼需要耐心、技巧和对自然的理解,这与他之前急于求成、追求快速成功的人生态度形成鲜明对比。钓鱼的过程充满了不确定性,正如人生的旅程。有时辛苦一整天也未必能有所收获,但每一次出钓都是一次新的尝试和希望。通过钓鱼,常宏学会了放慢脚步,享受过程,重新审视自己的生活方式,在自己做出转变的同时,也解开了贵明积压已久的心病。这种对过程的重视和对结果的淡然,大概就是《悲喜渔生》想要传达的人生态度吧。


  1. 在写第一篇新番吐槽的时候,曾暗自定下「每季度完结后写一篇吐槽练练文笔」的小目标。
  2. 喜欢钓鱼的都不会是坏人
🔲 ☆

一加 Ace5 Pro 快速上手及刷机小记

随着小米逐渐收紧手机 Bootloader 解锁政策,一加手机便成为了可能是 国内 最适合刷机爱好者的安卓设备:解锁不设限、Root 后在一定范围内支持保修。我能理解厂商封堵刷机的考量,如今的安卓生态早已告别那个功能匮乏、需要靠刷机来弥补的时代,再加上深度定制的 UI 系统和基于硬件的定制化功能,都让第三方 ROM 的生存空间日益萎缩。当刷机变得不再必要,刷机文化式微是必然的趋势。但安卓的魅力,始终来源于它的开放性,我还清楚地记得在小米 5 上实现三系统共存时那种纯粹的快乐——即便这并没有什么意义。

在因骁龙 888 的拉胯而从小米 11 转向 iPhone 之后,我已经有 4 年没接触过刷机了。这次有幸拿到一部一加 Ace5 Pro,机主( おとうと) 拜托我帮忙解锁并隐藏 Root,我得以借此再过一把刷机的瘾。

本文是机器快速上手及刷机过程的简要记录。

关于手机

一加 Ace5 Pro 发布于 2024 年 12 月 26 日,搭载当下最新的旗舰芯片 高通骁龙 8 至尊版( Snapdragon 8 Elite),配备 6.78 英寸的直面屏和 6100mAh 大电池,整机仅重 203g,和我手上的 iPhone 比较,轻得相当明显。

既然定位电竞手机,这样的配置倒还算中规中矩。我手上这台是 12GB+256GB 的内存和存储,只是打游戏的话还算够用,即便再安装个微信也不在话下。但要拿微信办公的话得另当别论。

后置三摄采用浴霸式排布,最高像素 5000 万。随手在昏暗的室内拍了一张,感觉色彩比我的 iPhone 要鲜艳一些。

经WebP压缩
经WebP压缩

拍照水平太次,看不出所以然来。

不过话说回来,可能是我太久没关注安卓市场,再加上之前基本只接触过小米的缘故,对其他品牌的产品线了解相当有限。我只能顺着「小米数字系列是高端旗舰,Redmi K 系列是中高端旗舰」的逻辑,猜想只划分为数字和 Ace 两条产品线、价格区间也近似的一加,定位应该也差不多。可是查询参数时才惊讶地发现,搭载相同 CPU 的一加 Ace5 Pro 居然比一加 13 的安兔兔跑分高了三万多。只能归结于晚发布的 Ace5 Pro 在芯片调教上要比老前辈更成熟一些。

关于系统

现在的一加预装的是和母公司 OPPO 一样的 ColorOS,我这台一加 Ace5 Pro 搭载的是最新的 ColorOS 15。这是我第一次使用 ColorOS,新鲜感虽谈不上十足,但也有些好奇心在。

桌面中规中矩,看不出明显的差异,至少和我印象中的 氢 OS 不太相似,毕竟氢壁纸在当时可算是独一档的设计。(我这是什么老古董)

毕竟现在的系统大都趋于雷同,做得太复杂是需要额外的上手成本的。

所以我直奔系统预装应用,倒要看看是不是只有小米才会预装一堆垃圾。

果不其然,大家都是半斤八两。

可当我尝试批量卸载这些应用时,才注意到无法直接在 抽屉模式 下批量卸载它们,提示仅能移除图标。

小米不是这样教我的诶,我记得把这些图标往顶栏拖动,是有一个按钮可以直接卸载的。

翻了翻桌面设置,才注意到 ColorOS 的桌面只有在 标准模式 下,才能批量卸载应用。

抽屉模式下,需要进入应用抽屉,点击右上角「管理」,勾选应用后,才可以批量卸载。

奇怪的逻辑。

预装应用如此,广告尤甚。ColorOS 锁屏默认使用「乐划锁屏」推送垃圾新闻、短视频和广告不说,系统自带应用还有无法关闭的广告。尤其是自带浏览器。浏览器不仅有开屏广告,主页还充满了各种各样低俗的广告。网页无法打开时还会推送低俗新闻,锁屏后重新打开浏览器还会弹一次开屏广告。即便能切换成极简模式,搜索框中也无时无刻不在滚动着广告推送。

乐划锁屏倒是能关,可这浏览器怎么都找不到关闭广告的入口。小米被骂了一顿之后老实了不少,系统广告大部分支持关闭。但一加我 简单 翻了翻,没有看到明显的关闭广告的选项。顶多一刀切关闭通知,或者卸载了之。

不过在试着整理桌面时,意外地发现 ColorOS 的应用文件夹居然支持自由调整大小。不仅可以横向/竖向拉伸,还能变成长条形、方形等不同布局。如果 iOS 能把这文件夹调整方式抄了去的话,我的桌面也能变得好看一些。

对比小米的设计,ColorOS 的处理方法明显更灵活,实际使用起来也顺手得多。

还发现了一个叫做「一加互传」的功能,写着支持 iPhone。我以为会是使用相同协议无感传输的那种,结果还是需要在 iPhone 端安装「O+互联」APP 才能互相传输文件。那和两台设备都安装一个互传应用并没有什么实质上的区别,只是一加自带了这个互传 APP 而已。

还有其他基于机载 AI 的功能,因为需要登录欢太账户,没能体验;另外骁龙 8 至尊版的游戏性能,因为只是解锁刷机,也没有深度测试一番。

最后是把小米 11 刷成 ColorOS 体验了番 AI……

关于刷机

一加解锁 Bootloader 不像小米需要下载专用的解锁工具,直接在「开发者选项」中允许「OEM 解锁」,再重启至 Fastboot 模式,然后使用 fastboot 命令,即可完成解锁,非常简单。

至于获取 Root 权限,我还停留在使用 Magisk 获取 Root 的古早思维,不知道现在是不是有什么新的方式。网上搜索了一圈,都推荐使用 KernelSU 获取 Root 权限。简单翻阅了下官方文档,似乎使用 LKM 模式 修补官方 init_boot.img 镜像的方式,和为 Magisk 修补 boot.img 镜像差不多。按部就班实操了一遍,成功获取到了 Root 权限。

刷机有风险,请做好救砖的心理准备!!!

准备

在开始之前,需要准备以下工具:

  • ADB Tool:用以执行 adb 和 fastboot 指令,传送门
  • Fastboot 驱动:解决 fastboot 指令不生效的问题,传送门
  • payload-dumper-go:提取原厂系统安装包中的 init_boot.img,用以后续修补 KernelSU,传送门
  • KernelSU 管理器:管理 Root 权限,传送门

解锁

首先,打开设置,下拉至「关于本机」,点击「版本信息」。接着连续点击顶部「版本号」7 次,开启「开发者选项」。

返回设置。下拉至「系统与更新」,点击「开发者选项」,找到「OEM 解锁」并开启,以允许解锁引导加载程序;开启「USB 调试」,以便后续连接电脑执行解锁命令。

将手机通过 原装数据线 与电脑相连接。接着,打开 我的刷机传家宝 ADB Tool,双击 打开CMD命令行.bat ,弹出终端窗口后输入:

adb devices

此时手机应当会弹出 USB 调试 授权,勾选 一律允许 并确认即可。

终端中会输出 List of devices attached 列出当前 adb 设备。

随后,再执行:

adb reboot bootloader

等待手机重启至 Bootloader 模式。

一加的 Bootloader 模式,屏幕左上角有一个显眼的 START 提示,下方以英文标注:

Press volume key to select, and press power key to select.

意为「使用音量按键选择,按下电源按键确认选择」。

以红字 DEVICE STATE - locked 提示设备已上锁。

完整示例如下,仅供参考:

-----------------------------------
START
-----------------------------------

Press volume key to select, and press power key to select.

FastBoot Mode
PRODUCE_NAME - sun
VARIANT - SMB UFS
BOOTLOADER VERSION -
BASEBAND VERSION -
SERIAL NUMBER - abcedf
SECURE BOOT - yes
DEVICE STATE - locked

在 adb 工具中输入:

fastboot devices

如果正常如下图输出设备代号,即可继续执行解锁命令。

如果没有任何输出,请安装 驱动,或是检查线材、数据线与手机 PC 的连接状态。

总之,在 fastboot devices 命令有正常输出后,继续在终端中输入解锁命令:

fastboot flashing unlock

此时终端中应当会提示:

D:\ADB Tool>fastboot flashing unlock
OKAY [  0.015s]
Finished. Total time: 0.016s

而手机上会出现解锁相关的警告。按 音量下键 选中 UNLOCK THE BOOTLOADER,再按下 电源键 确认解锁。

解锁将自动清空所有数据,并自动重启至开机引导模式。

修补

根据 KernelSU 官方文档 的说明,需要先检查设备是否支持使用 KernelSU。下载并安装 KernelSU 管理器后,打开 KernelSU。

  • 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU,你需要自己编译设备的内核才能使用,KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
  • 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。

咱的设备当然是支持的,估计是一些不常见的古董才会不被 KernelSU 支持。

接着,下载官方原版系统更新包。因为不知道从哪里可以获取到完整包,这里使用的是 大侠阿木 提供的全量更新包 度盘链接

这里我使用的是 PKR110_15.0.0.800(CN01) 版本,度盘中还提供了数个官方下载链接,如果没有度盘会员可以直接使用官方链接下载。

官方链接官方链接官方链接官方链接官方链接

官方链接官方链接官方链接官方链接官方链接

接着,解压安装包,获取 payload.bin 文件。

随后,下载 payload-dumper-go,并解压 payload-dumper-go_x.x.x_windows_amd64.zip,打开该文件夹,将上面获取到的 payload.bin 文件移动至该文件夹中。

接着在 该文件夹 空白处右键,选择「在终端中打开」。直接使用以下命令,可以省去将该应用添加到环境变量的步骤:

.\payload-dumper-go.exe payload.bin

由于一般只用得到 boot.imginit_boot.img ,可以不必等待全部跑完流程,看到这俩文件被提取出来就可以按 CTRL+C 取消进程,关闭窗口。

本来准备试试 一加全能工具箱,但打开就提示「终端设置异常」,即便恢复为「Windows 控制台主机」也无济于事。想了想也就敲几个命令的事,就没再继续修复环境异常,可能是 Win11 下这个工具水土不服吧。

init_boot.img 拷贝至手机中,打开 KernelSU 管理器,点击顶部左边第一个按钮,选择修补本地 init_boot.img

KernelSU 会将修补过的镜像文件保存在原镜像同路径,命名格式为 kernelsu_patched_xx.img,拷贝至电脑备用。

我这台机子目前的系统版本号为 PKR110_15.0.0.800(CN01),如果你的版本号和我一致,可以直接使用我修补好的镜像。传送门

但仍注意备份数据,并做好随时救砖的思想工作!!!!

Root

解锁完机子,修补好镜像,Root 就很简单了。先开启「USB 调试」并使用 ADB Tool 确认连接正常后,重启至 fastboot 模式:

adb reboot bootloader

测试 fastboot 连接,确保有正常输出:

fastboot devices

最后,将修补过的 kernelsu_patched_xx.img 使用以下命令刷入系统。

fastboot flash init_boot kernelsu_patched_xx.img

你可以参考下图将文件拖动至工具中,不用手敲文件路径。

提示刷入完成后,再使用 fastboot reboot 命令重启系统。

即可在 KernelSU 中查看到 Root 状态。

隐藏

本来是计划把隐藏的方法完整记录下来的,奈何老弟整活把手机搞到无限重启,只能双清恢复出厂设置。我没有及时保存截图,一切努力付诸东流……

关于 KernelSU 的用法以及隐藏 Root 的方法,这里就不赘述了。可以尝试使用 KernelSU 自带的功能对特定应用还原环境,或是在安装 ZygiskNext 模块后,使用 LSPosed 框架配合 隐藏应用列表 隐藏特定应用。如果仍被检测到 Root,或是提示对系统已被改动,可以尝试安装 PlayIntegrityFixTrickyStore 模块,一般就能摆脱嫌疑。

回锁

老弟发现这机子内核版本太高,不适合他搞小动作,准备退货。所以我辛辛苦苦解锁完、折腾完 Root 隐藏,还得全部清空给它锁回去……

上锁的话,需要先在 KernelSU 中选择「永久卸载」 移除 Root 权限和所有模块,还原原厂镜像。

保险起见,再使用完整包更新一遍系统,确保所有文件都恢复为原厂文件。

然后按照解锁的方式进入 fastboot 模式,使用以下命令回锁:

fastboot flashing lock

手机上会弹出提示,选择锁定菜单即可。

重启手机后,如果开机界面没有出现设备已解锁的提示,就说明回锁成功了。

但看说这个操作非常危险,有概率使设备 变砖,我只是运气好才一次成功。大家千万不要没事干解锁回锁反复横跳……

关于救机

如果因为瞎折腾导致无限重启什么的,可以尝试使用 电源键+音量减键进入 Recovery 模式,尝试清除系统数据,看看能不能再次回到开机引导界面……

文章参考

文中提到的所有应用和工具:

解锁和 Root 方面的参考:

🔲 ⭐

使用 Steam Deck 游玩《绝区零》

在购买一台 Steam Deck 之前,我做过一番思想斗争。我认真思考了应该玩些什么类型的游戏,才值得购买一台售价与普通 PC 相当的游戏掌机。

一个是通过 Steam Deck 兼容性验证的 3A 大作。例如《巫师3:狂猎》、《赛博朋克2077》、《艾尔登法环》。这类游戏玩家众多,厂商有针对 Steam Deck 进行额外的优化。尽管画质上有些牺牲,综合体验应该不赖。只是,这类游戏可能还是更适合在大屏幕下游玩(不如说所有游戏都适合在大屏幕才是)。因此,这算不上是值得购买 Steam Deck 的理由。

另一个是精品的独立游戏、小众游戏。像是《空洞骑士》、《奥日》、《战斗方块剧场》,或者是《女神异闻录5》。这些非常适合使用手柄操控的游戏,在 Steam Deck 的性能下,体验肯定完胜 Nintendo Switch。这似乎能支撑我购买 Steam Deck。

再一个则是一些未上架 Steam 的 Windows 游戏。虽说基于 Arch Linux 的 SteamOS 能否畅玩 Windows 游戏还有待验证,但在刷了不少 Proton 转译运行 Windows 游戏的视频后,这个顾虑被立即打消。只要不触及无法在 Linux 底层下运行的游戏,以及那些反作弊严苛的网络游戏, Steam Deck 似乎能成为绝佳的掌上游戏设备。

如此看来,Steam Deck 非常适合用来推 GalGame。

可光是推 GalGame 的话,Windows 系统 + 可触控的大屏幕的 Windows 平板岂不是更好?也许应该买巨硬的 Surface?但用平板玩 Gal 一点也不酷,甚至没有素材用来水文。光是写怎么折腾 SD 的系统都够我达成年度 KPI 了。所以,我必须要找到另一个能够支撑我购买 Steam Deck 的强有力( mò xū yǒu)的理由。

下面记录一下在 Steam Deck 上安装《绝区零》的相关流程。

准备工作

  1. 一台 Steam Deck(这个必须有
  2. Type C 接口的 USB扩展坞(可以没有,纯触摸屏操作也行)
  3. 米哈游启动器安装包(可以没有,稍后在浏览器内下载也行)
  4. 《绝区零》游戏本体(可以没有,稍后在启动器内下载也行)

进入桌面

通过 STEAM 按钮,或在 STEAM 菜单中选择「电源」,选择「切换至桌面」,打开 KDE Plasma 桌面环境。

可以参考本文进行一些 Steam Deck 的预设配置。

安装游戏

目前米家登陆 PC 平台的游戏,即便可以直接启动,后续也需要通过 米哈游启动器 方可更新,因此需要先安装启动器,再下载游戏。

由于在 SteamOS 中添加的每一个 Windows 应用都有属于自己的虚拟的独立空间不能投机取巧直接将 PC 端的启动器文件直接复制粘贴到 Steam Deck 中,否则会影响后续游戏大版本更新。因此,请按部就班地安装启动器。

安装启动器

如果已经安装了浏览器,那么打开浏览器,访问 https://launcher.mihoyo.com/ 下载米哈游启动器。

如果还没有安装浏览器,那么在开始菜单中,选择系统一栏,打开 Discover 软件管理中心。

搜索 Chrome 并安装。

接着,在浏览器中搜索米哈游启动器,下载安装包。

随后,打开 Steam,点击左下角 + 选择「添加非 Steam 游戏」。

在弹出的窗口中点击「浏览」。

Chrome 的默认下载路径为 /home/deck/Downloads ,即左侧常用位置中的下载。找到刚刚下载的启动器 .exe 程序。

点击「添加所选程序」,将启动器安装程序添加至 Steam 游戏库内。

接着,在 Steam 游戏库内找到刚刚添加的米哈游启动器,点击设置按钮,选择「属性」。

找到「兼容性」一栏,勾选「强制使用特定 Steam Play 兼容性工具」,选择「Proton Experimental」。

随后启动安装程序,按默认设置安装即可。安装过程中会自动安装一些缺失的必要组件。

待安装完毕后,不要点击开启启动器,先关闭它。

返回 Steam 游戏库,找到启动器,点击设置按钮,选择「快捷方式」。可以看到 目标 一栏内是启动器安装包的路径,而非安装后的启动器本体的路径,需要修改为正确的路径;另外,起始位置 一栏也需对应修改。

这时候就体现出键鼠的必要性了,我保证你会因为无法对着触摸屏准确选中启动器路径而抓狂。

所以,要么连接键鼠,要么使用 KDE Connect(参考本文使用 KDE Connect 连接 Steam Deck 的方法),复制启动器路径。

以下步骤在使用键鼠的前提下操作。

首先,使用 Ctrl + H ,显示隐藏文件夹 .local

接着,一层一层地打开 .local/share/Steam/steamapps/compatdata/ ,点击 修改时间 按时间顺序排序,最新的这个 2843999894 文件夹便是安装米哈游启动器所在的文件夹。

该文件夹数字编号为随机生成,每个人安装后的编号都不一样,切勿按图索骥。

继续深入。打开 2843999894/pfx/drive_c/Program Files/miHoYo Launcher/ ,可以看到名为 launchr.exe 的启动器程序就安静地躺在这里。

接下来就好办了。右键启动器程序,选择 复制位置

启动器路径便被复制到了剪切板中。

/home/deck/.local/share/Steam/steamapps/compatdata/2843999894/pfx/drive_c/Program Files/miHoYo Launcher/launcher.exe

随后返回 Steam 库,修改 目标起始位置 的路径。

目标中的路径为具体启动的程序,需要加上 英文引号 ""。即最终填写为:

"/home/deck/.local/share/Steam/steamapps/compatdata/2843999894/pfx/drive_c/Program Files/miHoYo Launcher/launcher.exe"

起始位置为启动器程序所在的文件夹无需引号。即:

/home/deck/.local/share/Steam/steamapps/compatdata/2843999894/pfx/drive_c/Program Files/miHoYo Launcher/

另外,可以将程序名修改为 HoyoPlay,方便后续使用 SteamGridDB 搜索相关图片。

即可正常启动米哈游启动器。

安装游戏

安装游戏有两种方法。

一是直接在启动器内下载绝区零,这个过程可能比较漫长。点击下载游戏,按默认路径安装即可。

二是复制粘贴现有的已经在 Windows 内安装的游戏。需要将游戏复制到启动器所在的文件夹内:

/home/deck/.local/share/Steam/steamapps/compatdata/2843999894/pfx/drive_c/Program Files/miHoYo Launcher/games/ZenlessZoneZero Game

然后在启动器内选择 定位游戏,手动选择目录。

会自动弹出虚拟环境内的文件管理器,像操作电脑文件夹那样依次打开 C:\Program Files\miHoYo Launcher\games\ZenlessZoneZero Game ,确认后即可自动定位到游戏。

校验一遍游戏即可。

由于目前《绝区零》还未对 Linux 有所限制,因而无需任何额外操作,直接就可以在桌面模式下,通过启动器测试启动《绝区零》。

只是桌面模式下的《绝区零》会非常卡顿。

添加至库

添加到 Steam 库的方法和添加启动器一样,只是这回不用修改什么启动路径了。打开《绝区零》游戏文件夹,找到 ZenlessZoneZero.exe ,右键选择 Add to Steam 即可。

不知为何死活截不上图,借用一下添加启动器的图片,各位能看懂就好。

随后在 Steam 库内将游戏名修改为 ZenlessZone Zero ,就可以啦!

启动游戏

切换至游戏模式后,可以使用 SteamGridDB 简单地美化一下 Steam 库的游戏界面。

随后,就可以尽情地游戏啦!

游戏更新

后续请通过米哈游启动器完成游戏更新。

其他

正如我预想的那样,《绝区零》确实非常适合在掌机上游玩。即便游戏对显卡的要求不低,在 800P 下也可以用中等画质跑到平均 50-60 的帧数。只是游戏不是很好玩,开服玩了一个月便弃坑了。

至于《原神》和《崩坏:星穹铁道》,我也测试过了,均不能正常启动。前者的手柄体验我觉得非常糟糕,且反作弊严格,即便入库后可以启动,也会因为环境异常而被强制登出。《崩坏:星穹铁道》与《绝区零》一样,在手柄与键鼠之间切换流畅,风格也适合在掌机上游玩,只是反作弊也相当严格,根本无法启动。

原神键鼠操作习惯了倒是无所谓,但是《崩坏:星穹铁道》不能在 Steam Deck 上游玩非常可惜。

🔲 ☆

GalGame 管理器,有 Vnite 就够了!

关于「游戏管理器」,你可能多少听说过 Playnite 的大名。这是一款 开源 的游戏管理工具,你可以用它链接来自 Steam、Epic 等 诸多平台 的游戏。你可以使用 Playnite 整理安装在电脑中的游戏,或是记录某款游戏的游玩时长,或者纯粹地为了满足收集的需求,将想玩的游戏提前导入到游戏库中。Playnite 会使用精美的封面将这些游戏集中展示在主页,并在详情页内附上游戏相关的信息。这样一来,你的桌面上只需要保留 Playnite 的快捷方式,就可以启动 任意 平台的任意游戏了 (虽说有时候关联的游戏平台还是需要打开)

满满一页游戏的 Playnite
满满一页游戏的 Playnite

你或许会觉得,既然大部分游戏都是在 Steam 上购得,Steam 也可以做到导入外部游戏、编辑游戏封面,为何不直接使用 Steam 当作游戏启动器呢? 这当然可以我的朋友。只是,在 Steam 上编辑游戏封面,需要自己准备好这些资料——即便可以通过网络在现成的游戏数据库中搜索到这些资料,但 Steam 也不支持修改游戏介绍。

编辑 Steam 库中的游戏图片
编辑 Steam 库中的游戏图片

这时,Playnite 的优势就体现出来了。在其丰富的扩展生态的辅助下,你可以十分轻松地从游戏数据库中获取游戏相关数据。要做的只是安装对应数据库的扩展程序,再简单配置一番,Playnite 就可以在导入游戏时通过这些数据源直接抓取游戏资料。无论是游戏介绍、官方网站链接,亦或是封面图片。

可即便如此,Playnite 还是无法满足部分像我这样喜爱 GalGame( ギャルゲーム)的玩家。这种起源于东方日出之国的独特的游戏类型,与大多数游戏有所不同,其核心并非战斗、解谜或策略,而是通过丰富的文本、精美的插画以及动人的音乐,带领玩家沉浸于一段段细腻的情感故事中。另一方面,玩家在游戏中扮演的角色往往需要通过对话选项来影响剧情走向,而每个选择都可能导向截然不同的结局。这种多分支的设计使得 GalGame 的存档管理变得尤为重要——玩家需要在关键节点保存多个存档,以便回溯并探索不同的故事线。但 Playnite 并未针对 GalGame 的特殊需求进行优化,也没有这方面的扩展程序(也可能是我没发现)

所以,今天的主角并不是 Playnite,而是另一款名不见经传的应用:Vnite

介绍

Vnite 是一款专为游戏爱好者设计的多功能的游戏管理软件,基础功能与 Playnite 类似,但侧重于 GalGame 游戏的管理体验。在入库方面,Vnite 支持从 Steam 中一键导入至游戏库,并保留游戏时间;支持通过扫描本地文件夹入库。在元数据方面,支持从多种数据源1 自动获取游戏元数据。此外,Vnite 还支持按开发商、标签等条件动态分类游戏,并通过系统链接唤醒并启动游戏。

想必你也猜到了,Vnite 的「V」,是 Visual Novel(视觉小说) 的「V」。针对 GalGame 玩家的特殊需求,Vnite 提供了多项贴心功能。 例如,按次记录的计时器支持记录单个文件或文件夹的游玩时间;细致的报告功能让玩家能够轻松回顾游戏历程;多样化的启动方式更是方便实用,支持与其他程序联动,如一键使用 LE 转区启动、模拟器启动等。

最重要的是,Vnite 支持通过 WebDav 实现多端云同步。 无论是游戏数据还是软件设置,都可以在不同设备间无缝同步至云端,确保玩家随时随地都能继续自己的游戏之旅。

准备

目前,Vnite 仅支持 Windows。访问 Github Releases 下载最新版本安装包,根据提示完成安装。安装完毕后,即可启动 Vnite。

但在开始之前,让我们先认识一下 Vnite 的界面。

界面

这里我简单地将 Vnite 的界面从左到右划分为三个区域:菜单、索引和游戏。

菜单从上到下有六个图标,依次为:

  • 游戏库:你的所有游戏都在这里
  • 记录:游戏时长等记录
  • 主题模式:快速切换 白天 / 夜间模式
  • 云同步状态:快捷查看云同步状态
  • 添加游戏:添加游戏
  • 设置:功能上的详细配置

索引包含搜索和游戏列表。

最最重要的自然是我们的游戏库。顶部为最近游戏列表。首部游戏使用尺寸较大的游戏背景作为封面,可以很直观地告诉你最近打开过哪些游戏。

看!这井井有条的布局,看!这恰到好处的模糊!Vnite 现代风格的用户界面,是我选择使用它的原因之一。

接着是我的收藏,在游戏数量繁多的时候非常有必要。你可以将心仪的游戏添加到任意收藏夹内,然后在这里快速打开这些游戏。

最后是完整的游戏列表。如果不是因为压制出来的动图体积太大,否则我真的想向你们展示 Vnite 优雅的动画效果。你可以依据名称、发布日期、最后运行日期、添加日期、游玩时间,对游戏进行排序。

设置

对 Vnite 的界面初步认识之后,就该微调 Vnite 的设置了。之所以说是「微调」,是因为Vnite 的需要设置的内容其实不多。

个人建议需要配置的项目如下:

  • 开机自启:勾选开机自启后,Vnite 将随系统开机启动。但是,以管理员权限启动的游戏,Vnite 也需要配置管理员权限启动。这时,Vnite 的开机自启将失效。开了等于没开。

  • 云同步:云同步是 Vnite 的一大特色,但目前仅支持同步至 Webdav。配置完毕后,Vnite 会将自身设置以及游戏数据库同步至云端。

另外,可以在菜单栏通过图标快速查看 Vnite 的数据 同步状态

  • 刮削器:目前支持从 Steam、IGDB、Bangumi、VNDB、YMgal 中获取游戏元数据。你可以按个人游戏偏好选择默认数据库。假如大多数游戏都是从 Steam 中购买,或者 Steam 中有售,则应当优先选择 Steam;如果是 Galgame 偏多,则可以选择 Bangumi、VNDB 或 YMgal。对于 GalGame,目前仅 Bangumi 和 YMgal 提供中文元数据,如果是没有上架 Steam 的Gal,因优先选择这两者为佳。至于其他类型的游戏,则可以选择 IGDB。

  • 数据库:Vnite 的数据默认保存在 C:\Users\{username}\AppData\Roaming\vnite\app\database 文件夹,但你可以开启 便携模式,这样数据会保存在 Vnite 安装文件夹下 \portable\app\database 目录内。

入库

接下来,就该做我们的正事了:添加游戏

从启动 Vnite 的那一刻起,屏幕中央的 加号 就指引着你动身添加你的第一个游戏。

在这里添加的游戏,默认是 使用刮削器 添加的。
后续可以在这里继续添加其他游戏。

使用刮削器

逐一

首先,我们需要填写 游戏名称游戏 ID,以便通过 Steam(数据来源) 搜索到游戏数据。

如果是使用名称,可能会搜索到多个游戏。

选择你想添加的正确的游戏,Vnite 会在 游戏 ID 一栏自动填充游戏对应的 ID。

所以,在已知游戏ID的前提下,直接使用 ID 添加会更加精准。例如 https://store.steampowered.com/app/1144400 中的 1144400 便是游戏ID。

然后点击 识别,等待 Vnite 获取游戏封面。

选择一张图片,点击确定后,等待游戏添加。这个过程十分缓慢(kàn liǎn),可能只需数秒,也可能需要等待半分钟。总之在弹出游戏 添加成功 的提示后,返回主页即可看到添加的游戏。

批量

一个个添加游戏是不现实的,所以在本地存在大量游戏的前提下,请使用批量入库。Vnite 会扫描指定 库文件夹 内的游戏文件夹,批量搜索游戏。

选择想要添加的游戏,点击 添加 按钮,等待游戏数据抓取。批量扫描读取的是文件夹的名称,因此文件夹名需要尽量准确。

但你可以修改获取到的名称,重试一次。如果匹配到游戏ID,那么游戏便能继续添加了。

待提示游戏添加成功后,即可返回主页查看添加的游戏。

从 Steam 中导入

Vnite 目前仅支持从 Steam 游戏库中批量导入游戏。点击左侧边栏添加游戏按钮,选择 从第三方导入。填入你的 Steam ID 获取游戏列表。

Steam ID 可以通过访问 https://store.steampowered.com/account/ 获取。或是在 Steam 客户端右上角,点击 账户明细 获取 ID。

填入 ID 后,点击 获取 按钮获取游戏列表。获取速度视网络状况而定。待列表加载完毕,勾选想要导入至 Vnite 的游戏。

但不能一次导入过多游戏,否则可能会导入失败。而且因为添加游戏缓慢,可能会被迫在这个界面卡好久……

不使用刮削器

你还可以反其道而行之,不使用任何刮削器,直接添加本地游戏。

但这样添加的游戏没有任何元数据,不是很好看。因此除非数据库搜索不到,或是网络不畅,否则不太建议这么添加。

编辑

这还没完!在上一步添加的游戏,需要进一步编辑才行,因为还未补全游戏路径;另外,也存在添加过程中因网络动荡导致元数据抓取失败的情况,可能是标签,也可能是图片。

补全路径

点击游戏详情页右侧的设置按钮,找到启动游戏的 exe 应用程序。

如果是 Steam 游戏,可以在 运行 中切换成 Steam 启动,Vnite 会自动填充形如 steam://rungameid/steamid 的启动链接。这样在 Vnite 中启动的 Steam 游戏,和直接在桌面点击游戏图标的效果是一样的。

补全图片

添加游戏的道路不可能一帆风顺,所以补全缺少的图片是家常便饭。

媒体 中查看并修改缺少或不喜欢的游戏图片。

你可以使用快捷操作,从文件或在线链接中导入图片,或是裁剪现有图片。

或者,使用 搜索 功能,Vnite 支持从 SteamGridDB 中搜索图片。

但也可能出现一个问题:明明是 Steam 的游戏,使用 SteamGridDB 却搜索不到图片。

不要惊慌,目前我遇到的情况有两种:

  • 网络问题:网卡了下,没搜出来。关掉搜索再搜一次。
  • 数据问题:游戏太过冷门 / 国产中文游戏 / 其他原因,导致没有人上传图片至 SteamGridDB,就像这样:

    解决办法倒也很简单:点击左上角的 View Original Steam Assets(查看 Steam 原始资源),手动复制图片链接。

    或者催更作者,让他加上直接从 Steam 抓取图片的功能(小声)。

补全信息

简介、标签、基本信息、相关网站等文字内容,可以点击右侧的 编辑 图标修改。简介支持 HTML 语法,添加多个标签或平台的时候,用英文逗号 , 隔开。

使用

添加过游戏,就可以使用 Vnite 记录游戏历程啦!启动游戏的方法不用我多说吧?让我们来看看其他值得注意的功能。

标记

在通关或是搁置一款游戏后,可以在游戏详情页修改游玩状态,编辑游戏评分

索引

搜索功能 目前支持通过游戏名、开发者、发行日期等元数据进行搜索。

游戏列表 可按开发商、收藏、类别、游玩状态进行 分组 查询。

顺便一提,列表是可以收起隐藏的。

信息

游戏相关的 信息 以最大的面积展示。

概览 一栏,你可以查看游戏的简介、基本信息、相关网站以及标签,并通过每个模块右上角的 编辑按钮 自行修正补充。

记录 中统计了游戏入库以来完整的游玩时间,以条形图的方式展示近期的游戏时长。另可按日期查看具体某日的时长。

在设置 存档路径 的前提下,游戏过程中的 存档 会在 退出游戏 后自动保存,你可以为某一时间段的存档添加备注。

回忆 这里官方并没有说明具体用法,你可以在这里保存游戏截图,或是针对某一部分内容作具体的注释、吐槽,或抒发感想。

例如,可以为某段「回忆」回忆添加封面或文字,文字支持 MD 语法。但目前并不支持直接在文本内添加图片,只能使用外部图片链接。

记录

点击左侧菜单栏 记录 按钮查看游戏记录,会将近一年的游戏时间以条形图的方式列出。我才刚开始使用 Vnite,有朝一日一定会填满这里的!

也可以查看游戏库中最高评分、总游戏时间、最多次数、最近运行的游戏排行。原神启动!

主题

通过点击主题按钮可以快速切换 白天 / 夜间模式

对 CSS 有一定涉猎的话,也可以尝试定制属于自己的 Vnite 界面。

最后

关于 Vnite 的使用及其功能,就介绍到这里了。这是我使用过的最好看的游戏启动器,(我也就用过两款游戏启动器,一款是Playnite,另一款是Vnite。)我迫不及待地想让大家体验一下这款应用程序,所以花了一点时间写了这篇文章。希望能在 Vnite 的帮助下,替玩家更好地管理本地或不存在于本地的游戏。但需要提醒大家的是,Vnite 目前还存在许多不足,比如添加游戏缓慢、图片数据源单一、游戏元数据无法二次抓取,云同步功能也还有改进空间。因此在使用过程中,可能还是需要诸位先克服这些困难。

不过也不要怕。如今的 Vnite 已经比我刚发现它的那会儿要完善得多了。我大概是在 1.2 版本(2024年9月份)的时候注意到的 Vnite,早期的 Vnite 功能比较简单,备份只能上传到 Github,体验比较一般,添加游戏也没现在这么顺畅,元数据源几乎没有,上手还有一定的门槛,完全比不上隔壁百花齐放的 Playnite。可只是短短数月,Vnite 就已经迭代到了 2.x 版本,从「堪用」进化到了「好用」。甚至在不远的未来,还有功能更加完善的 3.0 版本将问世,上面这些问题不仅能一一得到解决,可能还会有其他惊喜等待着大家发现!

那岂不是到时候 Vnite 更新,我这篇又要大改?啊……不要啊,我写不动了(

嘛,还等什么?快下载 Vnite,即刻充实自己的游戏宝库吧!

参考

另可加入 Telegram 群组参与讨论:Telegram 群组


  1. 如 Steam、IGDB、Bangumi、VNDB
🔲 ⭐

广州,夏天,猫

中央空调从头顶倾泻下来冰冷的风,仿佛是要刺进骨头一般,我不由得捂紧了衣服。尽管现在已经是 6 月,气温似乎还是有些低了。从 19 年底到现在已经过去了一年多,空气中还是弥漫着一股散不尽的消毒水的气味。一眼望去,大家都严严实实地戴着口罩,盯着手机,任凭播报列车时刻的声音在候车大厅飘荡着。这一幕似曾相识。两个月前,我也是在这里等着北上的列车,那时的我还做着闯荡大城市的美梦。但只是十八天,就让我真切地意识到自己不适合也无力在这样的大都市下生存,于是逃也似的回到了小山村。可家里蹲的日子太过空虚,我没有控制自己的能力。所以两个月后,我又只身前往另一座大城市。但就像不知道为什么要去上海一样,这次同样是充满着未知的旅程。无奈怎么也想不出来会是什么样的目的地,于是我决定闭上眼睛小憩一会。

忽然听见身边人起身的动静,我也赶忙拎起行李跟上进站的队伍。安检员只是站在闸机边上双眼无神地看着前方,大家都默不作声地隔开一定距离有序进站。上车后我才注意到,这辆直达广州南站的列车上仍是人头攒动,待行李放稳妥后,一切就又回到我所熟悉的列车上的场景:熟悉的乡音,操着粤语对着手机念叨着什么的商人,以及外放抖音笑得乐呵的孩童,当然,还有泡面的味道。我松了口气,幸好车上的氛围没有候车时那么严肃紧张。于是放下心来,美美地睡了一觉。

等到再次回过神来,已经到广州了。

肠粉

在正式吃肠粉之前,我特地向广东的朋友咨询了关于正宗的肠粉的定义。他只是说要像“抹布”一样。奈何我绞尽脑汁也想象不到抹布的滋味,所以只能凭自己的感受对第一次的肠粉下结论。

吃肠粉的地方位于公司出门左转五百米远的一处小吃摊。若是平时肯定没机会过来吃上一顿的,毕竟一日之计在于早晨充足的睡眠。但室友大爷说一定要让我“见识一下”,还说“这一顿他包了”,盛情难却我也就只能跟了过来。

肠粉五块钱,加蛋加肉加香肠的话八块,外带一杯新鲜豆浆。豆浆是不要钱的,喝完了可以续杯。老板拉开蒸笼托盘,摊上一层米浆铺开,再塞进蒸笼里,不一会儿肠粉就熟了。卷起来撒上佐料,加肉加蛋,切成一块块合适的大小,往碟上一摞,热腾腾的肠粉便完工了。这让我想起了鸡蛋饼。似乎除了原料不一样,流程好像都是差不多的。

就同沙县小吃拌面用的花生酱一样,肠粉的灵魂也是酱料。酱料不一样,味道可能就会天差地别。摊主的酱料调得不错,往肠粉上淋满厚厚一层,酱汁顺着肠粉的褶皱渗透进去,再趁热一口吃下。肠粉糯糯的口感,加上鸡蛋的香味,混合着酱汁的清甜,随即饮下一口豆浆。味道好极了!

只是抹布的味道还是没感觉出来。囫囵吃完后回想起来,可能是指肠粉的形状?搞不明白。

凉茶

好像说到广州美食,就是早茶粤菜之类的,我姑且也在村口往前不远的市场里面的小摊上买了瓶本地凉茶试试。正不正宗不知道,肯定是和加多宝王老吉之余的不太一样。光是“凉茶其实是热乎的好喝”这点就有些超纲了。

味道有点像中药,或者说是止咳糖浆的味道,至少是有罗汉果的味。

白云

一提到夏天,就少不了清脆的风铃、聒噪的蝉鸣、湛蓝的天空,以及一望无际的白云,还有耳畔呼呼的风扇。不过广州的夏天只有风扇怕是不够,唯有空调才能战胜这个夏天。

广州的云格外地多,并且总是厚得吓人。像棉被似的一团挤在一起的云,或者是偶尔会想着就这么往云上一扑,那得有多舒服。

公园

公园我去了两处,一处是雕塑公园。倒不是很远,某天休息日一早,室友大爷拎着我去的。坐公交周转约莫半个钟头,在不知道什么站下车,走过不清楚什么路口,就到公园了。大概是因为免费,所以条条大路都能通向公园。之所以这么说,是因为后来在网站上看到了雕塑公园的正门。明明逛了有一两个小时,我却没有路过这里!

十点钟的公园人还不是很多,也许是昨晚下过雨的缘故,路旁的行道树还挂着水珠。初夏的阳光透过层层枝叶,在地上投射出好看的图案来。但风一点没有,没走几分钟,背上就渗出汗来。大爷热得直扇扇子,我也有想脱下衣服的冲动,只好沿着小路往公园深处走去,希望能找到荫蔽的地方乘乘凉。偶尔与两三个驻足观赏雕塑的游客擦肩而过。顺着视线望去,都是有些奇形怪状的一眼看不出来立意的塑像。好在每座雕像底下都贴着一块写有名字、作者和简介的铜色牌子,有些则是直接刻在石头上。

不过鲁迅先生我还是认得的。会不会每座雕塑公园都有先生的塑像?

另一处则是越秀公园。毗邻雕塑公园,广州博物馆也在园中。乘坐地铁 5 号线往公园前方向,经过广州火车站,下一站就到了越秀公园。越秀公园要比雕塑公园大的多得多,8 月份也是旅游旺季,游客量比我在雕塑公园见到的要翻上个几倍。随处可见扭动着身躯跳舞的大妈,光着膀子晨练跑步的大爷,推着婴儿车从人群中穿过的年轻人,还有扛着大炮打鸟的老头,热闹得像个集市。感觉人挤人不是什么好去处,我只能顺着山间小路走到人流量少的地方——博物馆确实没什么人,毕竟光是门票就要十块钱,只是能观摩的东西比较少,上楼远眺中山纪念碑倒是不错。

「有了猫之后,手机相册里就只剩猫了」,这大概和「有了液压机后,家里只剩液压机」有着异曲同工之妙。在到广州的第二天,我发现了这只匍匐在墙边的黄纹花猫。第三天,我成功和猫打成了一片。猫是典型的田园品种,长相不大好看,四肢雪白,背上一片橘色,尾巴不知道什么原因只剩半截,腹部一排乳头清晰可见。按同事的说法,这猫是去年流浪到这附近,被保安大爷喂饱后定居在此处的。一块来的有两只,但过完年回来就只剩她一只了。

也许是流浪吃过太多苦,也许是正值哺乳期,即使是我这样的陌生人也格外亲近,任凭我从上到下抚摸个遍。在这之前我只撸过狗子,这是我人生中撸到的第一只猫。我这个人很简单,你愿意让我摸,我肯定不会亏待你的。所以我买了猫罐头和猫粮。

猫喜欢在下午两点多的太阳高照的时候,蜷缩在门口的树桩上睡午觉。我也觉得这是个睡觉的好地方,大小正好够她一个猫。

猫有一次不知道哪里叼了只老鼠,正好被我撞见。她看了我一眼,就蹲角落里加餐去了。好大一只老鼠,猫好样的!

在广州的一年半,猫产了三次崽。刚到广州的时候是第一次,三只还是四只几乎全军覆没;过了四个月,在某个雨夜猫出去鬼混,又成功被外面的野猫搞上,但只剩一只存活。

猫二代倒是争气,也是在某个阴雨连绵的夜晚嚎叫了一宿之后,在来年的初夏产了八只崽。没人想得到二代的小身板能容纳下八只猫崽。

这回我们及时送走了其中的三只。一只被一个自称是开小卖部的男人领养走了,说是要养只猫看家护院。没有后续。

另外两只被一对小情侣领养。女主人本来是想要小黑和小白,但我有私心,同事也不舍得小黑走,想看着小黑长大。于是最终带走小白和另一只橘猫。我很后悔没有让他们带走小黑,因为在两个月后,小黑也不见了。

而在年底,随着疫情的尾声,经营不善的公司也迎来了倒闭。更糟糕的事情是,猫在某一天集体中毒,二代包括她的孩子全部中招,只剩一只三花存活。我至今不知道是谁下的毒手。离开广州的那天,我把仅剩的半袋猫粮全倒了出来。三花非常怕人,总是躲我躲得远远的。小家伙,这次可算是被我拍到了。长大的话应该会是很帅气的吧。

两年过去了,不知道猫过得好不好。我很想她。

后记

本文是我于离开广州两年,在上海工作了半年后,从草稿堆中翻到的零碎的小记录集合而成的水。似乎原本的计划是夏天单独一篇,猫再另起一篇的。可是时间过去得太久,记忆已然出现了偏差。即便我再能水,也全然不记得当时是怎样的心境了。我只能从记忆的边角里扣出点料来,再添油加醋拼凑一番。毕竟我现在的状态有些糟糕,什么都写不出来。不管是开箱也好,生活也罢,甚至是有趣的软件,都写不出来了。生活是规律的两点一线,工作是枯燥的重复劳动,日常只有上班和下班,没有任何值得分享的事。有时候晚上想着写点什么,看着空白的编辑器,再看两眼手机,一回神,已经十点了。所以这篇加上前面那篇,就是最近半年的我所能写出来的极限。如果在阅读的时候发现有什么语句不通顺的地方,还请无视过去。

就把这篇当作我2021年的年度总结吧!总之,祝看到这里的朋友在即将到来的 2025 年,事事顺心,新年快乐。

❌