将EPUB书从繁体转向简体中文 Python 代码

file 一直在爱下电子书下些爽书看,不知道什么时候开始屏蔽了大陆的访问,然后下载也只有繁体,于是有了下面的代码。这段代码来自网络,无法确定出处,贴上来纯粹是把环境交代下,如果你有使用到可以参考。 Python 版本:3.9 Opencc库:pip install opencc-python-reimplemented==0.1.4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright 2022 ljmold.cn All rights reserved
#
# Authors: Carry Jin
# 将电子书中的繁体中文转换为简体中文
import opencc
import os
import zipfile
import shutil

def test_convert():
"""
s2t.json Simplified Chinese to Traditional Chinese 簡體到繁體
t2s.json Traditional Chinese to Simplified Chinese 繁體到簡體
"""
converter = opencc.OpenCC('s2t.json')
print(converter.convert('汉字acb')) # 漢字

converter = opencc.OpenCC('t2s.json')
print(converter.convert('漢字123')) # 漢字

def unzip_dir(zipfilename, unzipdirname):
"""解压zip文件"""
fullzipfilename = os.path.abspath(zipfilename)
fullunzipdirname = os.path.abspath(unzipdirname)
print("Start to unzip file %s to folder %s ..." % (zipfilename, unzipdirname))
# Check input ...
if not os.path.exists(fullzipfilename):
print("Dir/File %s is not exist, Press any key to quit..." % fullzipfilename)
inputStr = input()
return
if not os.path.exists(fullunzipdirname):
os.mkdir(fullunzipdirname)
else:
if os.path.isfile(fullunzipdirname):
print("File %s is exist, are you sure to delet it first ? [Y/N]" % fullunzipdirname)
while 1:
inputStr = input()
if inputStr == "N" or inputStr == "n":
return
else:
if inputStr == "Y" or inputStr == "y":
os.remove(fullunzipdirname)
print("Continue to unzip files ...")
break

# Start extract files ...
srcZip = zipfile.ZipFile(fullzipfilename, "r")
for eachfile in srcZip.namelist():
if eachfile.endswith('/'):
# is a directory
print('Unzip directory %s ...' % eachfilename)
os.makedirs(os.path.normpath(os.path.join(fullunzipdirname, eachfile)))
continue
print("Unzip file %s ..." % eachfile)
eachfilename = os.path.normpath(os.path.join(fullunzipdirname, eachfile))
eachdirname = os.path.dirname(eachfilename)
if not os.path.exists(eachdirname):
os.makedirs(eachdirname)
fd = open(eachfilename, "wb")
fd.write(srcZip.read(eachfile))
fd.close()
srcZip.close()
print("Unzip file succeed!")

def zip_dir(dirname,zipfilename):
filelist = []
if os.path.isfile(dirname):
filelist.append(dirname)
else :
for root, dirs, files in os.walk(dirname):
for dir in dirs:
filelist.append(os.path.join(root,dir))
for name in files:
filelist.append(os.path.join(root, name))

zf = zipfile.ZipFile(zipfilename, "w", zipfile.zlib.DEFLATED)
for tar in filelist:
arcname = tar[len(dirname):]
#print arcname
zf.write(tar,arcname)
zf.close()

def convert_file_to_chinese(file_path):
"""按行读取文件,存储到list集合中,转换元素的语言(繁体->简体),将结果写回到文件中"""
file_lines = []
converter = opencc.OpenCC('t2s')

with open(file_path, mode='r', encoding='utf-8') as f:
for line in f.readlines():
file_lines.append(converter.convert(line))

with open(file_path, mode='w', encoding='utf-8') as f:
f.writelines(file_lines)

def convert_epub_simplified(file_path):
"""将epub文件转繁体换成简体"""
if not (os.path.exists(file_path) or os.path.isfile(file_path)):
raise Exception("请检查文件路径:{}", file_path)

dir_name, file_name = os.path.split(file_path)
unzip_dir_path = dir_name + "/unzip"
unzip_dir(epub_file_path, unzip_dir_path)

files = find_content_files(unzip_dir_path)
for file in files:
convert_file_to_chinese(file)
new_file_name = file_name[0:file_name.rindex(".")] + "-简体.epub"
new_epub_file_path = os.path.join(dir_name, new_file_name)

zip_dir(unzip_dir_path, new_epub_file_path)
# os.remove(unzip_dir_path)
shutil.rmtree(unzip_dir_path)

