图片来源:使用 Aseprite 制作
rclone 虽然是用 Go 写的但官方支持编译为 C 库,在代码中通过 rclone 的 远程控制 API 进行通信
编译
git clone https://github.com/rclone/rclone.git
// 编译为Linux静态库
go -C rclone build --buildmode=c-archive -o librclone.a github.com/rclone/rclone/librclone
// 或者也可以编译为Linux动态库
go -C rclone build --buildmode=c-shared -o librclone.so github.com/rclone/rclone/librclone
编译完成后在 rclone 目录下会生成 librclone.h 和 librclone.a(动态库则为 librclone.so)
librclone.h
librclone.h 代码十分简单,要关注的只有一个结构体和四个函数
初始化和资源释放
// 初始化 librclone, 在使用 RcloneRPC 前调用
extern void RcloneInitialize();
// 资源释放,程序退出前或不再使用 librclone 后调用
extern void RcloneFinalize();
RcloneRPC
要控制 librclone 的行为唯一要做的就是调用 RcloneRPC
发送指令
// method 是 https://rclone.org/rc/ 中定义的方法名
// input 则是一个 json 结构
extern struct RcloneRPCResult RcloneRPC(char* method, char* input);
// RcloneRPC 返回的结构体,其中 Output 需要用户调用 free 或者 RcloneFreeString 释放
// 其中 Status 的值为 200 表示指令执行成功,否则为失败
// Output 是一个 json 结构,内容为部分指令的返回结果,或失败信息
struct RcloneRPCResult {
char* Output;
int Status;
};
extern void RcloneFreeString(char* str);
样例
以 Linux 用户如何从 VRPirates 手动安装下载 Meta Quest 游戏 中的两条 rclone 命令为例,将他们翻译为等效的 C++ 代码
下载单个文件
rclone sync ":http:/meta.7z" . --http-url "https://theapp.vrrookie.xyz/" --tpslimit 1.0 --tpslimit-burst 3
#include <iostream>
#include "librclone.h"
int main(int, char **)
{
RcloneInitialize();
// https://rclone.org/rc/#operations-copyfile
char rc_method[] = "operations/copyfile";
char rc_input[] = R"({
"srcFs": ":http,url='https://theapp.vrrookie.xyz/':",
"srcRemote": "/meta.7z",
"dstFs": ".",
"dstRemote": "meta.7z",
"_config": {
"TPSLimit": 1.0,
"TPSLimitBurst": 3,
"UserAgent": "rclone/v1.65.2"
}
})";
RcloneRPCResult result = RcloneRPC(rc_method, rc_input);
if(result.Status != 200)
{
std::cerr << "Error: " << result.Output << std::endl;
}
RcloneFreeString(result.Output);
RcloneFinalize();
return 0;
}
下载文件夹
rclone copy ":http:/d251dd0aa665a8c29a6c1116ed1a3f5f" . --transfers 1 --multi-thread-streams 0 --progress --rc --http-url "https://theapp.vrrookie.xyz/" --tpslimit 1.0 --tpslimit-burst 3
#include <iostream>
#include "librclone.h"
int main(int, char **)
{
RcloneInitialize();
// https://rclone.org/rc/#sync-copy
char rc_method[] = "sync/copy";
char rc_input[] = R"({
"srcFs": ":http,url='https://theapp.vrrookie.xyz':/d251dd0aa665a8c29a6c1116ed1a3f5f",
"dstFs": ".",
"_config": {
"Transfers": 1,
"TPSLimit": 1.0,
"TPSLimitBurst": 3,
"MultiThreadStreams": 0,
"UserAgent": "rclone/v1.65.2"
}
})";
RcloneRPCResult result = RcloneRPC(rc_method, rc_input);
if (result.Status != 200)
{
std::cerr << "Error: " << result.Output << std::endl;
}
RcloneFreeString(result.Output);
RcloneFinalize();
return 0;
}
异步下载
上面展示的程序都是阻塞等待下载完成,要实际上只需要在 input
中添加 "_async": true
即可实现异步下载,并通过其他指令查询下载进度
为简化代码未做错误处理,也未实现 getJsonValueByKey
和 jsonCreate
两个函数
#include <iostream>
#include <unistd.h>
#include "librclone.h"
int main(int, char **)
{
RcloneInitialize();
// https://rclone.org/rc/#sync-copy
char rc_method[] = "sync/copy";
// 增加 _async 参数,使得 rclone 后台运行,RcloneRPC会立即返回
char rc_input[] = R"({
"srcFs": ":http,url='https://theapp.vrrookie.xyz':/d251dd0aa665a8c29a6c1116ed1a3f5f",
"dstFs": "local_dir",
"_async": true,
"_config": {
"Transfers": 1,
"TPSLimit": 1.0,
"TPSLimitBurst": 3,
"MultiThreadStreams": 0,
"UserAgent": "rclone/v1.65.2"
}
})";
RcloneRPCResult result = RcloneRPC(rc_method, rc_input);
// 通过解析 result 可获取 jobid
int jobid = getJsonValueByKey(result.Output, "jobid");
RcloneFreeString(result.Output);
while (true)
{
sleep(1);
// 假设 jobid 为 1,jsonCreate 构造结果 {"jobid":1}
RcloneRPCResult job_status = RcloneRPC("job/status", jsonCreate("jobid", jobid));
// 通过解析 job_status 可获取 group 和 finished
// finished用来判断是否下载完成
const char *group = getJsonValueByKey(job_status.Output, "group");
bool isfinished = getJsonValueByKey(job_status.Output, "finished");
if (isfinished)
{
break;
}
// 通过解析 core/stats 可获取下载状态,包括下载速度、下载进度等
// 假设 group 值为 "job/1", jsonCreate 构造结果 {"group":"job/1"}
RcloneRPCResult stats_result = RcloneRPC("core/stats", jsonCreate("group", group));
std::cout << "Stats: " << stats_result.Output << std::endl;
RcloneFreeString(job_status.Output);
RcloneFreeString(stats_result.Output);
}
RcloneFinalize();
return 0;
}
参考:
- Remote controlling rclone with its API
- https://github.com/rclone/rclone/blob/master/librclone/README.md
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。