Pages

Monday, 20 January 2020

自建Anki同步服务器

什么是Anki

Anki是一个帮助记忆的Flash Card(教学卡片)软件,比如正面是英语单词,背面显示意思,然后让你选择,你觉得这个单词是容易、困难、或者完全不会,根据你选择的难易程度,这个单词会在接下来的不同时间出现,比如你点的困难,它可能1分钟后又出现了,如果你选的简单,它可能几天后才显示,而具体要几天后才出现这个是可以自己设置的。
Anki是开源的软件,使用python编写,在Windows、Mac、Linux三大平台通用,iOS和Android也有对应的软件,另外还有Web版,除了iOS版之外的所有版本都是免费的。
iOS版收费,是因为电脑版和iOS版都是同一个作者,他是全职做这个的,所以必须要有收入,好像是162还是163块RMB来着,安卓版免费是因为安卓版是另一个业余团队做的,所以安卓版可以自定义同步服务器而iOS版不可以。
Anki官网:https://apps.ankiweb.net
可以看看这些介绍的文章:https://zhuanlan.zhihu.com/-anki

自建AnkiServer原因

默认的Anki服务器同步很慢,而且据说这是个人项目,数据在别人手里也不可靠,说不定哪天丢了也有可能,所以我们自己搭建自己的服务器。但有个缺点就是iOS版的Anki不支持自定义服务器,因为iOS版是官方出品,

AnkiServer github

Anki和anki-sync-server都是python写的,这是python3版本的:anki-sync-server,由于python2版本的AnkiServer已经没有人维护,所以我们最好使用python3版本的。

安装epel

如果你不确定是否安装过epel,运行以下命令安装(如果已经安装它会自动识别,不会重复安装的):
yum -y install epel-release
Bash

安装python3

执行以下命令,搜索python3版本:
yum search python3
Bash
搜索结果可能有python34-xxxxpython36-xxxx等等,这个34,36表示3.4,3.6版本,安装其中最新的版本(当前2019.03.13,最新是python36),所以我们安装python36:
yum -y install python36
Bash
如果以后有37甚至38,安装的时候把36改成37或38即可。
安装后执行以下命令以确认是否安装成功(如果正常,则会出现Python 3.6.x的版本版本,同理,如果你以后有新版本python37,那么出来的就应该是Python 3.7.x):
python36 -V
Bash

安装pip3

从python官网下载get-pip.py(这个只是暂时使用的,可以下载到任何地方,使用完就可以删除):
wget https://bootstrap.pypa.io/get-pip.py
Bash
使用python36运行get-pip.py,以安装python36对应的pip3(注意此时要在get-pip.py所在的目录下执行以下命令):
python36 get-pip.py
Bash
安装完成后,python36的pip应该会被安装到以下路径:
/usr/local/bin/pip
Bash
为了与python2.7的pip区分,我们把它修改一下名字:
mv /usr/local/bin/pip /usr/local/bin/pip3
Bash
运行pip3 -V试试,如果是指向python3.6的路径,说明一切正常。
然后就可以删除get-pip.py文件了:
rm get-pip.py
Bash
使用pip3安装webob依赖:
pip3 install webob
Bash

安装AnkiServer

由于AnkiServer是用python语言写的,python是脚本语言,所以,所谓的安装,就是把代码clone到本地,你想放在哪里都无所谓,我是放在/usr/local/anki-sync-server下,你也可以放在~/anki-sync-server下:
git clone https://github.com/tsudoko/anki-sync-server.git /usr/local/anki-sync-server
Bash

安装Anki

注意,AnkiServer是AnkiServer,Anki是Anki,这不是同一个东西,AnkiServer依赖于Anki,所以下载AnkiServer后,还必须下载Anki,Anki下载后要放在anki-sync-server目录下的一个叫anki-bundled的目录里。
那Anki从哪里下呢?
方法一:因为Anki本身也是python写的,它也是开源的,它的github地址是:https://github.com/dae/anki, 所以你可以从这个地址里clone到anki-bundled里。
方法二:直接下载anki-sync-server项目下的anki-bundled @ cca3fcb,它其实本身就是Anki的github源码链接,只不过这个链接不是Anki的主分支,而是anki-sync-server作者测试过的可用的稳定分支,所以我们就使用这种方法下载,不要用方法一。

疑问

问:有人可能会觉得奇怪,为什么我clone完anki-sync-server后,anki-bundled是空的?为什么它里面的内容不会下载下来?
答:因为它是一个链接,属于另一个版本库,你可以点它试试,它已经跳转到anki项目里了