def find_content_files(folder_path):
"""查询文件夹中所有需要修改的文件的路径,返回路径的列表
只转换epub文件的内容,搜索后缀为 'xhtml' 的文件
"""
result_files = []
for root, dirs, files in os.walk(folder_path):
# for dir in dirs:
# filelist.append(os.path.join(root, dir))
for name in files:
if name.endswith('xhtml'):
result_files.append(os.path.join(root, name))

return result_files

if __name__ == '__main__':
# 测试繁体简体转换
# test_convert()
epub_file_path = "/Users/jinzhao/Downloads/天字第一当.epub"
convert_epub_simplified(epub_file_path)

NAS 捡垃圾升级篇致敬2022

file

起因

最近上传了大量图片,因为有AI标签,5万张照片要三天才能索引好,结果一周后就挂了,一看是usb hub爆掉了,估计是主板温度高哪里脱焊了。这个板子是2012年的,2017年入手,已经服役五年,可以退役了。

升级

file 大概看了下垃圾频道,发现性价比还是1xx系列最高,也就是从三代u升级到6代,目前最新是12代,12代差不多要8百块,玩过nas的都知道,代差其实对性能影响没那么大,只有cpu的选型差距才大。比如上了服务器用的E3E5那肯定爆表,可是功率也是可怕的,J和N开头的低功率芯片能力又实在拉胯(如果没有很重的计算,只是看片和文件共享足够了),我选的是G系列,大概在50-60w,前面J系列大概是10-20w。cpu这次没有选丐版,而是升级到G4400,就是上图。

G4400 选择理由

  1. 便宜,70块钱,当然G3900才50块更便宜
  2. 之所以不选3900是因为保有量上4400更多,频率比之前用的三代u提高一点
  3. 最重要的是本来用来买sata扩展的20块因为主板sata口有富余可以省下来
  4. 如果追求极致的建议3900,gpu和4400是一样的

主板选华擎B150M pro4s

  1. 首先是适配matx
  2. 华擎一向质量好,耐操,而且sata口多最适合nas
  3. 如果买不到华擎,MSI也可以

关于M.2

目前还比较新,垃圾的性价比不高,下一代可以上

关于 ipmi

这是个好东西,但是主板上服务器版本就是成本会翻倍,细细考虑下确实不需要就放弃了。

捡垃圾

  1. 虽然价格是原价的一成,但是可以发挥出原来的功能
  2. nas大部分时候其实是待机状态所以低配更省电
  3. linux的驱动支持总是落后的,旧产品的支持反倒是最稳定的
  4. 为啥没买8代u,有人魔改了这个板子的bois支持到8代u,8代确实优秀,但是bug都是老外解决的,所以稳妥起见不动他了。

Wireguard + vmess组网经验和Netmaker弃坑

file

这几天构建vpn网络的血泪史

起因是zerotier挂掉了,到了经常不可用的状态,这个就头疼了,毕竟谁没几台服务器呢。 于是开始上openvpn,大名鼎鼎,但是配置过于复杂,好多好多警告,还有error,程序员真的忍不了。 折腾openvpn累的时候偶然看到了wireguard,立马被它精简的设计理念所吸引,于是马上上马。 配置过程非常顺利,但是从2peers到5peers的时候发现mesh有点机械重复的工作,想着应该有人解决这个问题,于是掉进netmaker这个惊天大坑。 蛋疼的是netmaker名气还挺大,所以爬坑的时候我还一步三回头,血泪啊。

Netmaker为什么被我抛弃

  1. Netmaker因为同时提供商业版,所以骗了不少人,但这是下面几点原因的根本。
  2. 本来只是一个配置管理+状态监控的服务,本来可以很轻巧,硬生生加进了coredns、加进了MQ,这里赞一下它用的MQ轻量级是真的,而且特别适合小规模应用,这些功能的耦合把wg本身轻巧和低到可以忽视的资源占用给糟蹋了
  3. 最新版本舍弃了grpc,搞笑的是另几个类似功能的项目还在用,配置管理的精髓就是pull和push就和git一样,之前它甚至才用wg内网而不是公网api来通信,这不是死循环了么
  4. 第3点是改进,这里就是退步了,它强制要求api所在节点启用wg,更坑的是docker版本还跟宿主不可见,据说是直接调用内核速度更快,快不快的我不知道,但是这个该死的节点根本不可用,谁会把一台服务器单独拿来当netmaker的管理节点呢?
  5. 没错还有第五点,上面的4官方已经承认是bug了,它在设置hub模式时每次都会重复添加管理节点的wg,哎
  6. 这个不是所有人都会碰到,它的很多默认规则无法定制化,这时候wg的灵活也被糟蹋了,常见的场景就是wg的出口我打算通过我的代理走,netmaker就无法做到。

