Merge pull request #19 from huangxd-/dev
添加环境变量BILIBILI_COOKIE,用于获取完整b站弹幕
This commit is contained in:
4 files changed
+251
-59
No files matched your search
@@ -1,7 +1,7 @@
|
||||
# LogVar 弹幕 API 服务器
|
||||
|
||||
[](https://github.com/huangxd-/damnu_api)
|
||||

|
||||

|
||||

|
||||
[](https://t.me/logvar_danmu_channel)
|
||||
[](https://t.me/logvar_danmu_group)
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
本项目仅为个人爱好开发,代码开源。如有任何侵权行为,请联系本人删除。
|
||||
|
||||
有问题提issue或私信机器人都ok。https://t.me/ddjdd_bot
|
||||
有问题提issue或 [私信机器人](https://t.me/ddjdd_bot) 都ok。
|
||||
|
||||
新加了tg频道 (https://t.me/logvar_danmu_channel) ,方便发送更新通知,以及群组,太多人私信咨询了,索性增加一个互助群 (https://t.me/logvar_danmu_group) ,大家有问题可以在群里求助。请点击上面蓝色的按钮进行添加关注。
|
||||
新加了 [tg频道](https://t.me/logvar_danmu_channel) ,方便发送更新通知,以及群组,太多人私信咨询了,索性增加一个 [互助群](https://t.me/logvar_danmu_group) ,大家有问题可以在群里求助。
|
||||
|
||||
## 功能
|
||||
- **API 接口**:
|
||||
@@ -124,6 +124,7 @@
|
||||
|
||||
### 优化点
|
||||
Settings > Functions > Advanced Setting > Function Region 切换为 Hong Kong,能提高访问速度,体验更优
|
||||
> hk有可能访问不了360,也可以尝试切其他region,如新加坡等
|
||||
|
||||
## 部署到 腾讯云 edgeone pages 【推荐】
|
||||
|
||||
@@ -134,7 +135,9 @@ Settings > Functions > Advanced Setting > Function Region 切换为 Hong Kong,
|
||||
>
|
||||
> 示例请求:`https://{your_domian}/{TOKEN}/api/v2/search/anime?keyword=子夜归`确认是否部署成功
|
||||
>
|
||||
> 部署的时候项目加速区域最好设置为"全球可用区(不含中国大陆)",不然不绑定自定义域名貌似只能生成3小时的预览链接?https://edgeone.cloud.tencent.com/pages/document/175191784523485184
|
||||
> 部署的时候项目加速区域最好设置为"全球可用区(不含中国大陆)",不然不绑定自定义域名貌似只能生成3小时的预览链接?[相关文档](https://edgeone.cloud.tencent.com/pages/document/175191784523485184)
|
||||
>
|
||||
> 也可直接用国际站的部署按钮一键部署,默认选择"全球可用区(不含中国大陆)" [](https://edgeone.ai/pages/new?template=https://github.com/huangxd-/danmu_api&project-name=danmu-api&root-directory=.%2F&env=TOKEN)
|
||||
>
|
||||
<img src="https://i.mji.rip/2025/09/17/3a675876dabb92e4ce45c10d543ce66b.png" style="width:400px" />
|
||||
|
||||
@@ -157,7 +160,8 @@ Settings > Functions > Advanced Setting > Function Region 切换为 Hong Kong,
|
||||
创建一个worker,将`danmu_api/worker.js`里的代码直接拷贝到你创建的`worker.js`里,然后点击部署。
|
||||
|
||||
## API食用指南
|
||||
支持 forward/senplayer 等支持弹幕API的播放器。
|
||||
支持 forward/senplayer/hills/小幻/yamby/eplayerx/afusekt 等支持弹幕API的播放器。
|
||||
|
||||
以`senplayer`为例:
|
||||
1. 获取到部署之后的API地址,如`http://192.168.1.7:9321/87654321`,其中`87654321`是默认token,前提是没有传TOKEN环境变量
|
||||
2. 将API地址填入自定义弹幕API,在`设置 - 弹幕设置 - 自定义弹幕API`
|
||||
@@ -169,12 +173,15 @@ Settings > Functions > Advanced Setting > Function Region 切换为 Hong Kong,
|
||||
<img src="https://i.mji.rip/2025/09/14/9fdf945fb247994518042691f60d7849.jpeg" style="width:400px" />
|
||||
<img src="https://i.mji.rip/2025/09/14/dbacc0cf9c8a839f16b8960de1f38f11.jpeg" style="width:400px" />
|
||||
|
||||
> 注意:小幻在填写API的时候需要在API后面加上`/api/v2`,如`http://192.168.1.7:9321/87654321/api/v2`
|
||||
|
||||
## 环境变量列表
|
||||
| 变量名称 | 描述 |
|
||||
| ----------- | ----------- |
|
||||
| TOKEN | 用户token |
|
||||
| OTHER_SERVER | 兜底第三方弹幕服务器,如 https://api.danmu.icu |
|
||||
| VOD_SERVER | vod查询站点,如 https://www.caiji.cyou |
|
||||
| TOKEN | 【可选】自定义用户token,不填默认为`87654321` |
|
||||
| OTHER_SERVER | 【可选】兜底第三方弹幕服务器,如 https://api.danmu.icu |
|
||||
| VOD_SERVER | 【可选】vod查询站点,如 https://www.caiji.cyou |
|
||||
| BILIBILI_COOKIE | 【可选】b站cookie(填入后能抓取完整弹幕),如 `buvid3=E2BCA ... eao6; theme-avatar-tip-show=SHOWED`,请自行通过浏览器或抓包工具抓取 |
|
||||
|
||||
## 项目结构
|
||||
```
|
||||
@@ -186,6 +193,9 @@ danmu_api/
|
||||
│ ├── server.js # 本地node启动脚本
|
||||
│ ├── worker.js # 主 API 服务器代码
|
||||
│ ├── worker.test.js # 测试文件
|
||||
├── node-functions/
|
||||
│ ├── [[...path]]..js # edgeone pages 所有路由跳转指向index
|
||||
│ ├── index.js # edgeone pages 中间处理逻辑
|
||||
├── .gitignore
|
||||
├── Dockerfile
|
||||
├── package.json
|
||||
@@ -202,7 +212,6 @@ danmu_api/
|
||||
- cloudflare貌似被哔风控了。
|
||||
- 如果想更换兜底第三方弹幕服务器,请添加环境变量`OTHER_SERVER`,示例`https://api.danmu.icu`。
|
||||
- 如果想更换vod站点,请添加环境变量`VOD_SERVER`,示例`https://www.caiji.cyou`。
|
||||
- 会返回所有跟title匹配的影片,所以主要使用场景是手动弹幕搜索场景。
|
||||
- 推荐vercel和claw部署,cloudflare好像不稳定,当然最稳定还是自己本地docker部署最佳。
|
||||
|
||||
### 📈项目 Star 数增长趋势
|
||||
|
||||
+169
-18
@@ -1,6 +1,6 @@
|
||||
// 全局状态(Cloudflare 和 Vercel 都可能重用实例)
|
||||
// ⚠️ 不是持久化存储,每次冷启动会丢失
|
||||
const VERSION = "1.0.3";
|
||||
const VERSION = "1.0.4";
|
||||
let animes = [];
|
||||
let episodeIds = [];
|
||||
let episodeNum = 10001; // 全局变量,用于自增 ID
|
||||
@@ -39,6 +39,16 @@ function resolveVodServer(env) {
|
||||
return DEFAULT_VOD_SERVER;
|
||||
}
|
||||
|
||||
const DEFAULT_BILIBILI_COOKIE = ""; // 默认 bilibili cookie
|
||||
let bilibliCookie = DEFAULT_BILIBILI_COOKIE;
|
||||
|
||||
// 这里既支持 Cloudflare env,也支持 Node process.env
|
||||
function resolveBilibiliCookie(env) {
|
||||
if (env && env.BILIBILI_COOKIE) return env.BILIBILI_COOKIE; // Cloudflare Workers
|
||||
if (typeof process !== "undefined" && process.env?.BILIBILI_COOKIE) return process.env.BILIBILI_COOKIE; // Vercel / Node
|
||||
return DEFAULT_BILIBILI_COOKIE;
|
||||
}
|
||||
|
||||
// 添加元素到 episodeIds:检查 url 是否存在,若不存在则以自增 id 添加
|
||||
function addEpisode(url, title) {
|
||||
// 检查是否已存在相同的 url
|
||||
@@ -320,7 +330,7 @@ async function get360Animes(title) {
|
||||
return animes;
|
||||
} catch (error) {
|
||||
log("error", `get360Animes error: ${error.message}`);
|
||||
throw error;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +365,7 @@ async function get360Zongyi(entId, site, year) {
|
||||
return links;
|
||||
} catch (error) {
|
||||
log("error", `get360Animes error: ${error.message}`);
|
||||
throw error;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +453,17 @@ function convertToDanmakuJson(contents, platform) {
|
||||
for (const item of items) {
|
||||
let attributes, m;
|
||||
|
||||
if ("timepoint" in item) {
|
||||
// 新增:处理新格式的弹幕数据
|
||||
if ("progress" in item && "mode" in item && "content" in item) {
|
||||
// 处理新格式的弹幕对象
|
||||
attributes = [
|
||||
(item.progress / 1000).toFixed(2), // progress 转换为秒
|
||||
item.mode || 1,
|
||||
item.color || 16777215,
|
||||
`[${platform}]`
|
||||
].join(",");
|
||||
m = item.content;
|
||||
} else if ("timepoint" in item) {
|
||||
// 处理对象数组输入
|
||||
attributes = [
|
||||
parseFloat(item.timepoint).toFixed(2),
|
||||
@@ -453,6 +473,9 @@ function convertToDanmakuJson(contents, platform) {
|
||||
].join(",");
|
||||
m = item.content;
|
||||
} else {
|
||||
if (!("p" in item)) {
|
||||
continue;
|
||||
}
|
||||
// 处理 XML 解析后的格式
|
||||
const pValues = item.p.split(",");
|
||||
attributes = [
|
||||
@@ -682,6 +705,89 @@ function md5(message) {
|
||||
return (wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d)).toLowerCase();
|
||||
}
|
||||
|
||||
function parseDanmakuBase64(base64) {
|
||||
const bytes = base64ToBytes(base64);
|
||||
const elems = [];
|
||||
|
||||
let offset = 0;
|
||||
while (offset < bytes.length) {
|
||||
// 每个 DanmakuElem 在 elems 列表里是 length-delimited
|
||||
const key = bytes[offset++];
|
||||
if (key !== 0x0a) break; // field=1 (elems), wire=2
|
||||
const [msgBytes, nextOffset] = readLengthDelimited(bytes, offset);
|
||||
offset = nextOffset;
|
||||
|
||||
let innerOffset = 0;
|
||||
const elem = {};
|
||||
|
||||
while (innerOffset < msgBytes.length) {
|
||||
const tag = msgBytes[innerOffset++];
|
||||
const fieldNumber = tag >> 3;
|
||||
const wireType = tag & 0x07;
|
||||
|
||||
if (wireType === 0) {
|
||||
// varint
|
||||
const [val, innerNext] = readVarint(msgBytes, innerOffset);
|
||||
innerOffset = innerNext;
|
||||
switch (fieldNumber) {
|
||||
case 1: elem.id = val; break;
|
||||
case 2: elem.progress = val; break;
|
||||
case 3: elem.mode = val; break;
|
||||
case 4: elem.fontsize = val; break;
|
||||
case 5: elem.color = val; break;
|
||||
case 8: elem.ctime = val; break;
|
||||
case 9: elem.weight = val; break;
|
||||
case 11: elem.pool = val; break;
|
||||
case 13: elem.attr = val; break;
|
||||
case 15: elem.like_num = val; break;
|
||||
case 17: elem.dm_type_v2 = val; break;
|
||||
}
|
||||
} else if (wireType === 2) {
|
||||
// length-delimited
|
||||
const [valBytes, innerNext] = readLengthDelimited(msgBytes, innerOffset);
|
||||
innerOffset = innerNext;
|
||||
switch (fieldNumber) {
|
||||
case 6: elem.midHash = utf8BytesToString(valBytes); break;
|
||||
case 7: elem.content = utf8BytesToString(valBytes); break;
|
||||
case 10: elem.action = utf8BytesToString(valBytes); break;
|
||||
case 12: elem.idStr = utf8BytesToString(valBytes); break;
|
||||
case 14: elem.animation = utf8BytesToString(valBytes); break;
|
||||
case 16: elem.color_v2 = utf8BytesToString(valBytes); break;
|
||||
}
|
||||
} else {
|
||||
// 其他类型不常用,忽略
|
||||
const [_, innerNext] = readVarint(msgBytes, innerOffset);
|
||||
innerOffset = innerNext;
|
||||
}
|
||||
}
|
||||
|
||||
elems.push(elem);
|
||||
}
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
function readVarint(bytes, offset) {
|
||||
let result = 0n;
|
||||
let shift = 0n;
|
||||
let pos = offset;
|
||||
while (true) {
|
||||
const b = bytes[pos++];
|
||||
result |= BigInt(b & 0x7f) << shift;
|
||||
if ((b & 0x80) === 0) break;
|
||||
shift += 7n;
|
||||
}
|
||||
return [Number(result), pos];
|
||||
}
|
||||
|
||||
function readLengthDelimited(bytes, offset) {
|
||||
const [length, newOffset] = readVarint(bytes, offset);
|
||||
const start = newOffset;
|
||||
const end = start + length;
|
||||
const slice = bytes.slice(start, end);
|
||||
return [slice, end];
|
||||
}
|
||||
|
||||
// =====================
|
||||
// 获取腾讯弹幕
|
||||
// =====================
|
||||
@@ -1086,7 +1192,7 @@ async function fetchBilibili(inputUrl) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let title, danmakuUrl;
|
||||
let title, danmakuUrl, cid, aid, duration;
|
||||
|
||||
// 普通投稿视频
|
||||
if (inputUrl.includes("video/")) {
|
||||
@@ -1111,6 +1217,7 @@ async function fetchBilibili(inputUrl) {
|
||||
if (inputUrl.includes("BV")) {
|
||||
videoInfoUrl = `${api_video_info}?bvid=${path[2]}`;
|
||||
} else {
|
||||
aid = path[2].substring(2)
|
||||
videoInfoUrl = `${api_video_info}?aid=${path[2].substring(2)}`;
|
||||
}
|
||||
|
||||
@@ -1127,7 +1234,8 @@ async function fetchBilibili(inputUrl) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cid = data.data.pages[p - 1].cid;
|
||||
duration = data.data.duration;
|
||||
cid = data.data.pages[p - 1].cid;
|
||||
danmakuUrl = `https://comment.bilibili.com/${cid}.xml`;
|
||||
} catch (error) {
|
||||
log("error", "请求普通投稿视频信息失败:", error);
|
||||
@@ -1156,7 +1264,8 @@ async function fetchBilibili(inputUrl) {
|
||||
for (const episode of data.result.episodes) {
|
||||
if (episode.id == epid) {
|
||||
title = episode.share_copy;
|
||||
const cid = episode.cid;
|
||||
cid = episode.cid;
|
||||
duration = episode.duration / 1000;
|
||||
danmakuUrl = `https://comment.bilibili.com/${cid}.xml`;
|
||||
break;
|
||||
}
|
||||
@@ -1176,18 +1285,59 @@ async function fetchBilibili(inputUrl) {
|
||||
log("error", "不支持的B站视频网址,仅支持普通视频(av,bv)、剧集视频(ep)");
|
||||
return [];
|
||||
}
|
||||
log("log", danmakuUrl, cid, aid, duration);
|
||||
|
||||
const response = await httpGet(danmakuUrl, {
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
},
|
||||
});
|
||||
// 计算视频的分片数量
|
||||
const maxLen = Math.floor(duration / 360) + 1;
|
||||
log("log", "maxLen: ", maxLen);
|
||||
|
||||
const contents = response.data;
|
||||
printFirst200Chars(contents);
|
||||
const segmentList = [];
|
||||
for (let i = 0; i < maxLen; i += 1) {
|
||||
let danmakuUrl;
|
||||
if (aid) {
|
||||
danmakuUrl = `https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=${cid}&pid=${aid}&segment_index=${i + 1}`;
|
||||
} else {
|
||||
danmakuUrl = `https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=${cid}&segment_index=${i + 1}`;
|
||||
}
|
||||
|
||||
return convertToDanmakuJson(contents, "bilibili");
|
||||
segmentList.push({
|
||||
segment_start: i * 360 * 1000,
|
||||
segment_end: (i + 1) * 360 * 1000,
|
||||
url: danmakuUrl,
|
||||
});
|
||||
}
|
||||
|
||||
// 使用 Promise.all 并行请求所有分片
|
||||
try {
|
||||
const allComments = await Promise.all(
|
||||
segmentList.map(async (segment) => {
|
||||
log("log", "正在请求弹幕数据...", segment.url);
|
||||
try {
|
||||
// 请求单个分片的弹幕数据
|
||||
let res = await httpGet(segment.url, {
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
"Cookie": bilibliCookie
|
||||
},
|
||||
base64Data: true,
|
||||
});
|
||||
|
||||
return parseDanmakuBase64(res.data);
|
||||
} catch (error) {
|
||||
log("error", "请求弹幕数据失败: ", error);
|
||||
return [];
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 合并所有分片的弹幕数据
|
||||
const mergedComments = allComments.flat();
|
||||
return convertToDanmakuJson(mergedComments, "bilibili");
|
||||
|
||||
} catch (error) {
|
||||
log("error", "获取所有弹幕数据时出错: ", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// =====================
|
||||
@@ -2836,6 +2986,7 @@ async function handleRequest(req, env) {
|
||||
token = resolveToken(env); // 每次请求动态获取,确保热更新环境变量后也能生效
|
||||
otherServer = resolveOtherServer(env);
|
||||
vodServer = resolveVodServer(env);
|
||||
bilibliCookie = resolveBilibiliCookie(env);
|
||||
|
||||
const url = new URL(req.url);
|
||||
let path = url.pathname;
|
||||
@@ -2948,5 +3099,5 @@ export async function vercelHandler(req, res) {
|
||||
}
|
||||
|
||||
// 为了测试导出 handleRequest
|
||||
export { handleRequest, searchAnime, searchEpisodes, matchAnime, getBangumi, getComment, fetchTencentVideo, fetchIqiyi, fetchMangoTV,
|
||||
fetchBilibili, fetchYouku, fetchOtherServer, httpGet, httpPost };
|
||||
export { handleRequest, searchAnime, searchEpisodes, matchAnime, getBangumi, getComment, fetchTencentVideo, fetchIqiyi,
|
||||
fetchMangoTV, fetchBilibili, fetchYouku, fetchOtherServer, httpGet, httpPost };
|
||||
+56
-31
@@ -1,7 +1,7 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert').strict;
|
||||
const { handleRequest, searchAnime, matchAnime, getBangumi, getComment, fetchTencentVideo, fetchIqiyi, fetchMangoTV,
|
||||
fetchBilibili, fetchYouku, fetchOtherServer, httpGet, httpPost } = require('./worker');
|
||||
const { handleRequest, searchAnime, matchAnime, searchEpisode, getBangumi, getComment, fetchTencentVideo, fetchIqiyi,
|
||||
fetchMangoTV, fetchBilibili, fetchYouku, fetchOtherServer, httpGet, httpPost } = require('./worker');
|
||||
|
||||
// Mock Request class for testing
|
||||
class MockRequest {
|
||||
@@ -52,10 +52,10 @@ test('worker.js API endpoints', async (t) => {
|
||||
// assert(res.length > 2, `Expected res.length > 2, but got ${res.length}`);
|
||||
// });
|
||||
|
||||
// await t.test('GET bilibili danmu', async () => {
|
||||
// const res = await fetchBilibili("https://www.bilibili.com/bangumi/play/ep1231564");
|
||||
// assert(res.length > 2, `Expected res.length > 2, but got ${res.length}`);
|
||||
// });
|
||||
await t.test('GET bilibili danmu', async () => {
|
||||
const res = await fetchBilibili("https://www.bilibili.com/bangumi/play/ep1231564");
|
||||
assert(res.length > 2, `Expected res.length > 2, but got ${res.length}`);
|
||||
});
|
||||
|
||||
// await t.test('GET youku danmu', async () => {
|
||||
// const res = await fetchYouku("https://v.youku.com/v_show/id_XNjQ3ODMyNjU3Mg==.html");
|
||||
@@ -97,30 +97,55 @@ test('worker.js API endpoints', async (t) => {
|
||||
// assert(commentData.count > 0, `Expected commentData.count > 0, but got ${commentData.count}`);
|
||||
// });
|
||||
|
||||
// 测试 POST /api/v2/match 接口
|
||||
await t.test('POST /api/v2/match for matching anime', async () => {
|
||||
// 构造请求体
|
||||
const requestBody = {
|
||||
"fileName": "生万物 S01E28",
|
||||
"fileHash": "1234567890",
|
||||
"fileSize": 0,
|
||||
"videoDuration": 0,
|
||||
"matchMode": "fileNameOnly"
|
||||
};
|
||||
// // 测试 POST /api/v2/match 接口
|
||||
// await t.test('POST /api/v2/match for matching anime', async () => {
|
||||
// // 构造请求体
|
||||
// const requestBody = {
|
||||
// "fileName": "生万物 S01E28",
|
||||
// "fileHash": "1234567890",
|
||||
// "fileSize": 0,
|
||||
// "videoDuration": 0,
|
||||
// "matchMode": "fileNameOnly"
|
||||
// };
|
||||
//
|
||||
// // 模拟 POST 请求
|
||||
// const matchUrl = `${urlPrefix}/${token}/api/v2/match`; // 注意路径与 handleRequest 中匹配
|
||||
// const req = new MockRequest(matchUrl, { method: 'POST', body: requestBody });
|
||||
//
|
||||
// // 调用 handleRequest 来处理 POST 请求
|
||||
// const res = await handleRequest(req);
|
||||
//
|
||||
// // 解析响应
|
||||
// const responseBody = await parseResponse(res);
|
||||
// console.log(responseBody);
|
||||
//
|
||||
// // 验证响应状态
|
||||
// assert.equal(res.status, 200);
|
||||
// assert.deepEqual(responseBody.success, true);
|
||||
// });
|
||||
|
||||
// 模拟 POST 请求
|
||||
const matchUrl = `${urlPrefix}/${token}/api/v2/match`; // 注意路径与 handleRequest 中匹配
|
||||
const req = new MockRequest(matchUrl, { method: 'POST', body: requestBody });
|
||||
|
||||
// 调用 handleRequest 来处理 POST 请求
|
||||
const res = await handleRequest(req);
|
||||
|
||||
// 解析响应
|
||||
const responseBody = await parseResponse(res);
|
||||
console.log(responseBody);
|
||||
|
||||
// 验证响应状态
|
||||
assert.equal(res.status, 200);
|
||||
assert.deepEqual(responseBody.success, true);
|
||||
});
|
||||
// // 测试 GET /api/v2/search/episodes 接口
|
||||
// await t.test('GET /api/v2/search/episodes for search episodes', async () => {
|
||||
// // 构造请求体
|
||||
// const requestBody = {
|
||||
// "fileName": "生万物 S01E28",
|
||||
// "fileHash": "1234567890",
|
||||
// "fileSize": 0,
|
||||
// "videoDuration": 0,
|
||||
// "matchMode": "fileNameOnly"
|
||||
// };
|
||||
//
|
||||
// const matchUrl = `${urlPrefix}/${token}/api/v2/search/episodes?anime=子夜归`;
|
||||
// const req = new MockRequest(matchUrl, { method: 'GET' });
|
||||
//
|
||||
// const res = await handleRequest(req);
|
||||
//
|
||||
// // 解析响应
|
||||
// const responseBody = await parseResponse(res);
|
||||
// console.log(responseBody);
|
||||
//
|
||||
// // 验证响应状态
|
||||
// assert.equal(res.status, 200);
|
||||
// assert.deepEqual(responseBody.success, true);
|
||||
// });
|
||||
});
|
||||
@@ -13,7 +13,14 @@ export const onRequest = async (context) => {
|
||||
// 构造完整的 URL
|
||||
let fullUrl;
|
||||
try {
|
||||
fullUrl = new URL(request.url, baseUrl).toString();
|
||||
let targetUrl = request.url;
|
||||
|
||||
// 判断是否包含 node-functions/index.js,如果是则用 / 代替
|
||||
if (request.url.includes('node-functions/index.js')) {
|
||||
targetUrl = '/';
|
||||
}
|
||||
|
||||
fullUrl = new URL(targetUrl, baseUrl).toString();
|
||||
console.log('Request fullUrl:', fullUrl);
|
||||
} catch (error) {
|
||||
console.error('URL Construction Error:', error);
|
||||
|
||||
Reference in new issue
Block a user