Project RC

一只腊鸡的技术成长。

VeriPress 主题集合

创建于
分类:Misc
标签:VeriPressBlogThemeCollection

目前 VeriPress 已经有了好几个官方和第三方主题,为了便于查找,这里做一个集合列表。

主题名 预览/截图 简介
default Preview 为博客设计的默认主题
clean-doc Preview 为文档设计的默认主题
fenki - 为 Wiki 设计的默认主题
richardchien/veripress-theme-r - 一个简洁的纯色博客主题
richardchien/veripress-theme-light Preview 一个风格小清新的博客主题,样式比较轻快
txperl/Story-for-VeriPress Preview 一个适合写作与阅读的博客主题

以上的主题均可以直接通过:

$ veripress theme install 主题名

来安装,并可通过:

$ veripress theme update 主题名

来更新。

使用位置模拟器进行步道乐跑的跑步打卡

创建于
分类:Misc

这学期学校突然非常贴 zhi 心 zhang 地引入了「步道乐跑」这一软件来完成大学生阳光体育的政策要求(以前是去体育场刷卡),简单了解了一下发现这个软件非常臭名昭著——随机打卡点、只使用 GPS 定位导致路线记录不准、对较新版本 Android 系统兼容性不好等。

对于这种强迫我们在危险的大马路上跑步、还经常需要停下来低头看手机的黑恶势力软件,乖乖听话是不可能的,于是花了点时间研究了一下另类方法。

准备

首先你需要准备一部已经 root 的 Android 手机,具体怎么 root 这里就不说了。

注意,如果你没有接触过 root,那么需要小心谨慎地详细了解一下,因为 root 操作比较危险,会让手机变得不安全,尽量在备用机上操作。

我一开始尝试了在夜神模拟器里面运行,结果步道乐跑会检测蓝牙状态,夜神模拟器不支持蓝牙,于是放弃了,不过 Genymotion 这种是有可能可以的,因为它也模拟了蓝牙等传感器。

安装 Xposed

这里 页面底下的 de.robv.android.xposed.installer_v33_36570c.apk(实际版本号可能不同)链接下载 Xposed Installer,然后安装。

打开之后会看到如下红色感叹号:

红色下面是下图:

点「Version 89」(具体版本可能不同),然后弹窗中选「Install」即可安装,安装之后需要重启手机。

完成之后红色感叹号会变成绿色的对号:

此时即为安装成功。

安装大牛助手(位置模拟器)

大牛助手是一款利用 root 权限模拟定位的软件,目前最新版是收费的,但旧的 1.1.7 版本是免费的,虽然它强制更新,但可以用 Xposed 模块来强行关闭弹窗。

1.1.7 版本可以从 这里 下载。

安装之后别急着打开,开了也千万不要更新,直接退出。

然后下载安装 对话框取消 Xposed 模块,安装之后需要在 Xposed Installer 的「模块」中勾选,然后重启手机。

重启之后打开对话框取消模块,勾选「开启模块」和「开启增强模式」,然后在下面的应用列表找到大牛助手,进行如下配置:

然后点保存并重启,之后打开大牛助手应该就不会出现强制升级的弹窗了。

安装步道乐跑

就在应用商店正常安装即可,不过我安装的是一个旧版本,并不确定新版本中下面的方法是否还有效。

旧版本下载地址:链接: https://pan.baidu.com/s/1GMiGWocpXEJqX8yfypRIIA 提取码: abib

如果你安装的是旧版本,那么需要像上一步那样对步道乐跑也启用对话框取消,否则它也会弹窗强制更新。

防止步道乐跑检测到位置模拟器

步道乐跑会「检测」手机中是否有运动模拟器,经过一番研究,发现实际上它是通过获取手机的应用列表来判断的,当发现 Xposed Installer、大牛等应用,就会提示你要诚信跑步。

好在大牛助手有 app 防检测功能,进入大牛的 app 防检测,添加大牛和 Xposed Installer 两个应用,选择防止步道乐跑检测,即可。

模拟跑步

具体模拟跑步时,首先进入步道乐跑,然后点进跑步界面,当出现地图之后,就会出现第一个打卡点。

然后打开大牛,使用「路线模拟」功能添加两个点,第一个点是你的当前位置(或任意学校内的位置),第二个点是步道乐跑的打卡点,然后在「配置」页面调整速度,根据步道乐跑要求的平均配速(3~9 分钟 1 km),设置速度为 10 km/h 左右,然后保存并开启路线模拟。

