小雅网盘(基于alist)
alist文档:https://alist.nn.ci/zh//
小雅文档: https://xiaoyaliu.notion.site/xiaoya-docker-69404af849504fa5bcf9f2dd5ecaa75f
终端安装命令,端口 5678
bash -c "$(curl http://docker.xiaoya.pro/update_new.sh)"
或者 端口 6789
bash -c "$(curl http://docker.xiaoya.pro/update_new.sh)" -s host
阿里token获取地址:https://aliyuntoken.vercel.app/
阿里opentoken地址:https://alist.nn.ci/tool/aliyundrive/request.html
转存文件夹ID:网页版阿里云,进转存文件夹,地址栏内最后64开头的数字
创建一个名为 xiaoyakeeper 的docker定时运行小雅转存清理并升级小雅镜像
定时清理缓存
可以不加-tg
模式3:创建一个名为 xiaoyakeeper 的docker定时运行小雅转存清理并升级小雅镜像
bash -c "$(curl -s https://xiaoyahelper.zengge99.eu.org/aliyun_clear.sh | tail -n +2)" -s 3 -tg
模式5:与模式3的区别是实时清理,只要产生了播放缓存一分钟内立即清理。签到和定时升级同模式3
bash -c "$(curl -s https://xiaoyahelper.zengge99.eu.org/aliyun_clear.sh | tail -n +2)" -s 5 -tg
套娃挂载:
输入命令,获取令牌
docker exec -i xiaoya sqlite3 data/data.db <<EOF
select value from x_setting_items where key = "token";
EOF
挂载 :alist v3
输入令牌
输入链接
注意docker
名称
同步小雅网盘
如果你是基于Linux系统的(包括openwrt),可以用以下方法设置定时更新,终端执行
crontab -e
添加一条记录
0 6 * * * docker restart xiaoya
- 按
o
插入一行 - 然后把这堆文字输入进去
- 然后按键盘左上角 ESC键退出编辑模式
- 输入
:wq
保存退出
就是每天凌晨6点自动重启xiaoya docker去同步数据,你把6改成13,那就是下午1点
小雅CF反代
登录cloudflare,创建Worker
代码如下,修改const upstream_url = "alist地址";
免费地址寻找:网络空间雷达 复制ip
import { connect } from 'cloudflare:sockets';
const upstream_url = "http://183.134.157.38:5678";
const proxyList = ["mypikpak", "sharepoint"];
const replace_dict = {
'$up_url': '$cust_url',
'$upstream': '$custom_domain'
}
let up_scheme = upstream_url.split("://")[0]
let upstream = upstream_url.split("://")[1].split(":")[0];
let port_string = upstream_url.split("://")[1].split(":")[1];
if (!port_string) {
port_string = up_scheme === "http" ? "80" : "443";
}
let port = parseInt(port_string);
const timeoutDuration = 5000;
const chunkSize = 1024 * 4;
export default {
async fetch(request, env, ctx) {
let url = new URL(request.url);
let cust_url = new URL(request.url);
url.port = port.toString();
let original_url_hostname = url.hostname;
url.hostname = upstream;
url.protocol = up_scheme + ":";
if (url.pathname.startsWith("/proxy/")) {
let proxyUrl = new URL("https://" + url.pathname.replace(/\/proxy\//g, "") + url.search);
let proxyReq = new Request(proxyUrl, request);
let proxyResponse = await fetchOverTcp(proxyReq, 443);
return proxyResponse;
}
let new_request = new Request(url, request);
let original_response = await fetchOverTcp(new_request);
let response_headers = original_response.headers;
let new_response_headers = new Headers(response_headers);
if (original_response.status === 302) {
const locationHeader = new_response_headers.get('location');
if (locationHeader && isReplace(locationHeader)) {
const modifiedLocation = locationHeader.replace(/https:\/\/+/g, "https://" + original_url_hostname + "/proxy/");
new_response_headers.set('location', modifiedLocation);
}
}
const content_type = new_response_headers.get('content-type');
if (url.pathname.includes("tvbox") || (content_type != null && (content_type.includes('text/html') || content_type.includes('json')) && (content_type.includes('UTF-8') || content_type.includes('utf-8')))) {
let dic_def = {};
dic_def["$upstream"] = upstream;
dic_def["$custom_domain"] = original_url_hostname;
dic_def["$cust_url"] = `${cust_url.protocol}//${cust_url.hostname}`;
dic_def["$up_url"] = `${upstream_url}`;
let original_text = replace_response_text(await original_response.text(), dic_def);
let status = original_response.status;
return new Response(original_text, {
status,
headers: new_response_headers
});
}
else {
return original_response;
}
},
};
async function fetchOverTcp(request, cust_port = null) {
let url = new URL(request.url);
let req = new Request(url, request);
let out_port = cust_port ? cust_port : port
if ((url.protocol === "https:" && out_port === 443) || (url.protocol === "http:" && out_port === 80)) {
return await fetch(req);
}
let tcpSocket = connect({
hostname: url.hostname,
port: out_port,
}, { secureTransport: "starttls" });
if (url.protocol === "https:") {
tcpSocket = tcpSocket.startTls();
}
try {
const writer = tcpSocket.writable.getWriter();
let headersString = '';
let bodyString = '';
for (let [name, value] of req.headers) {
if (name === "host" || name === "x-forwarded-proto" || name === "x-real-ip" || name === "accept-encoding") {
continue;
}
if (name === "connection") {
value = "close";
}
headersString += `${name}: ${value}\r\n`;
}
let fullpath = url.pathname;
if (url.search) {
fullpath += url.search.replace(/%3F/g, "?");
}
if (req.method === "POST") {
const body = await req.text();
bodyString = `${body}`;
}
await writer.write(new TextEncoder().encode(`${req.method} ${fullpath} HTTP/1.0\r\nHost: ${url.hostname}:${port}\r\n${headersString}\r\n${bodyString}`));
writer.releaseLock();
const response = await constructHttpResponse(tcpSocket, timeoutDuration);
return response;
} catch (error) {
tcpSocket.close();
return new Response('Internal Server Error', { status: 500 });
}
}
async function constructHttpResponse(tcpSocket, timeout) {
const reader = tcpSocket.readable.getReader();
let remainingData = new Uint8Array(0);
try {
while (true) {
const { value, done } = await raceWithTimeout(reader.read(chunkSize), timeout);
const newData = new Uint8Array(remainingData.length + value.length);
newData.set(remainingData);
newData.set(value, remainingData.length);
remainingData = newData;
const index = indexOfDoubleCRLF(remainingData);
if (index !== -1) {
const headerBytes = remainingData.subarray(0, index);
const bodyBytes = remainingData.subarray(index + 4);
const header = new TextDecoder().decode(headerBytes);
const [statusLine, ...headers] = header.split('\r\n');
const [httpVersion, statusCode, statusText] = statusLine.split(' ');
const responseHeaders = {};
headers.forEach((header) => {
const [name, value] = header.split(': ');
responseHeaders[name.toLowerCase()] = value;
});
responseHeaders['content-encoding'] = 'identity';
const responseInit = {
status: parseInt(statusCode),
statusText,
headers: new Headers(responseHeaders),
};
const bodyStream = new ReadableStream({
async start(controller) {
controller.enqueue(bodyBytes);
},
async pull(controller) {
while (true) {
try {
const { value, done } = await raceWithTimeout(reader.read(chunkSize), timeout);
if (value) {
controller.enqueue(value);
}
if (done) {
controller.close();
tcpSocket.close();
break;
}
} catch (e) {
controller.close();
tcpSocket.close();
return;
}
}
},
});
return new Response(bodyStream, responseInit);
}
if (done) {
tcpSocket.close();
break;
}
}
return new Response();
} catch (error) {
tcpSocket.close();
}
}
function raceWithTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout-1')), timeout))
]);
}
function indexOfDoubleCRLF(data) {
if (data.length < 4) {
return -1;
}
for (let i = 0; i < data.length - 3; i++) {
if (data[i] === 13 && data[i + 1] === 10 && data[i + 2] === 13 && data[i + 3] === 10) {
return i;
}
}
return -1;
}
function replace_response_text(text, dic_def) {
var i, j;
let new_replace_dict = {};
for (i in replace_dict) {
j = replace_dict[i]
i = dic_def[i] ? dic_def[i] : i;
j = dic_def[j] ? dic_def[j] : j;
new_replace_dict[i] = j;
}
for (i in new_replace_dict) {
j = new_replace_dict[i]
let re = new RegExp(i, 'g')
text = text.replace(re, j);
}
let host_name = dic_def["$custom_domain"];
if (isReplace(text) && host_name) {
text = text.replace(/https:\/\/+/g, "https://" + host_name + "/proxy/");
}
return text;
}
function isReplace(urlString) {
for (let i = 0; i < proxyList.length; i++) {
if (urlString.includes(proxyList[i])) {
return true;
}
}
return false;
}
其他内容扩充
执行时机和清理缓存的操作是完全相同的。
可以通过手动创建
/etc/xiaoya/mycheckintoken.txt
文件来定义多个网盘签到的32位refresh token,每行一个。若不添加文件,则使用默认小雅转存的网盘签到。自动刷新
/etc/xiaoya/mycheckintoken.txt
和/etc/xiaoya/mytoken.txt
文件,这有可能延长refresh token的时效,具体效果需要观察。定时运行模式包括:
- 默认每天从运行脚本的下一分钟开始执行。
- 运行时间可以通过手动创建
/etc/xiaoya/myruntime.txt
文件进行修改,例如06:00和18:00表示每天早晚6点各运行一次。
自动升级的说明:
- 定时升级的命令保存在
/etc/xiaoya/mycmd.txt
中。删除该文件将变成定时重启小雅。 - 完成清理和签到后,脚本会自动执行
/etc/xiaoya/mycmd.txt
中的命令。该文件默认包含升级小雅镜像的命令,不建议修改。
- 定时升级的命令保存在
关于TG推送:
- 所有模式加上
-tg
功能均可绑定消息推送的TG账号,只有第一次运行需要加上-tg
参数。
- 所有模式加上