Wireguard为什么还要上vmess

wg非常容易识别,虽然咱不干坏事,但是容易被殃及池鱼,所以海外主机的组网最好通过vmess走。 建议,虽然理论上不必要,跨外网的最好另建一个net,比如我的组网方式就是大陆abc服务器是一个net,然后abc和海外d主机零件一个hub 中继网络。 这里就体现出wg强大的地方了,精简的设置外,可以通过叠加多个网路实现复杂的路由功能,虽然很死板,但是类似netmaker这样的总有一天能开窍,知道自己正确的roadmap足可以解决手动配置的痛苦。

wireguard 的dns设置

本来linux dns管理挺麻烦的,wg顺带支持的这个小功能可以很稳定的重写服务器dns配置,然后就舒坦了。

Drone 加速一: 解决Clone速度慢的问题 (drone v2)

file

前提

应该不少小伙伴是把drone和git托管放在一起的,这时候默认的clone可能只是几秒钟,那请忽略本文。但如果跟我一样不在一个服务器上,而且有的项目比较大的时候可能手动clone会更好一些。

手动clone的几个问题

  1. 官方提供了git插件,如果能通过clone depth改为1解决的话也不需要手动clone;
  2. 发现很小的项目卡在clone往往要30s,很不理解,难道是跨境的问题?
  3. 当手动触发build的时候也要30s就难以忍受了。

手动clone的思路

  1. 新建一个用于存放git仓库的目录/srv/git
  2. clone你的项目/srv/git/projectA
  3. map 服务器的.ssh目录用于鉴权,当然也可以用drone的secret
  4. 在drone yml中禁用clone
  5. 增加一个pull step用于同步更新,取代系统默认的clone,并拷贝到drone工作目录

.drone.yml示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
kind: pipeline
type: docker
name: default

clone:
disable: true

steps:
- name: pull
image: alpine/git
volumes:
- name: ssh
path: "/root/.ssh"
- name: git_repo
path: "/srv/git_repo"
commands:
- cd /srv/git_repo/projectA
- git pull origin main
- cd -
- "cp -R /srv/git_repo/carry-dockers/* ./"
volumes:
- name: ssh
host:
path: /root/.ssh
- name: git_repo
host:
path: /srv/git_repo

Drone 加速二: 解决Build占用资源过高的问题 (drone v2)

file

发现

突然发现服务器反应慢了,就在granafa瞅了一眼,结果cpu96+,吓了一跳,一想也就是drone跑的没有限制资源使用。drone-runner这个名称有误导,开始以为是在这个容器中build,回念一想它定然是重新开一个container跑,结果一看真的是,所以用docker-compose中的deploy是无法限制资源的。这个所谓的runner更多的是一个agent,事实上一开始它就叫drone-agent,用于创建真正的runner。

CPU和内存限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
drone-runner:
image: drone/drone-runner-docker:1.8
container_name: drone-runner
ports:
- "3000:3000"
depends_on:
- drone
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:rw
environment:
- TZ=Asia/Shanghai
....
- DRONE_CPU_PERIOD=100000
- DRONE_CPU_QUOTA=150000
- DRONE_MEMORY_LIMIT=500*1024*1024*8
  • DRONE_CPU_PERIOD参数最大值100000,代表一个cpu的使用时间,如果希望单个cpu使用不超过75%则改为75000
  • DRONE_CPU_QUOTA和上面的参数配合使用,代表了所有cpu的使用时间总和,不知为什么没有提供cpus那个参数,会好理解的多
  • DRONE_MEMORY_LIMIT内存限制,这个很好理解,单位是byte,其中500_1024_1024*8代表500MB

个人服务器常见服务内存使用参考

file

说明:服务会随着使用更新,所有服务均在待机状态下,也就是非高负载均值。内存MB,cpu为双核百分比。

