2026-04-21 17:40:00
![]()
为了保持对 GPT Gemini 等的访问,电脑开着系统代理,导致影响对国内网站的访问(速度)的问题,个别情况甚至会因 IP 异常导致封号。
Foxy Proxy 插件 来管理浏览器的代理设置,实现对不同网站的流量进行智能分流。在 Clash 中导入订阅后,进入 设置-Clash 设置-端口设置,将代理端口设置好(默认为 7897),并确保 允许局域网连接 已开启。

Foxy Proxy 插件 插件。Options 进入设置页面。Proxies 页面,点击 Add 添加一个新的代理配置,填写以下信息(切记点击 Save 保存):
Title: Clash VergeType: SOCKS5Hostname: 127.0.0.1Port: 7897
Clash Verge 代理配置下,点击 Proxy by Patterns 左边的 “+” 号添加新的规则。Title: AllPattern: *Title: BaiduPattern: *.baidu.com
注意,特定网站的流量并不止访问域名,还可能涉及到其他域名(如 CDN、API 等),因此需要根据实际情况添加更多的规则来覆盖这些相关域名。 可以通过
F12开发者工具中的Network和Source-Page面板来查看访问的域名,并将这些域名添加到 Foxy Proxy 的规则中。

|
|
Proxy by Patterns 规则后,要切换到 Proxy by Patterns 配置,才能让规则生效。
Options 页面,找到 Keyboard Shortcuts 部分,设置快捷键来快速切换代理配置.

2026-04-15 10:42:00
![]()
一开始搭建出博客后就基于 Aplayer 制作了大多博客都有的页内音乐播放器,主要功能实现起来简单,但是有两个痛点:1. 音乐资源存储;2. 切换页面断点续播。
在 AI 编程工具日益强大的当前,上个月很快利用 Pjax 等基本实现了第二个音乐续播的问题(副作用是部分页面 JS 失效,可能需要多刷新一次 T_T),而第一个问题在我临时采用另一个仓库存储音乐资源后,就再也没管了。
在这套方案下,每当我需要更新若干首曲目到我的音乐列表时,不仅需要手动上传音乐到音乐资源仓库,还需要手动更新博客仓库中的 Aplayer 配置文件。因此,想要自动化整个流程,实现 push 音乐后,自动更新博客配置并重新部署的想法一直萦绕在我脑海里。
在了解到 CI/CD 的概念,并轻度使用了 Github Workflow 后,今天在 AI 的协助下,终于实现了整个流程。
音乐资源仓库(music)和博客仓库(hugo)之间的数据流程如下:
用户提交更新音乐文件到music仓库后,触发 Github Workflow:1. 在music仓库中执行脚本,更新musicList.json文件;2. 将更新后的musicList.json文件提交到博客仓库(hugo)的特定分支;3. 博客仓库(hugo)的 Workflow 监听到该分支的更新后,自动部署博客。