这时候大牛就已经开始模拟位置的变化了,切换到步道乐跑,点开始跑步,然后等它打卡成功,之后出现下一个点,回到大牛,停止路线模拟,删掉第一个点,再添加新的打卡点,然后再次开启模拟,再切回步道乐跑。

第三个打卡点以此类推,三个点全部打完就可以结束跑步了。为了避免在大牛那边的操作耗时导致配速不符合要求,可以适当提高大牛的模拟路线的速度,或者在切换到大牛之前先暂停跑步。

运动模拟器?

步道乐跑最让人诟病的就是它只通过 GPS 来确定跑步路线和距离,而没有通过陀螺仪等运动传感器来辅助判断,也正因为这一点,不需要开运动模拟器也可以模拟跑步,不过,为了保险起见,可以安装 Xposed 模块 运动模拟器 来模拟运动,具体方法这里就不详述了,和「对话框取消」的安装方法基本上一样。

最后

其实步道乐跑这软件,或者说大学生阳光体育运动,它们本身的动机是好的,现在大学生天天宅在宿舍里确实需要运动,但这样强制通过跑步的方式来运动,为了避免作弊还一定要在校园内随机打卡地点,还花了很多精力去检查系统环境、检查模拟器,就显得有些病态了,搞得本来就喜欢运动的人也需要百般折腾。

虽然这里给出了破解方法,但还是建议抽出一些时间去真实地做一些运动,毕竟身体健康还是很重要的。

使用 Netlify 部署 VeriPress

创建于
分类:Misc
标签:BlogVeriPressNetlify

博客好久没更新,最近突然想重整一下,于是重新写了个 主题,样式是抄的以前 LiveChat 的官方博客的样式(现在他们的博客已经不长这样了)。

然后之前把博客迁移到 GitHub Pages,用 VeriPressgenerate 命令生成之后 push 到 GitHub,现在觉得还是有点麻烦,从 VuePress 那边学到了部署到 Netlify 这招,就想着能不能把 VeriPress 也部署到 Netlify,后来折腾了一下发现可行。

首先需要看 VeriPress 生成静态文件的步骤(已经创建 instance 目录的情况下):

veripress theme install theme-name
veripress generate

这里安装主题这一步是必须的,所以导致 VeriPress 最少也需要两行命令,再加上主题允许自定义的 custom 目录,实际可能需要更多命令才能完成静态文件的生成,于是考虑用一个 shell 脚本,这样在 Netlify 那边的 build 命令就只需要填写 bash build.sh 就行了,我的 build.sh 如下:

#!/usr/bin/env bash

veripress theme install richardchien/veripress-theme-light --name light

theme=`python -c "import config; print(config.THEME)"`
rm -rf ./themes/$theme/templates/custom
cp -R ./theme-custom ./themes/$theme/templates/custom

veripress generate --app-root=/

然后再添加两个文件 runtime.txt

3.6

requirements.txt

veripress

分别告诉 Netlify 运行所需的 Python 版本和依赖项。

最后去 Netlify 添加站点,build command 填 bash build.sh,publish directory 填 _deploy 就可以了。

以后写博客只需要将 VeriPress 实例的目录整个 push 到 GitHub(我忽略了 themes 目录),之后 Netlify 会自动生成静态文件然后部署,可以说非常棒了~

记一次关于 C++ 多线程写文件操作的错误修复

创建于
分类:Dev
标签:C++Multithreading

前段时间酷 Q HTTP API 插件有用户报错说,连续请求两次 API 发送同一张图(通过 URL),会发生第二张图发不出去的问题。想了一下,问题很显然,因为插件在处理第一个请求的时候,开始下载图片,以 URL 的 md5 作为文件名,然后第二个请求到的时候(多线程处理),图还没有下完,但是同名文件已经存在了,插件以为是缓存,于是试图直接发送下到一半的文件,自然是发不出去了。

最简单的修复方法是让插件先把图片下载到一个临时文件,下载完成后再改名。但是这样的话,两个请求都会造成文件下载,但实际上这是不必要的,因为它们实际发的是同一张图。那么自然要想到让第二个请求发现已经有请求在发送同一张图的时候,等待其完成,然后直接发送已缓存的图。

一开始想到用一个 std::map<std::string, std::mutex> 来按文件名保存互斥锁,每个线程首先算出文件名,然后尝试获取文件名对应的锁。这个方法看起来虽然好像可以,但实际上仔细一想发现了问题,std::map 本身就不是线程安全的数据结构,两个线程很可能在创建和删除锁的时候发生冲突,而如果再给 map 加个锁,逻辑就开始有些混乱了,有发生死锁的风险。