服务选择原则:

  1. 如果是C写的服务资源几乎可以忽略,当首选比如nginx
  2. C写的毕竟少,第二必须是rust,但是服务更少,等并入linux内核后会很多
  3. GO的应用这两年大量出现,资源占用也很低,git托管和CI改为GO后云资源大量节省,java滚蛋

常见服务建议:

  • 数据库:postgres是趋势有的程序只有pg才能火力全开,但是mysql目前还是明显读取效率更好一点,内存富裕建议都上
  • web服务器:必选是nginx,如果内存富余50MB可以上traefik,自动LE证书和服务发现,幸福指数满满
  • web程序:首选go,其次flask,最后才是java、php等其他
  • 监控:不是必须的,但是有了监控那个b格蹭蹭的上,对各个服务器资源占用会更直观,而且目前云厂商监控都开始收费了
  • 单向NAT打洞推荐frps,稳定压倒一切
  • 双向NAT相当复杂请参考stun技术
  • vpn私域推荐wireguard,5.6内核开始支持,缺点是还不适合上集群,不过已经有第三方客户端在做这块了,对于个人的服务器来说绝佳,甚至可以full mesh
  • Git托管:毫无疑问有钱上gitlab,最好的git托管,ruby写的资源占用极高4g内存打底。个人使用推荐gogs,占用最少,缺点是活跃度很低,不支持搜索,所以我切换到了gitea也是go写的
  • docker是必须上的,swarm国外个人很流行,暂时docker-compose可以cover大部分需求,如果都是微小服务器比如内存只有1-2g就不用考虑swarm甚至k3s这些了

常见服务内存占用 (配置说明:内存/CPU)

服务名 描述 内存 CPU
nginx 一个fastcgi站点和一个静态站 5.45MB 0.3% 常驻
traefik 两个站点,开启gzip和ssl 30.5MB 1.63% 常驻
wordpress 主程序常见配置 137MB 6% 非常驻
flask 精简程序 0.5MB 0
flask 大程序,一般组件 31.04MB 0.05% 常驻
gitea 25个库开启索引,如果不索引建议使用gogs 131.9MB 0
gogs 25个库 26MB 0
portainer_agent 只有agent端 5.2MB 0
postgresql 一个库,版本14 54.44MB 0
mysql 四个库版本5.7 258MB 0.1 常驻
redis 一个库 5MB 0.09常驻
haproxy 监听一个端口 3.113MB 0.01
frps 监听一个端口 9.578MB 0
dnsmasq 10.81MB 0.31% 常驻
cadvisor 16MB 1.4%-5%
grafana 四个仪表 45MB 0.01%
prometheus 七八个数据源 204MB 0
alertmanager 一个提醒 13MB 0.1
node-exporter 11.22MB 0-1.5%
其他exporter 7-9MB 0

如何在git中保持一个空文件夹 / 忽略文件夹下的文件但保持文件夹本身

file

起因

最近非常注重一些操作中的细节,以前总是skip一些无伤大雅的小坑,后来觉得一次skip次次skip是不行的。这个问题应该每个使用git的都有碰到过,只是搜索解决方案略显麻烦

问题

有时候出于权限或者不希望运行中去创建文件夹的时候我们就希望保留类似node_moulds或者一些私有secrets文件夹,但是git却不支持这么操作。是的,官方就不支持空文件夹,于是就有了下面一个精彩的workround。

解决

  1. 在根.gitignore中删除空文件夹的排除规则

  2. 在空文件夹下新建文件.gitignore,内容如下

    1
    2
    3
    4
    # Ignore everything in this directory
    *
    # Except this file
    !.gitignore

极简升级postgresql by docker 9.4-14 稳稳的幸福

目前最新版postgresql是15beta4,正式版最新是14

pg确实好用,多年未动,最近整理服务器时pgadmin弹窗才发现最新的客户端不适配了都,于是就想着升级一下,9.4看上去像是上个时代的产物了。

须知

  1. 数据库升级跟库升级一样,是一件痛苦的事,好在postgresql有提供pg_upgrade,它要求逐个版本升级
  2. 版本9时遵循A.B.C版本号,也就是9.6.1-n无需升级可以直接用,版本10开始10.x无需升级可以直接用。所以我的9.4需要先升级到9.6才可以继续大版本升级
  3. pg升级的方式都是面向集群的,所以如果不用docker要开两个实例很麻烦,备份同理,幸运的是有好心人把升级都打包好了tianon/postgres-upgrade