music 仓库
在 .github/workflows/music-sync.yml 中配置 Workflow:
main 分支的 push 事件,且仅当特定路径(如 musics/**, lrc/**, test.py, .github/workflows/music-sync.yml)发生变化时触发;同时支持手动触发。test.py脚本生成 musicList.json。musicList.json 是否有变化,如果有则提交更新到 music 仓库。
|
|
test.py 脚本主要功能是扫描 musics/ 目录下的音乐文件,生成对应的 musicList.json 文件,并根据需要生成歌词文件(lrc/)。脚本会根据环境变量控制是否跳过歌词生成、是否要求必须有歌词等逻辑。
|
|
music 仓库中执行 Git 操作(如提交更新)以及触发博客仓库的 dispatch 事件。打开 Github 右上角头像,进入 Settings -> Developer settings -> Personal access tokens。
点击 Personal access tokens,然后点击 Fine-grained tokens。
点击 Generate new token,选择 Generate new token (fine-grained)。
配置 Token:
BLOG_REPO_DISPATCH_TOKEN
Only select repositories,然后选择 lihan3238/lihan3238.github.io (hugo博客)仓库。Contents 设置为 Read and write
生成 Token 后,复制 Token 的值。
进入 music 仓库的 Settings -> Secrets and variables -> Actions,点击 New repository secret。
Name: BLOG_REPO_DISPATCH_TOKEN,Value: 粘贴刚才复制的 Token,保存。
hugo 仓库
music_list_updated)和手动触发。
|
|
lihan3238/lihan3238.github.io 仓库,并且权限需要包含 Pull requests: Write 和 Contents: Write,以便 Workflow 可以自动审批和合并 PR。hugo 仓库的 Settings -> Actions -> General 中,确保 Workflow permissions 设置为 Read and write permissions,以允许 Workflow 执行写操作(如提交更改、创建 PR 等)。Github 的 workflow 功能非常强大,可以实现跨仓库的自动化操作,极大提升了开发效率和自动化水平。通过合理设计 Workflow 的触发条件和操作步骤,我们可以实现复杂的多仓库联动场景,如本次的音乐列表同步和博客部署流程。
由此看到,包括 K8s、github workflow 等 CI/CD 工具在内的现代开发工具链,已经成为提升开发效率和自动化水平的关键组成部分。学习和掌握 CI/CD 十分重要。
2026-03-31 22:37:00
![]()
春天都快过去了,这时候才提笔 2025 的总结,确是晚了,巧在又是在开往武汉的列车上,蜷缩在卧铺里,气氛到这里了,不写点什么是说不过去了。

2025 的六月清晰地把全年一分为二,上半年还是校园里年轻的白杨,下半年就深锁秦岭了。
说实话,现在回想起来,还是不敢相信短短半年时间里,居然留下了那么多回忆。一月和 lcy 的 粤旅250111算是毕业前狂欢的预告;三月和 cwt 吃遍 CUC 周边面馆、逛遍超市;四月的王府井、雍和宫还有万恶之源的青边口长城让我爱上了旅行;五月除了驻京办计划,珠江绿洲的顶层才是我心中的广州塔;速通完天津,六月还有东猴顶的日出……
珠江绿洲楼顶的夜里,我记得和 cwt 感叹为何直到毕业前一个月才发现了这个好地方,穿梭在老陕面馆和超市间,我们也后悔太迟开始享受生活,4年的本科生活除却失败的恋爱,真成了头轻尾巴尖重了。
我将我的魂魄留在了通惠河畔,这里也是 cwt 面试上如今令我羡慕的工作的地方,7月在武汉与重庆的小聚,26年初在河南的重逢,也让我的青春打赢了两场复活赛。
可是冰冷的现实总在每一次重温毕业随想时提醒起我:
再无青春,再无这般 CUC
在最后的日子里,对未来的恐惧也牢牢地占据了脑袋的一小片。从保研后的调研来看,在 NWPU 的日子非但不会好过,甚至可谓“龙场悟道”的一场修行。随着开学日子的逼近,纵使同 lcy 打了一个假期三角洲,暂时麻痹了青春的怀念,总得面对研究生的现实了。
记得在通惠河畔最后一次痛哭时,我料想硕士三年是剥夺一切娱乐与情感的苦修。不幸的是,现实恰如我料的一般,离地铁站的17km 拦住了我几乎所有的娱乐,一个接一个的横向与宛如铁笼的校园管理,更是让我彻底窒息:我非旦没有想错,甚至乐观了不止一点
仅仅三个月时间就打破了 CUC 四年带给我的自信,仅仅一个学期的煎熬,让我对西安的滤镜跌得粉碎。我再也不去幻想读博当教授,我只想早点就业脱离魔窟,找个轻松安逸的工作赶赶青春的尾巴。
外公的去世在高二那年,也许宇宙的内存容量有限,自那以后我获得了他释放的内存,自我意识清晰了起来。
今天出门的时候,看着姥姥不舍的样子,幸好我的内心还有几分波纹。记得姥姥说过,每次我和父母离开西安时,她都会守在窗台望向出小区门的必经之路,这两年倒是很少走这个门出了,我们都习惯从地下直穿到地铁站,我想她该好久没看到我们的背影了吧。
于是按下了电梯一楼的按钮,我站在楼下拼命地朝着十楼的角度挥手,房间里关着灯,街边的路灯也没有过年时候亮了,我更卖力地挥着:学历越来越高,也许前途越来越好,为什么连多陪姥姥几日都做不到呢?
我讨厌这个世界。
没啥好谈的,结论很清晰了,刷力扣、做项目、背面经、抢实习,早点就业,早点自由,早点完成所有的遗憾。
刚入学的时候,带着 CUC 给我的自信,我选了班长又选研会主席, openvpn 晾了一年终究是给我整会了,十月国庆在绿树上号三角洲时,上月游戏时长只有 6h。
lyx 带着我开始搞科研,说真的,他的努力我是真的佩服,从早干到凌晨,一连着就是几个礼拜,可惜我确实不是这块的料,也许又可能是努力晚了,随着我决定就业,科研和读博当教授之路算是彻底放弃了。
十月还拉着 lyx 爬了山,秦岭连绵,本想着后面两周一座山,脚伤让我又是半年走不好路。1月的时候,硬撑着和 1228 河南相见,险些腿断在龙门石窟,直到半个月前才算好了彻底。
经历了人生第一次挂科,也意味着我研究生生活彻底的崩坏,lihan 不得不现实起来,放弃了读博】放弃了恋爱、放弃了打游戏(好吧晚上还会打打),能偷偷学学技术,找个互联网私企外企都不自信了。
玩就玩好吧,总不能真让 NWPU 毁了青春,多少挣扎一下吧。
————2026.3.31 午夜再于返鄂列车上
2026-03-28 16:40:00
![]()
Clash Verge 在局域网内通过端口代理流量时,虚拟机或局域网内设备无法 ping 通代理端口。
局域网连接,确保允许来自局域网的连接。编辑文件,找到bind-address,将其值改为0.0.0.0,保存后重启 Clash Verge。export http_proxy=http://<Clash Verge所在设备的IP地址>:<代理端口>,并 ping google.com 来测试是否成功。2026-03-13 22:24:00
![]()
arr = []
arr = [0] * n
dp = [[0] * n for _ in range(m)] (严禁使用 [[0]*n]*m)arr = [x**2 for x in range(10) if x % 2 == 0]
+ 运算符: [1, 2] + [3, 4] -> [1, 2, 3, 4] (产生新列表,耗时)arr.extend([3, 4]) (等价于按顺序 append,$O(K)$)append(x) $O(1)$, pop() $O(1)$, arr[::-1] 翻转 $O(N)$, sort() 原地排序 $O(N \log N)$。s = set() (严禁使用 {},那是空字典)s = {1, 2, 3}
s1 | s2 或 s1.union(s2)
s1 & s2 或 s1.intersection(s2)
s1 - s2
add(x) $O(1)$, remove(x) $O(1)$ (不存在会报错), discard(x) $O(1)$ (不存在不报错)。d = {} 或 dict()
d = defaultdict(int) (访问不存在的键返回默认值)(from collections import defaultdict)d = {'a': 1, 'b': 2}
d3 = d1 | d2 (合并字典,同名键覆盖)d1.update(d2)
d.get(key, default) (安全读取), key in d $O(1)$。s[:4] # “leet” (前 4 个字符)s[-4:] # “code” (后 4 个字符)s[::-1] # “edocteel” ($O(N)$ 极速反转)s.find("etc") # 返回 2 (子串起始索引,找不到返回 -1,笔试首选)s.startswith("le")# Trues.endswith("de") # Trues.isalnum() # True (判断是否只包含字母和数字,双指针判断回文时必用)s.isdigit() # False (判断是否纯数字)s2 = " a b c "s2.strip() # “a b c” (默认袪除首尾所有空白符)s2.replace(" ", "") # “abc” (极其好用的全量替换)s.lower() # 全部转小写;如果本来就是小写、数字或符号,通常保持不变,例如 "abc123!?".lower() 还是 "abc123!?"
s.upper() # 全部转大写;数字和符号不变s.capitalize() # 首字母大写,其余小写s.title() # 每个单词首字母大写s.swapcase() # 大小写互换s.casefold() # 更强的大小写归一化,适合不区分大小写比较t = ()
t = (1,) (⚠️ 必须带逗号,否则 (1) 会被当作整数运算)t = (1, 2, 3)
visited.add((r, c));DP 记忆化 memo[(idx, weight)] = res。r, c = (0, 1) (多变量同时赋值)t[0], t[::-1] (与列表行为一致,但不能修改元素)
|
|
| 转换方向 | 语法 | 说明 |
|---|---|---|
| List -> Set | set(arr) |
瞬间去重,常用于降维打击 $O(N)$ 查找 |
| Set -> List | list(s) |
集合转回数组,常用于去重后排序 |
| String -> List | list("abc") |
变为 ['a', 'b', 'c'],因为字符串不可变,需转列表修改 |
| List -> String | "".join(arr) |
高效拼接,arr 内部必须全是字符串元素 |
| Dict -> List |
list(d.keys())list(d.values())
|
分别提取字典的键或值构成列表 |
| List <-> Tuple |
tuple(arr)list(t)
|
列表转元组以使其可哈希(存入 Set/Dict),或元组转列表以修改内容 |
处理国内笔试题(如牛客网)必备,避免 input() 导致超时或读取异常。
|
|
|
|
避坑:Python 中在 for 循环内修改 i 无效,会被下一轮自动重置。如需动态调整指针必须用 while。
|
|
|
|
|
|
|
|
float('inf'), float('-inf')。pow(a, b, mod) 快速幂取模算法(比自己手写快)。divmod(a, b) 同时返回商和余数。ord('a') -> 97, chr(97) -> ‘a’。s.split() (默认处理所有连续空白符并丢弃空串)。s.isalnum() (判断是否全为字母或数字,回文串常用)。s.find(sub) (找子串,找不到返回 -1,严禁使用 index() 因为会抛异常报错)。for i, val in enumerate(arr): (同时拿索引和值)。for x, y in zip(arr1, arr2): (双数组齐头并进)。arr.sort() # 默认升序,原地排序,适用于数字/字符串arr.sort(reverse=True) # 降序排序arr.sort(key=lambda x: x[0]) # 按元素的第一个字段排序(如区间、元组、二维数组)arr.sort(key=lambda x: (x[0], -x[1])) # 多条件排序:优先按第一元素升序,相同则按第二元素降序sorted(arr) # 返回新排序后的列表,不改变原数组sorted(arr, key=..., reverse=...) # 一切排序技巧均可用intervals.sort(key=lambda x: x[0]) # 区间按左端点排序,区间合并/覆盖问题高频a if 条件 else b (等价于 C/C++/Java 的 条件 ? a : b)res = x if x > 0 else -x # 取绝对值自带的 bisect 库是 $O(\log N)$,直接替代手写二分。
|
|
|
|
|
|
|
|
|
|
在你听到“红黑树”、“B+树”等高大上的名词时,需要首先明确工业界底层开发与 LeetCode 算法应试的绝对分界线:
std::set / std::map,在 Java 中使用 TreeMap,在 Python 中则依赖第三方库 sortedcontainers 或使用 bisect 模块结合列表模拟。以下是为你剥离冗余后,针对 LeetCode 场景的树与图 Cheat Sheet。
辩证本质: 树形结构中的“二分查找”。它将线性数组的查找效率从 $O(N)$ 降维至 $O(\log N)$,但代价是每次插入/删除都需要动态维护其严格的大小关系。
Python 模板:利用中序遍历验证 BST
|
|
辩证本质: 典型的“空间换时间”。通过将字符串拆解为字符并构建多叉树,将海量字符串的匹配时间复杂度,从 $O(N \times L)$ 极致压缩到 $O(L)$($L$ 为单个单词的长度),代价是需要极其庞大的节点内存。
Python 模板:Trie 的标准实现
|
|
辩证本质: 树其实是图的一种极度受限的特例(树是没有环的连通无向图)。图抛弃了“父子关系”的严格层级,允许数据之间存在任意的网状联系。
visited 集合来记录走过的节点,坚决不走回头路。辩证本质: 将一个存在依赖关系的网状图,拉平为一条线性的执行序列。如果图中存在环(如 A 依赖 B,B 依赖 A),则拓扑排序必然失败(发生死锁)。
Python 模板:利用 BFS (Kahn算法) 实现拓扑排序
|
|
机试时,先看题目给定的变量范围 $N$,直接反推该用什么算法:
已完成联网核对。前文提到的“有效的字母异位词”题号有误(LeetCode 24 实际为“两两交换链表中的节点”,242 才是“有效的字母异位词”),下表已修正。
这是一份纯粹用于检验 Python 数据结构熟练度的闭卷通关表:
| 题目号及名称 | 核心知识点 | 闭卷 AC 验收标准 |
|---|---|---|
| 125. 验证回文串 | 字符串过滤、切片翻转 | 熟练使用推导式 [c.lower() for c in s if c.isalnum()],用切片 s == s[::-1] 一行判断,不写双指针。 |
| 349. 两个数组的交集 | Set 的初始化与交集运算 | 严禁手写两层循环,必须一句话搞定:return list(set(nums1) & set(nums2))。 |
| 128. 最长连续序列 | Set 的 $O(1)$ 极速查找 | 将数组转为 set,彻底理解并利用 num in num_set 的 $O(1)$ 特性防超时。 |
| 242. 有效的字母异位词 | 词频统计 (Counter) |
严禁手写循环统计,直接引入 Counter,使用 return Counter(s) == Counter(t) 一行秒杀。 |
| 49. 字母异位词分组 |
defaultdict、Tuple 作为哈希键 |
熟练写出 d = defaultdict(list),深刻理解必须将排序后的字符串转为 tuple 才能作为字典的 key。 |
| 169. 多数元素 | 字典遍历 / API 获取极值 | 熟练手写字典遍历寻找最大值,或直接使用 Counter(nums).most_common(1)[0][0]。 |
| 20. 有效的括号 | List 作为栈的使用 | 熟练使用 append() 和 pop(),并用静态字典 {')': '(', ']': '[', '}': '{'} 优雅映射替代大量 if-else。 |
| 102. 二叉树的层序遍历 |
deque 实现 BFS |
倒背如流 q = deque([root]) 和 q.popleft(),严禁在此处使用 list.pop(0)。 |
| 200. 岛屿数量 | 多维坐标的 Tuple 打包与 Set 去重 | 在二维 BFS/DFS 中,极其熟练地将坐标打包成元组存入访问记录:visited.add((r, c))。 |
| 215. 数组中的第K个最大元素 |
heapq 核心 API |
熟练使用 heapq.heapify 以及维护大小为 $K$ 的小顶堆(heappush 和 heappop)。 |
| 347. 前 K 个高频元素 | 大顶堆技巧与复杂结构嵌套 | 结合 Counter 统计频率,并熟练写出“取负数塞入小顶堆模拟大顶堆”的操作:heappush(hp, (-freq, num))。 |
| 56. 合并区间 | 列表的高阶排序 (lambda) |
不看资料直接写出按左端点升序排列的定制规则:intervals.sort(key=lambda x: x[0])。 |
2026-03-11 14:23:00
![]()
在搭建个人博客的过程中,我一直有一个执念:希望网页底部的音乐播放器能够像网易云音乐那样,在页面切换时永不中断。
传统的静态博客(如 Hugo 生成的站点)每一次点击链接,浏览器都会重新加载整个页面 (Full Page Reload)。这意味着:
为了解决这个问题,我们需要引入 SPA (Single Page Application) 的概念,或者更轻量级的方案 —— Pjax (PushState + Ajax)。
Pjax 的工作原理非常直观:
<a> 标签的点击事件。Ajax 请求新页面的 HTML 内容。.main-container)。history.pushState 修改浏览器的 URL地址栏,使其看起来像正常跳转。通过这种方式,页脚 (Footer) 和 侧边栏 (Sidebar) 可以保持不变,驻留在其中的音乐播放器自然也就不会中断了。
首先在 <head> 中引入 Pjax 库(推荐使用 pjax 库而非老旧的 jquery-pjax):
|
|
这是最关键的一步。为了保证 Stack 主题的正常渲染,如果你直接替换整个 body,播放器还是会挂掉。我们需要精准打击。
我在 layouts/partials/head/custom.html 中进行了如下配置:
|
|
关键点:不仅要通过 CSS 选择器指定更新区域,还要自定义 switch 函数,确保 body 标签只更新属性而不重置内容。
实现 Pjax 只是第一步,真正的挑战在于副作用。
现象:跳转到 Timeline 页面,Mastodon 动态加载不出来。
原因:通过 innerHTML 插入的 HTML 片段中如果包含 <script> 标签,浏览器出于安全和规范考虑,通常不会执行它们。
解决方案:
我们将初始化代码封装为全局函数,并在 Pjax 完成事件 (pjax:complete) 中手动调用。
|
|
现象:虽然使用了 Pjax,但用户有时会习惯性按 F5 刷新,或者 Pjax 请求超时回退到普通跳转,这时候音乐还是会断,且进度归零。
解决方案:状态持久化 (State Persistence)。
利用 localStorage 在播放器每秒更新时记录状态:
|
|
在页面加载时(无论是 Pjax 还是普通加载),尝试恢复状态:
|
|
这里还有一个细节:audio 元素必须在元数据加载后才能 seek,所以需要监听 loadedmetadata 或 canplay 事件。
通过引入 Pjax 并配合精细的生命周期管理,我们成功在静态博客上实现了类似 SPA 的流畅体验:
折腾博客的乐趣往往不在于写文章本身,而在于通过解决这些具体的技术问题,窥探现代 Web 开发的冰山一角。