于是去 Stack Overflow 搜了一圈,发现了 std::condition_variable 这么个东西,通过 waitnotify_onenotify_all 方法来实现让线程等待和唤醒等待中的线程,基本上满足了我的需求。

最终修复代码如下:

static unordered_set<string> files_in_process;
static mutex files_in_process_mutex;
static condition_variable cv;

if (!filename.empty() && make_file != nullptr) {
    unique_lock<mutex> lk(files_in_process_mutex);
    // wait until there is no other thread processing the same file
    cv.wait(lk, [=] {
        return files_in_process.find(filename) == files_in_process.cend();
    });
    files_in_process.insert(filename);
    lk.unlock();

    // we are now sure that only our current thread is processing the file
    if (make_file()) {
        // succeeded
        segment.data["file"] = filename;
    }

    // ok, we can let other threads play
    lk.lock();
    files_in_process.erase(filename);
    lk.unlock();
    cv.notify_all();
}

使用一个 std::unordered_set<std::string> 保存了当前正在处理的请求对应的文件名,同时对应有一个互斥锁。

第一部分首先获取互斥锁,然后调用 std::condition_variablewait 方法,这个方法会首先判断传入的谓词(第二个参数)是否满足(在拥有锁的情况下调用),如果不满足,释放锁,然后等待(阻塞当前线程)。在第一个 API 调用时,显然并没有其它线程正在处理同名文件,所以它会往下执行,把当前文件名放到集合,然后释放锁(这是很重要的,在实际下载文件开始之前需要释放锁,否则下载别的文件的线程也得等这个线程执行完才能执行)。

第二部分就是下载文件操作了,这里之所以不用拥有锁,是因为我们在第一部分已经确定没有其它线程正在操作这个文件,而一旦文件名插入到集合中(拥有锁的时候插入的),别的下载同名文件的线程就会在 wait 方法里阻塞了。因此第二部分我们可以确定每个文件只有一个线程在下载。

第三部分就是下载完成后从文件名集合中删除当前文件名了,在拥有锁的情况下删除,然后立即释放锁,接着调用 notify_all,还记得其它需要发送同名文件的线程都在 wait 中等待吗,调用 notify_all 之后它们会被唤醒,然后依次获取锁、判断谓词是否满足,当第二个发送同一图片的线程执行到第二部分的时候,它会发现图片已经缓存了,于是直接发送缓存了的图片。

至此完美解决了一开始的问题。

使用 PyInstaller 将 Python 程序打包成无依赖的可执行文件

创建于
分类:Dev
标签:PythonPyInstaller

本文以 Windows 为例,其它系统上应该坑会更少一点。

安装

首先一点,截至目前(17 年 6 月),PyInstaller 还不兼容 Python 3.6,根据官方的说明,目前支持的版本是 2.7 和 3.3 到 3.5。当你看到这篇文章时,可能已经支持更新版本了,建议查看官方 repo 的 README:pyinstaller/pyinstaller

用 pip 安装:

pip install pyinstaller

在 Windows 上,pip 会同时安装 pypiwin32 包,这是 PyInstaller 在 Windows 上的一个依赖。

这里就有了第一个坑,根据 Requirements

It requires either the PyWin32 or pypiwin32 Python extension for Windows.

理论上应该默认情况下安装了 pypiwin32 就可以运行的,但其实并不行,直接用的话会报错 ImportError: DLL load failed: The specified module could not be found.

根据 #1840 这个 issue,再安装个 PyWin32 就可以了。

前往 Python for Windows Extensions,点进 pywin32 目录里面的最新 build,找到对应当前 Python 版本的 exe,下载安装。

使用

比如我们现在有一个脚本文件 main.py,要将它打包成可执行文件,直接运行:

pyinstaller -F main.py

这将会在 main.py 所在目录下生成一些其它目录,最终的可执行文件就在 dist 目录中。这条命令中 -F 表示生成单个可执行文件,如果不加 -F,则默认生成一个目录,其中除了可执行文件,还包括其它依赖文件。

总体来说用起来是非常简单的,不过在打包使用了 requests 包的程序时,出现了 ImportError: No module named 'queue' 的报错,发现 这里 也有遇到了同样的问题。

这个问题只需要在打包时加入 --hidden-import 参数即可:

pyinstaller -F --hidden-import queue main.py

更多用法请参考 Using PyInstaller