升级步骤

下文中的9.4是我的版本号,如果你的是9.x请替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

cd 你的pg目录,就是data文件夹
mkdir -p ./9.4
mv ./data ./9.4/
# 最好先zip备份下数据
zip -r 9.4.zip 9.4
mkdir -p ./9.6/data
mkdir -p ./14/data
# 目录准备完毕,开始升级
#先升级到9.6
docker run --rm \
-v $PWD:/var/lib/postgresql \
tianon/postgres-upgrade:9.4-to-9.6 \
--link
# 再升级到14
docker run --rm \
-v $PWD:/var/lib/postgresql \
tianon/postgres-upgrade:9.6-to-14 \
--link

最谨慎的升级版本(建议大系统采用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ mkdir -p postgres-upgrade-testing
$ cd postgres-upgrade-testing
$ OLD='9.4'
$ NEW='9.5'

$ docker pull "postgres:$OLD"
$ docker run -dit \
--name postgres-upgrade-testing \
-v "$PWD/$OLD/data":/var/lib/postgresql/data \
"postgres:$OLD"
$ sleep 5
$ docker logs --tail 100 postgres-upgrade-testing

$ # let's get some testing data in there
$ docker exec -it \
-u postgres \
postgres-upgrade-testing \
pgbench -i -s 10

$ docker stop postgres-upgrade-testing
$ docker rm postgres-upgrade-testing

$ docker run --rm \
-v "$PWD":/var/lib/postgresql \
"tianon/postgres-upgrade:$OLD-to-$NEW" \
--link

$ docker pull "postgres:$NEW"
$ docker run -dit \
--name postgres-upgrade-testing \
-v "$PWD/$NEW/data":/var/lib/postgresql/data \
"postgres:$NEW"
$ sleep 5
$ docker logs --tail 100 postgres-upgrade-testing

$ # can now safely remove "$OLD"
$ sudo rm -rf "$OLD"

WireGuard 配置手册

file

为什么要用wireguard

必用理由就一个,它被并入linux内核,效率极高。我也是因为openvpn使用中发现警告太多,配置过于复杂,然后发现好多人转向wg,配置非常精简且灵活。

真正的p2p

跟其他vpn不一样的是它没有严格意义上的server端或者client端,所以你既可以组建单网管的,也可以实现mesh互通。

几个其他教程中有误或者已经过时的地方

  1. mesh时要删除preshared key,这是个误解,正确配置preshare key可以mesh;
  2. 有人说节点变多了配置就会是灾难,这里有两个问题,一是wg只是热重载那么更新配置不会中断连接,也就是底层具备配置更新的能力,二是目前已经有项目在做这块功能了,蛮好的,wg保持他的稳定、精简即可
  3. 各大平台都有官方的gui端,至于linux要么必须要是5.6+内核,如果是ubuntu,5.4+即可
  4. 只要指定了endpoint,它就是主动去勾搭,另一端默默享受就好
  5. AllowIPs,它会自动转发相应网段到wg0网卡,换一句话说是否暴露本地lan网段就靠它
  6. 其实wireguard的配置生成器很多,可以找一个上手,会了以后还是自己动手最灵活

常用命令

debian安装apt install wireguard-tools 私钥生成wg genkey > privatekey 公钥生成wg pubkey < privatekey > publickey 或者一步到位生成wg genkey | tee privatekey | wg pubkey > publickey 共享密钥生成(非必要操作)wg genpsk > presharedkey 查看wg状态wg或者wg show 激活配置/开始wg-quick up wg0 关闭配置/关闭wg-quick down wg0 生成二维码cat client.conf | qrencode -o - -t UTF8 重启服务systemctl restart wg-quick@wg0 热更新配置wg syncconf wg0 <(wg-quick strip wg0)

开机启动

1
2
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0

一个将Git库从master切换到main并推送的脚本

file

因为黑人的原因,不建议再使用master作为分支名,正好在迁移自己的git库,索性一起改过来,下面是写的一个脚本,使用方法:网站上新建好库,cd到git库们所在的文件夹,touch一个upload.sh,执行. ./upload.sh 【库名】

1
2
3
4
5
6
7
cd ./${1}.git
git remote remove origin
git remote add origin git@git.xxxx:xxx/${1}.git
git branch -m master main
git push -u origin main
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
cd ../
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.