进入anki-sync-server目录,注意你自己下载的目录也许跟我的不同:
cd /usr/local/anki-sync-server
Bash
运行以下命令,用于下载git子模块代码(即Anki的代码,这一步执行完,就安装了Anki了):
git submodule update --init
Bash
进入以下目录,你会发现已经不是空的了:
cd anki-bundled
Bash
删除“requirements.txt”文件中的“pyaudio”:
先用以下命令打开文件,然后用方向选把光标指向pyaudio那一行,然后按dd即可删除,最后输入:x然后回车(注意,都要英文输入法下操作):
vim requirements.txt
Bash
执行以下语句,用于安装Anki的依赖:
pip3 install -r requirements.txt
Bash
vim编辑ankisyncctl.py文件,它的第一行默认是:
#!/usr/bin/env python
Python
把它改成:
#!/usr/bin/env python36
Python
表示用python36运行ankisyncctl.py脚本。
这样就算安装调试完了,运行以下命令查看使用方法:
./ankisyncctl.py usage
Bash
如果正常,出来的结果应该是这样的:
usage: /usr/local/bin/ankisyncctl  []

Commands:
  adduser  - add a new user
  deluser  - delete a user
  lsuser             - list users
  passwd   - change password of a user
到这里,AnkiServer就安装完毕了。

使用ankisyncctl.py(主要是管理账号)

添加一个用户名为“zhangsan”的用户(它会让你输入密码):
./ankisyncctl.py adduser zhangsan
Bash
查看已添加的用户列表:
./ankisyncctl.py lsuser
Bash
修改用户名为“zhangsan”的用户的密码:
./ankisyncctl.py passwd zhangsan
Bash
删除用户名为“zhangsan”的用户:
./ankisyncctl.py deluser zhangsan
Bash
因为没有修改用户名功能,如果你要修改用户名,那么你要先删除,再重新添加。

启动anki-sync-server

执行以下命令启动anki-sync-server,它的服务名称叫ankisyncd,注意,以下命令必须在anki-sync-server文件夹下执行,因为它要找该文件夹下的ankisyncd文件夹的:
python36 -m ankisyncd &
Bash
如果启动正常,它应该会有类似以下的提示:
[2019-03-14 06:28:17,456]:INFO:ankisyncd:Loaded config from /usr/local/anki-sync-server/ankisyncd.conf
[2019-03-14 06:28:17,461]:INFO:ankisyncd:Serving HTTP on 0.0.0.0 port 27701...
查看是否正常启动:
ps -ef | grep ankisyncd
Bash
如果你看到有类似以下的结果,说明运行正常:
root      5838  3130  0 06:28 pts/0    00:00:00 python36 -m ankisyncd
注意:一般情况下,在启动命令后加&表示在后台启动,但是,在这里不行,目前我也不知道原因,如果你想后台启动,有两种方法,一种是使用nohup(注意该命令还是必须在anki-sync-server文件夹下执行):
nohup python36 -m ankisyncd >> /var/log/ankisyncd.log 2>&1 &
Bash
注意,即便是用nohup,也不能直接关闭窗口,而是用exit命令来退出窗口,你需要执行两次exit命令才能退出窗口。
然后把它加入开启启动:
echo "nohup python36 -m ankisyncd >> /var/log/ankisyncd.log 2>&1" >> /etc/rc.d/rc.local
Bash
第二种后台运行的方法就是使用supervisor,注意,如果你用了“nohup”这种方法,下边的“使用supervisor管理进程”就不用看了,两种方法选一种。

关闭防火墙

为了避免端口不开放导致的问题,使用以下命令关闭防火墙:
systemctl stop firewalld
systemctl disabled firewalld
Bash
当然如果你懂防火墙怎么用,你也可以开放27701端口。

检测自建AnkiServer是否能正常访问

假设你的服务器ip是12.34.56.78,那么你在浏览器访问http://12.34.56.78:27701,会显示Anki Sync Server,那就说明是正常的,如果打不开,那就有问题。

电脑端设置

