fix: 更新 Nysoure 域名并重构 VikaACG 搜索逻辑

* 更新 Nysoure 平台的 API 和基础 URL 至新域名 nysoure.com。
* 将 VikaACG 从基于正则匹配 HTML 的搜索改为调用官方 JSON API。
* 为 VikaACG 引入了完整的接口类型定义及更健壮的错误处理。
* 优化了 VikaACG 的搜索请求参数,并支持从 API 获取总结果数。
This commit is contained in:
Jurangren
2025-12-20 00:15:27 +08:00
parent 92327b2354
commit 65789b18be
2 changed files with 53 additions and 43 deletions

View File

@@ -1,13 +1,13 @@
import { fetchClient } from "../../utils/httpClient";
import type { Platform, PlatformSearchResult, SearchResultItem } from "../../types";
const API_URL = "https://res.nyne.dev/api/resource/search";
const BASE_URL = "https://res.nyne.dev/resources/";
const API_URL = "https://nysoure.com/api/resource/search";
const BASE_URL = "https://nysoure.com/resources/";
interface NysoureItem {
id: number;
title: string;
}
}
interface NysoureResponse {
success: boolean;

View File

@@ -1,8 +1,30 @@
import { fetchClient } from "../../utils/httpClient";
import type { Platform, PlatformSearchResult, SearchResultItem } from "../../types";
const API_URL = "https://www.vikacg.com/wp-json/b2/v1/getPostList";
const REGEX = /<h2><a target="_blank" href="(?<URL>.*?)">(?<NAME>.*?)<\/a>/gs;
const API_URL = "https://www.vikacg.com/api/vikacg/v1/getPosts";
/**
* 响应体最小类型(按你贴的 JSON
*/
type VikaGetPostsResponse = {
status?: string;
code?: number;
message?: string;
statusMessage?: string;
data?: {
list?: Array<{
id: number;
title: string;
// 其他字段这里不需要就不展开了
}>;
count?: number;
paged?: number;
page_count?: number;
pages?: number;
};
};
const POST_URL = (id: number) => `https://www.vikacg.com/p/${id}`;
async function searchVikaACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
@@ -17,26 +39,17 @@ async function searchVikaACG(game: string): Promise<PlatformSearchResult> {
"Content-Type": "application/json",
},
body: JSON.stringify({
paged: 1,
post_paged: 1,
post_count: 1000, // Hardcoded limit, larger values may cause timeouts
post_type: "post-1",
post_cat: [6],
post_order: "modified",
post_meta: [
"user",
"date",
"des",
"cats",
"like",
"comment",
"views",
"video",
"download",
"hide",
],
metas: {},
order: "updated_at",
sort: "desc",
status: null,
search: game,
page_count: 50,
paged: 1,
category: null,
tag: null,
rating: null,
is_pinned: false,
user_id: null,
}),
});
@@ -44,28 +57,25 @@ async function searchVikaACG(game: string): Promise<PlatformSearchResult> {
throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`);
}
// The response is a JSON-encoded string containing HTML.
// .json() will parse the JSON and unescape the string content.
const html: string = await response.text();
const decodedHtml = html.replaceAll('\\/', '/').replaceAll('\\\\', '\\').replaceAll('\\"', '"').replace(/\\u([\d\w]{4})/gi, (match, grp) => {
return String.fromCharCode(parseInt(grp, 16));
});
const matches = decodedHtml.matchAll(REGEX);
const json: VikaGetPostsResponse = await response.json();
const items: SearchResultItem[] = [];
for (const match of matches) {
if (match.groups?.NAME && match.groups?.URL) {
items.push({
name: match.groups.NAME.trim(),
url: match.groups.URL,
});
}
if (json.status !== "success" || !json.data) {
const msg = json.message || json.statusMessage || "接口返回非 success";
throw new Error(`资源平台 SearchAPI 返回异常:${msg}`);
}
searchResult.items = items;
searchResult.count = items.length;
const list = json.data.list ?? [];
const items: SearchResultItem[] = list
.filter((p) => typeof p?.id === "number" && typeof p?.title === "string")
.map((p) => ({
name: p.title.trim(),
url: POST_URL(p.id),
}));
searchResult.items = items;
// ✅ 建议 count 用后端总数data.count否则用本页长度兜底
searchResult.count = typeof json.data.count === "number" ? json.data.count : items.length;
} catch (error) {
if (error instanceof Error) {
searchResult.error = error.message;
@@ -86,4 +96,4 @@ const VikaACG: Platform = {
search: searchVikaACG,
};
export default VikaACG;
export default VikaACG;