2025-10-07 09:09:00
这篇月小结拖更蛮久, 原因在于最近的生活实在是充实, 时值枫叶季, 去了一趟Mont-Tremblant,赏枫, 打算整理照片后在下个月度小结发布. 学期开学1月有余, 课业压力让我又想起了刚出国的日子... 本学期的高压课程是算法, 其次是计算机网络. 这2门课对我来说都是弱项.
算法课程细致入微, 课上课下学起来都颇费功夫. 计网截止到上周讲的还都接触过, 例如DNS解析之类的, 这周开始的可靠数据传输RDT还需要下一番功夫.
在这里不得不重提一下, 一台NAS真的优化了我的很多场景, 文件夹同步/动下载影视/外网的照片访问等功能对我来说真的很有用, 缺点也有, 外网访问摄制的视频时遇到了速度不够的问题, 接下来打算琢磨一下把视频自动备份到google drive/dropbox方便访问.
之前因为罢工和找了不靠谱的第三方集运, 我从国内寄过来的衣物和键盘丢了, 迫不得已在小红书上收了一个键盘, 这个老哥最近又在出硬盘, 我就花了55$买入了一个1T的990pro.
这2年在家里用的最多的耳机是女朋友送的水月雨Aria2, 因为我使用时间过长, 遇到了金属网老化/耳机腔体严重掉漆的情况. 金属网联系售后是10块/5对, 但是腐蚀掉漆问题无能为力, 清掉腐蚀的地方后偶尔会有一点漏电. 看到Bestbuy的棱镜2打折, 就火速购入了. 耳罩式耳机的确对耳朵更友好, 但使用下来音质的确没有有线强, 不过强在有无线和有麦克风, 偶尔和国内外朋友打游戏, 剪视频看视频还是很舒服的.
不出意料的话就要出意外了, 这里讲一下在使用这个耳机的时候遇到的断连问题, 以方便有读者通过关键词搜索到此文:
++tv/1e1nw4EEXVAwE0bSImEp9y++
++tv/5SFon8PVjPuAIIaKziZToO++
++movie/7haEvPPYBSLlekzDwj8m2i++
++book/6uMu8bCA886NcGxZVd9lAz++
++game/1EZcXj3398L8TKZ4ELPIkW++
++game/2vyzNrQ3s0lDx11arqfZm1++
2025-09-22 05:09:00
一台能一天24小时开机的主机一直是我的梦想之一. 这个梦想的出现要追溯到我的初中时期, 那时候Minecraft刚刚推出, 我和好朋友就开始琢磨怎么样能有一个自己的游戏服务器. 之后用了很长的一段时间才知道了部署一个持久运行的server需要哪些东西, 又需要运行哪些服务.
大一的时候, 身边有同学买了Synology群晖, 同学很慷慨的给我开了一个账户让我体验, 但缺乏目的性的体验让我很是迷茫. 大二的时候, 我捡垃圾, 拥有了一台真正意义上的服务器, 印象中的配置是i3-8100/16G + 2TB SATA
. 不过那时候对linux一窍不通, 所以用了Windows server
作为系统, 装了Jellyfin
和qBittorrent
, 简单的作为一个手动管理的数据存储/影音库. 但很快这个主机就吃灰了----- 我妈在家里听到主机吭吭哧哧的就直接拔掉了插座. (笑)
上文中的好朋友一直是一个能折腾的人, 他整了一个芯片是N100的小主机, 刷了FnOS来当作NAS. 我对于NAS系统的了解还出在一个模糊的时期, 在了解完晚上的系统后, 我找他开了一个系统账号, 体验下来, 我觉得是一个开箱即用的好系统. 于是购入了一个N200/12G RAM的全固态NAS. 选择全固态的原因也很简单, 静音, 快速, 适合一个没有固定居所的留学生.
本文用于初步记录以下现阶段我对NAS的需求, 以及通过FnOS部署了哪些服务, 且仅当作一篇随笔看好了. 教程在网上很全, 唯一踩坑的地方是: 在FnOS中不要用ssh安装Nginx
, 这会在重启后挤掉系统内嵌的trim_nginx
服务, 导致无法访问NAS面板.
FnOS自带的影视中心和相册配合软件已经很好的实现了照片备份和观影, 我就没有再进行任何额外的配置.
qBittorrent
中进行下载SMB
搭配Symfonium(安卓端听歌app, 很好用)听歌.为什么标题要叫需求明确? 互联网上你经常能刷到: "XXXX年了, 你真的需要一台NAS吗?" 购入之前, 我列了一个清单, 考虑到我会用来部署什么服务/提供什么便利. 当然这中间几经琢磨, 放弃了一些想法.
在8月小结中我有提到, 我的手机经历过一次"变砖", 后果就是我丢了从上一年2月到今年8月之间的所有照片, 所以我迫切的需要一个足够安全的私有云来备份照片; 在我的MacBook上, 我给从大学时期到现在位置的所有个人证件,重要文件打上了红色的tag, 但当我用ForkLift
时误触了删除tag, 并没有及时发现. 所有文件都被删到了垃圾桶中, 失去了原本的内容结构; 依赖单个设备的数据存储和安全管理已经没有办法满足我的需求了, 于是NAS就成为了必需品.
如果我对NAS进行定义, 存储一定一定是第一优先级, 如果你很喜欢折腾, 那你更需要一台个人服务器, 而不是NAS------即使前者能兼顾后者的功能.
NAS就是这么一个东西(笑), 一个能帮助你解决数据方面烦恼的最优解. 我在没有的时候很憧憬, 拥有后很庆幸, 也祛魅了.
2025-08-23 19:08:00
<iframe data-testid="embed-iframe" style="border-radius:12px" src="https://open.spotify.com/embed/track/0gPPcSZ5dlAncGsxa56Ck2?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
写这篇博客的时候, 国内时间正好是我上一年出发, 经苏黎世国际机场转机来到加拿大的日子. 并不是什么重大的日子但也值得纪念, 毕竟也是在异乡呆了足足一年了. 一时间感慨很多, 按照国内的卷王视角来看, 我着实是碌碌无为. 但和来到这里前的自己相比, 我认为已经全方面提升了, 至少我这么说服了我自己.
来到这边后一而再再而三的感慨很多不正常的人, 但好在幸运的遇到了比较合拍的朋友们, 我们给自己起的群名是抽象大队
, 逢大家生日的时候, 都会做蛋糕, 精挑细选一家合寿星口味的饭店一起吃饭.
距离8月中旬的Summer学期结束已经1个星期了, 8月的这段时间每周保持着和朋友们运动1~2次的频率, 运动方式为骑车/游泳/网球, 主要还是打网球. 作为一个初学者, 我把羽毛球的一套动作给带到了网球中, 后果就是第一次打完胳膊酸痛, 后面学会双手引拍后好了一些.
这是一个便携/小巧的OpenWRT系统路由器, 我在国内的时候朋友要走了家里的华为路由组mesh, 给了我一个GL-AXT1800, 我刷入了32G的存储来跑一些小东西, 例如媒体服务之类的小功能. 走的时候担心行李超重, 就没有带这个小东西过来(太后悔了). 趁着有不错的deal我就在Amazon上购入了一个MT3000, 相比AXT1800体积更小巧, 也更fit for me. 到手后开启ssh装了一些网络加速的插件, 和之前那个十几年前的路由器相比真的是质的飞跃.
这个就说来惭愧了...一开始, 我满怀信心的在网上多方比较了教程, 选了一个看起来靠谱且简单的. 然后就开始按着步骤操作. 但在格式化系统安装OxygenOS的时候和电脑之间出现了传输问题. 然后手机就陷入了循环重启(bootloop). 在查找修理方法无果后, 果断联系了专家: 大侠阿木, 具体方法就是远程我电脑, 用更专业的工具去重装了系统. 从手机坏到修好, 历时2天, 好在结局是好的. 使用几天后, 和国内的ColorOS系统相比, 个人感觉有以下不同:
今天下午(北美时间)的时候, 小孩5-4击败了blaz拿到了EWC"街霸6"的冠军. 我也是看了整整一下午的比赛.
上一年还在国内的时候, 从英国留学回来的好朋友一直在玩这个游戏, 当时我对于格斗游戏的认知还停留在街机厅的盗版拳皇/街霸和苹果手机上的kof-i
. 玩kof-i的很长时间都是去打人机, 看人物/队伍背景故事.
直到好友拉了另外2个人入坑, 看他们菜鸡互啄让我也很感兴趣, 然后我就成为了"云"玩家, 偶尔刷到视频看一看, 刷到比赛看一看. 上个月街霸6打折的时候我买了下来, 因为就是因为看到了舞(Mai, 不知火舞, 街霸6在第二年的联动角色, 除此之外还有饿狼传说的特瑞, 上图左下那位)学了一些基础打法就开始玩排位, 直到上手才发现这个游戏想要玩好真的很难, 得益于好友的指导, 定段到了黑铁(下面还有ROOKIE).
直到和真人对战, 我终于发现了格斗游戏的乐趣. 每一次拆掉对面招数的兴奋, 被拆招的懊悔, 获胜的激动都是极其纯粹的. 你只能和自己较劲, 剔除自己的弱点, 提高自己的反应. 毫不夸张的说在和真人对战胜利后的喜悦是之前所有游戏没有办法比拟的.
于是有了今天才拿到的hitbox, 上手练了一会就去找真人了, 手感良好😊:
Astro-blog记录(7) - 小修小改和 Status Cafe组件
补充一个额外的改动: 修复了移动端的主页bug/添加了移动端的header适配
++movie/0iqN1YrAG0Hh0HdmFv7y8v++
++movie/0NxFGhJflscHm7pEZaiW6Y++
++movie/01OH5yp4S3vzDTRNoAOJDl++
++tv/1e1nw4EEXVAwE0bSImEp9y++
++tv/6HBkGRTvqrik3cgp9Of7mg++
2025-08-09 06:08:00
不知道是我经常在互联网冲浪还是运气好, 总是能在冒出一些想法的时候找到需要的工具....最近在玩Warframe, 有一些奖励丰厚任务是随机出现的, 我就调API简单用Discord Webhook进行推送:
于是我又想到, 能不能用写一个Discord的bot, 我把一些零碎的实时状态发过去, bot会读取然后更新blog的repo, 实时显示到blog上. 但前段时间Alanone发消息更新blog的时候, 我顺便去串门看了一下, 发现了Status Cafe这个网站.
Status Cafe是一个发布实时状态的网站/组件,用户发布的状态可以RSS订阅,用网站提供的代码也可以显示到自己的网页中. 这类似于自部署的博客点赞小组件Openheart, 在curl
的时候额外带上了文本. 省去了折腾的功夫. 使用过程中唯一遇到的问题就是从注册到收到成功邮件约1天时间, 看样子是站长亲自审核的, 吓得我以为是Cloudflare的邮件转发(博客的主要沟通邮箱通过Cloudflare部署)宕机了, 连着换邮箱注册了3个账户....
这个网站有一个Status stream
, 你可以在上面看到所有人的status, 就像一个大型的博客聚合, 偶尔会发现让人感叹的网站. (大家怎么能这么会设计? 博客大装修再次提上日程). 闲逛过程也是发现鲜有中文的status, 不过也逛到了小鱼老师的博客, 点进去发现果然有写status的文章.
Status cafe提供了一些badge, 例如:<a href="https://status.cafe/users/asyncx"><img src="https://status.cafe/users/asyncx/badge.png" alt="Status Cafe Profile"/></a>和<a href="https://status.cafe"><img src="https://status.cafe/assets/button.png" alt="Status Cafe"/></a>, 不过被我嫌丑所以没有打算用这个. 之后装修博客的时候计划为这些组件画一些像素badge(inspired by LMNT).
此外, Status cafe提供了一个显示最新状态的小组件, 通过这个小组件你就可以把最新的status显示在网页中. 自带的css略微复古, 就手搓了一个.
<div
id="statuscafe"
class="ml-auto w-fit rounded-md border-bgdefault dark:border-fontdefault p-3 transition-colors duration-300"
>
<div class="text-center">
<div
id="statuscafe-content"
class="inline text-sm font-mono leading-relaxed text-zinc-900 dark:text-zinc-100"
>
Loading...
</div>
<span class="mx-1 text-xs text-zinc-500 dark:text-zinc-500">(</span>
<div
id="statuscafe-username"
class="inline text-xs font-mono tracking-wide text-zinc-500 dark:text-zinc-500"
>
Loading...
</div>
<span class="mx-1 text-xs text-zinc-500 dark:text-zinc-500">)</span>
</div>
</div>
PS: 因为Status cafe的主页还没装修, 目前并不打算很显式地公布link, 所以当我发现这个小组件会请求远程的js来进行渲染, 就把js嵌入到本地进一步修改了:
document.writeln(`
<div id="statuscafe">
<div id="statuscafe-username"></div>
<div id="statuscafe-content"></div>
</div>
`);
fetch("https://status.cafe/users/asyncx/status.json")
.then((r) => r.json())
.then((r) => {
if (!r.content.length) {
document.getElementById("statuscafe-content").innerHTML = "No status yet.";
return;
}
document.getElementById("statuscafe-username").innerHTML = r.timeAgo;
document.getElementById("statuscafe-content").innerHTML = r.content;
});
效果就像在主页看到的一样:
Astro v5 把 <ViewTransitions />
组件改名成了 <ClientRouter />
, 已手动修改. 但还是有一些bug没有解决, 例如整个网站对某些外部资源的请求只进行一次, 如果请求内容发生变化仍有几率不会重新请求, 进而导致显示异常, 比如Twiiko评论, 这个bug我会尽可能的修复.
发布本文2h后已修复. 解决方法为在
<head>
添加相应外部资源进行持久化
V2EX的站长Livid上线了一些加密货币的功能, 出发点是好的, 但站内充斥的乞讨信息令人厌烦. 个人建议加一个分区叫 我一直对去中心化和联邦宇宙等比中心化自由的架构形态充满兴趣, 所以买了60$的V2EX币, 也顺手给blog加上了赞赏链接, 不过为了不影响观感, 添加到了某个不显眼的地方.Begging
.
最近做了什么
这个页面我并不经常更新, 可见性没有Status cafe高, 互动性没有Mastodon强, 趁着这次更新干脆就暂时移除掉了, 后续如果有更好的想法或装修好Status cafe会再次开放.
2025-07-11 21:07:00
忙于学业, 好久没有更新. 为了追上之前落下的学分, 我选择在summer term报了full-time, 又因为夏季的学期有2段, 所以有2门课程集中在了第1段, 这就类似于我在一个正常的学期一周上5门课,每门课又有1/2个LEC课和1/2个LAB课. 第一段学期对我来说实在是太难了. 好在磕磕碰碰的结束了这个学期, 除了考试我感到不满意外, 我的作业和项目几乎都拿到了高分以至于满分. 一定程度上这对我来说也是一种鼓励吧.
4个月前称体重的时候, 我的体重还一如既往的是65kg. 上周称体重的时候, 已经69kg了. 上一年爬Saint-Bruno的时候, 也是我有史以来第一次爬山爬到上气不接下气, 心率一度到200. 刚好看到Garmin的官网在打折, 于是买了一块Garmin Fēnix 7 Pro Sapphire Solar.
到手后跑了一下, 发现我的身体机能下降到了一个不敢想象的地步.1km一圈, 跑了3圈, 配速是8' 8' 7'. 第三圈的末尾我的心率从140断崖式上升到200. 很难想初中高中那时候的跑操是怎么跑下来的.
很多专业的数据我还没有了解清楚, 我目前看的指标有: BPM(平均心率, 运动的时候看)和Body Battery(实时显示的身体电量, 休息和睡觉会充电, 压力和运动会消耗身体电量). 此外, Garmin会根据你的上述指标和其他状态来告诉你今天的理想运动内容. 目前我只用到了一部分的功能, 其他的还没有琢磨.
第二段学期只有1门课, 所以我打算在每次下课后去爬Mont-Royal来增强心肺功能.
最后, 我一如既往的折腾了一个运动网页. 本来计划想用yihong0618/running_page这个repo来实现, 但是我并不想展示轨迹, 于是在了解了Garmin connect API的原理后自己动手写了一个. 功能很简单, 具体来说就是Python模拟登录来获得运动数据的json
并处理成.csv
, 存储到src/component
中, 前端渲染交给Astro
来实现. 我会在晚些时候单独写一篇文章.
运行交给了Github, 部署交给了Cloudflare. 使用Actions, 在每日凌晨自动读取数据, 触发Cloudflare更新.
我现在住的地方是外公外婆的房子, 天气变暖他们就从国内回来了. 所以我自然而然失去了厨房的所有权. 现在顿顿吃得极度清淡, 只能寄希望于和朋友们出门吃点口味重的. 考完试后, 我们一起去了昆虫馆:
++game/5pvs201VxbkldH4LOEtDVt++
++game/35pyo0ZwNaEsTGrywLIsJ1++
++movie/3pzmdXn8JRf4xIulfvb9Ef++
++movie/6Vu6XIJIMSY2WWWobxj9r1++
2025-07-11 21:07:00
本文的实现并没有包含路径展示,分年统计的功能, 如果你需要更成熟的方案, 可以使用yihong0618/running_page.
前几天购入了一块佳明, 在进行几次运动后一如既往的想要折腾一下, 就有了写一个运动数据页的想法. 我的需求很简单, 有一个总体数据展示, 并且可以兼顾隐私性(数据太多不会读)和扩展性(万一以后想加点什么). 所以初步功能就定为了:
本文基于Python模拟Garmin connect登录以获得运动数据, 并存储为csv文件, 之后使用Astro基于csv文件进行前端页面的渲染. 其中自动化运行Python是通过Github Actions完成的. 你可以访问我的库来获得详情: A5yncX/my_running
由于佳明开启心电图功能后需要二次验证(邮箱验证码/短信验证码), 并且在短时间内多次的登录会触发cloudflare的风控, 所以本文方法是基于登录生成的token登录. 你需要如下包:
pip install garminconnect httpx cloudscraper
接下来, Fork我的仓库是最快的方法, 如果你并不想fork, 也可以点击Code -> Download zip
到本地修改并继续运行.
在项目目录运行:
python3 running/garminconnect_exporter.py --username YOUR_MAIL --password YOUR_PSWD
这一步的作用是在本地生成2个JSON格式的Token, 用来持久化登录, 有效期据github上的大家说是1年.
在你首次运行并接收到验证码后, 这两个文件会生成到位于你电脑的根目录的.garminconnect
文件夹内, 文件的名字是oauth1_token.json/oauth2_token.json
, 之后你运行一下命令就可以直接基于token进行登录了:
python3 running/garminconnect_exporter.py
首次运行后你会发现csv文件生成于src/components/activities.csv
. 这就是后续使用Astro可以进行读取的文件了. 接下来的步骤需要你先推送到github.
如果你更倾向于手动更新, 那么恭喜你, 你可以略过这里跳转到部署那一步了.
本地运行garminconnect_exporter.py
依托于本地的那2个token, 但是在Github上如果直接将这2个json的内容作为variable传入, 会出现换行符等问题导致没法正常使用, 所以需要用base64转换并存储, 接下来是详细步骤:
到本地.garminconnect
文件夹的目录中, 你会发现那2个json文件, 分别运行:
base64 -i oauth1_token.json
和
base64 -i oauth2_token.json
分别复制这2个命令的输出, 并点击存储库的setting: Setting->Secrets and variables->Actions->New repository secrets
设置两个secret, 命名为GARMIN_OAUTH1_B64/GARMIN_OAUTH2_B64
, 分别存储这2个命令的输出.
这一步的作用是方便没有邮箱/短信登录验证的人使用的, 以及如果后续token失效, 你的邮箱/短信会收到一次验证码, 可以起到提醒你更新token的作用.
依旧点击存储库的setting: Setting->Secrets and variables->Actions->New repository secrets
, 设置两个secret, 命名为GARMIN_USERNAME/GARMIN_PASSWORD
, 值就是你的登录账号密码.
删除github repo中之前生成的csv文件, 点击Actions
手动运行一下, 查看是否正确生成.
侧边栏选择计算(Workers), 创建pages, 模板选择Astro, 默认的命令和路径不需要改变, 等待部署成功即可.