以下配置在mac和win测试通过(版本为2.1.11),由于我没有Linux桌面版Linux系统,所以未在Linux上测试,不过原理都是一样的。
注意:由于我本地都是安装的英文版的,所以下面我说的点击哪个菜单我都是用英文的,如果你安装的中文版,请自行对应,相信这几个英文难不倒你。
打开Anki,点击顶部菜单栏的ToolsAdd-ons会打开插件窗口,然后再点击插件窗口的View files按钮,会打开插件安装目录。在该目录下新建一个文件夹ankisyncd,然后在里面新建一个文件,命名为__init__.py,打开__init__.py,把以下命令粘贴进去,然后把其中的12.34.56.78修改成你服务器的ip,如果你修改过ankisyncd.conf里的“port”(即端口),这里的端口也要与ankisyncd.conf中的“port”对应,然后保存:
import anki.sync, anki.hooks, aqt

addr = "http://12.34.56.78:27701/" # put your server address here
anki.sync.SYNC_BASE = "%s" + addr
def resetHostNum():
    aqt.mw.pm.profile['hostNum'] = None
anki.hooks.addHook("profileLoaded", resetHostNum)
Python
最后重启anki,即把anki软件关掉,再重新打开。
点击Sync(即“同步”)测试,如果正常,它会提示你输入账号密码,这个账号密码就是你前边用./ankisyncctl.py adduser添加的账号和密码了。

安卓端

点击左上角三道杠→点击设置→点击高级设置→点击自定义同步服务器→勾选使用自定义同步服务器,然后下边有同步地址媒体文件同步地址
同步地址:http://12.34.45.78:27701(把ip改成你的服务器ip)
媒体文件同步地址:http://12.34.45.78:27701/msync(改成你的服务器ip),即媒体文件同步地址是在同步地址后面添加/msync(其实这个名字是在服务器的配置文件ankisyncd.conf中的base_media_url选项指定的)。
设置好之后,点击同步按钮,正常的话会让你输入账号密码,输入与电脑端相同的账号密码,即可进行同步。

iOS端

App不支持,无解,换手机吧😄

使用supervisor管理进程

supervisor是一个进程管理软件,启动后可以通过网页来查看进程状态并管理进程,如果你已经使用前面的nohup方式启动ankisyncd,那么这一步就不用做了,否则就要做这一步, 管是nohup还是supervisor,都是为了使ankisyncd能在后台运行。

安装supervisor:
yum -y install supervisor
Bash
编写配置文件:
vim /etc/supervisord.d/ankisyncd.ini
Bash
i切换到输入状态,把以下内容粘贴进去,输入:x保存:
[program:ankisyncd]
command=/bin/python36 -m ankisyncd              ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1                    ; number of processes copies to start (def 1)
directory=/usr/local/anki-sync-server/                ; directory to cwd to before exec (def no cwd)
;umask=022                     ; umask for process (default None)
;priority=999                  ; the relative start priority (default 999)
autostart=true                ; start at supervisord start (default: true)
autorestart=true              ; retstart at unexpected quit (default: true)
startsecs=3                  ; number of secs prog must stay running (def. 1)
;startretries=3                ; max # of serial start failures (default 3)
;exitcodes=0,2                 ; 'expected' exit codes for process (default 0,2)
;stopsignal=QUIT               ; signal used to kill process (default TERM)
;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
;user=chrism                   ; setuid to this UNIX account to run the program
;redirect_stderr=true          ; redirect proc stderr to stdout (default false)
stdout_logfile=/var/log/ankisyncd.log        ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
;stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false   ; emit events on stdout writes (default false)
stderr_logfile=/var/log/ankisyncd.error.log    ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10     ; # of stderr logfile backups (default 10)
;stderr_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false   ; emit events on stderr writes (default false)
;environment=A=1,B=2           ; process environment additions (def no adds)
;serverurl=AUTO                ; override serverurl computation (childutils)
Ini
用以下命令打开supervisord的配置文件:
vim /etc/supervisord.conf
Bash
找到[inet_http_server],把它下面的三个参数修改成以下的样子,其中username和password修改成你自己想要的用户名和密码(注意:这个用户名密码与ankiserver的租房名密码无关,这个是supervisord这个软件的网页版的登录密码):
[inet_http_server]
port=*:9001
username=zhangsan
password=192837465
Ini
启动supervisord:
/usr/bin/supervisord -c /etc/supervisord.conf
Bash
把supervisord加入开机自启动:
vim /etc/rc.local
Bash
然后把下面这句添加进去:
/usr/bin/supervisord -c /etc/supervisord.conf
Bash
浏览器里输入:http://12.34.56.78:9001(ip要修改成你服务器的ip), 输入上边设置的账号和密码即可从页面中访问查看你的anki同步服务进程ankisyncd.