feat: 引入平台标签系统,优化平台信息及错误日志

- 引入平台标签系统,提供详细的平台特性说明。
- 重构搜索结果初始化,避免平台名称重复定义。
- 修正核心搜索错误日志,确保正确记录平台名称。
- 移除两个Galgame平台:TianYouErCiYuan(收费)和YingZhiGuang(网站转型)。
- 更新部分平台的颜色、魔法属性和标签信息。
This commit is contained in:
Jurangren
2025-10-06 22:09:13 +08:00
parent 325f11fd24
commit 4d93d1b3e1
37 changed files with 44 additions and 161 deletions

View File

@@ -43,13 +43,14 @@ export async function handleSearchRequestStream(
if (result.error) {
// 记录平台错误
console.log(JSON.stringify({
message: `平台 ${result.name} 搜索错误: ${result.error}`,
message: `平台 ${platform.name} 搜索错误: ${result.error}`,
level: "error",
}));
}
const streamResult: StreamResult = {
name: result.name,
name: platform.name,
color: result.error ? 'red' : platform.color,
tags: platform.tags,
items: result.items,
error: result.error,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /<a target="_blank" href="(?<URL>.*?)" title="(?<NAME>.*?)" clas
async function searchACGYingYingGuai(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "ACG嘤嘤怪",
count: 0,
items: [],
};
@@ -51,6 +50,7 @@ async function searchACGYingYingGuai(game: string): Promise<PlatformSearchResult
const ACGYingYingGuai: Platform = {
name: "ACG嘤嘤怪",
color: "white",
tags: ["LoginRep", "SplDrive"],
magic: false,
search: searchACGYingYingGuai,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /<div class="post-info">\s*?<h2><a href="(?<URL>.*?)">(?<NAME>.*?
async function searchBiAnXingLu(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "彼岸星露",
count: 0,
items: [],
};
@@ -51,6 +50,7 @@ async function searchBiAnXingLu(game: string): Promise<PlatformSearchResult> {
const BiAnXingLu: Platform = {
name: "彼岸星露",
color: "lime",
tags: [],
magic: false,
search: searchBiAnXingLu,
};

View File

@@ -18,7 +18,6 @@ interface DaoHeGalResponse {
async function searchDaoHeGal(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "稻荷GAL",
count: 0,
items: [],
};
@@ -63,6 +62,7 @@ async function searchDaoHeGal(game: string): Promise<PlatformSearchResult> {
const DaoHeGal: Platform = {
name: "稻荷GAL",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchDaoHeGal,
};

View File

@@ -15,7 +15,6 @@ interface FuFuACGResponse {
async function searchFuFuACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "FuFuACG",
count: 0,
items: [],
};
@@ -59,6 +58,7 @@ async function searchFuFuACG(game: string): Promise<PlatformSearchResult> {
const FuFuACG: Platform = {
name: "FuFuACG",
color: "white",
tags: ["LoginPay"],
magic: false,
search: searchFuFuACG,
};

View File

@@ -7,7 +7,6 @@ const REGEX = /<a index=\d+ id="bid(?<URL>\d*?)" name="title" c=".*?" target="_b
async function searchGGBases(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "GGBases",
count: 0,
items: [],
};
@@ -56,6 +55,7 @@ async function searchGGBases(game: string): Promise<PlatformSearchResult> {
const GGBases: Platform = {
name: "GGBases",
color: "lime",
tags: ["NoReq", "BTmag"],
magic: false,
search: searchGGBases,
};

View File

@@ -11,7 +11,6 @@ interface GgsItem {
async function searchGGS(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "GGS",
count: 0,
items: [],
};
@@ -49,6 +48,7 @@ async function searchGGS(game: string): Promise<PlatformSearchResult> {
const GGS: Platform = {
name: "GGS",
color: "lime",
tags: ["NoReq"],
magic: false,
search: searchGGS,
};

View File

@@ -21,7 +21,6 @@ function stripHtml(html: string): string {
async function searchGalTuShuGuan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "GAL图书馆",
count: 0,
items: [],
};
@@ -81,6 +80,7 @@ async function searchGalTuShuGuan(game: string): Promise<PlatformSearchResult> {
const GalTuShuGuan: Platform = {
name: "GAL图书馆",
color: "lime",
tags: ["NoReq", "SplDrive"],
magic: false,
search: searchGalTuShuGuan,
};

View File

@@ -15,7 +15,6 @@ interface GalgameXResponse {
async function searchGalgameX(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "Galgamex",
count: 0,
items: [],
};
@@ -76,6 +75,7 @@ async function searchGalgameX(game: string): Promise<PlatformSearchResult> {
const GalgameX: Platform = {
name: "Galgamex",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchGalgameX,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /" class="lazyload fit-cover radius8">.*?<h2 class="item-heading">
async function searchHikarinagi(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "Hikarinagi",
count: 0,
items: [],
};
@@ -51,6 +50,7 @@ async function searchHikarinagi(game: string): Promise<PlatformSearchResult> {
const Hikarinagi: Platform = {
name: "Hikarinagi",
color: "white",
tags: ["LoginPay", "SuDrive"],
magic: false,
search: searchHikarinagi,
};

View File

@@ -17,7 +17,6 @@ interface JiMengACGResponse {
async function searchJiMengACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "绮梦ACG",
count: 0,
items: [],
};
@@ -63,6 +62,7 @@ async function searchJiMengACG(game: string): Promise<PlatformSearchResult> {
const JiMengACG: Platform = {
name: "绮梦ACG",
color: "lime",
tags: ["Rep", "SuDrive"],
magic: false,
search: searchJiMengACG,
};

View File

@@ -7,7 +7,6 @@ const REGEX = /<a class="game_item"\s+href="(?<URL>.+?)"\s*>.*?<span style="back
async function searchKoyso(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "Koyso",
count: 0,
items: [],
};
@@ -53,6 +52,7 @@ async function searchKoyso(game: string): Promise<PlatformSearchResult> {
const Koyso: Platform = {
name: "Koyso",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchKoyso,
};

View File

@@ -14,7 +14,6 @@ interface KunGalgameItem {
async function searchKunGalgame(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "鲲Galgame",
count: 0,
items: [],
};
@@ -60,6 +59,7 @@ async function searchKunGalgame(game: string): Promise<PlatformSearchResult> {
const KunGalgame: Platform = {
name: "鲲Galgame",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchKunGalgame,
};

View File

@@ -7,7 +7,6 @@ const REGEX = /<entry>.*?<title>(.*?)<\/title>.*?<url>(.*?)<\/url>.*?<\/entry>/g
async function searchLiSiTanACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "莉斯坦ACG",
count: 0,
items: [],
};
@@ -52,6 +51,7 @@ async function searchLiSiTanACG(game: string): Promise<PlatformSearchResult> {
const LiSiTanACG: Platform = {
name: "莉斯坦ACG",
color: "lime",
tags: ["NoReq", "NoSplDrive"],
magic: false,
search: searchLiSiTanACG,
};

View File

@@ -6,7 +6,6 @@ const REGEX = />\s*<h2 class="item-heading"><a target="_blank" href="(?<URL>.*?)
async function searchLiangZiACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "量子acg",
count: 0,
items: [],
};
@@ -51,6 +50,7 @@ async function searchLiangZiACG(game: string): Promise<PlatformSearchResult> {
const LiangZiACG: Platform = {
name: "量子acg",
color: "white",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchLiangZiACG,
};

View File

@@ -19,7 +19,6 @@ interface MaoMaoResponse {
async function searchMaoMaoWangPan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "猫猫网盘",
count: 0,
items: [],
};
@@ -77,6 +76,7 @@ async function searchMaoMaoWangPan(game: string): Promise<PlatformSearchResult>
const MaoMaoWangPan: Platform = {
name: "猫猫网盘",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchMaoMaoWangPan,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /<div class="item-thumbnail">\s*<a target="_blank" href="(?<URL>.*
async function searchMiaoYuanLingYu(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "喵源领域",
count: 0,
items: [],
};
@@ -56,6 +55,7 @@ async function searchMiaoYuanLingYu(game: string): Promise<PlatformSearchResult>
const MiaoYuanLingYu: Platform = {
name: "喵源领域",
color: "white",
tags: ["Login", "SuDrive"],
magic: false,
search: searchMiaoYuanLingYu,
};

View File

@@ -16,7 +16,6 @@ interface NysoureResponse {
async function searchNysoure(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "Nysoure",
count: 0,
items: [],
};
@@ -59,7 +58,8 @@ async function searchNysoure(game: string): Promise<PlatformSearchResult> {
const Nysoure: Platform = {
name: "Nysoure",
color: "gold",
color: "lime",
tags: ["NoReq", "magic", "SuDrive"],
magic: true,
search: searchNysoure,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /" class="lazyload fit-cover radius8">.*?<h2 class="item-heading">
async function searchQingJiACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "青桔ACG",
count: 0,
items: [],
};
@@ -57,6 +56,7 @@ async function searchQingJiACG(game: string): Promise<PlatformSearchResult> {
const QingJiACG: Platform = {
name: "青桔ACG",
color: "lime",
tags: ["NoReq", "SplDrive"],
magic: false,
search: searchQingJiACG,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /<h2 class="post-list-title">\s*<a href="(?<URL>.*?)" title=".+?"
async function searchShenShiTianTang(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "绅仕天堂",
count: 0,
items: [],
};
@@ -50,7 +49,8 @@ async function searchShenShiTianTang(game: string): Promise<PlatformSearchResult
const ShenShiTianTang: Platform = {
name: "绅仕天堂",
color: "gold",
color: "white",
tags: ["Login", "magic", "SuDrive"],
magic: true,
search: searchShenShiTianTang,
};

View File

@@ -10,7 +10,6 @@ interface TaoHuaYuanItem {
async function searchTaoHuaYuan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "桃花源",
count: 0,
items: [],
};
@@ -48,6 +47,7 @@ async function searchTaoHuaYuan(game: string): Promise<PlatformSearchResult> {
const TaoHuaYuan: Platform = {
name: "桃花源",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchTaoHuaYuan,
};

View File

@@ -1,57 +0,0 @@
import { fetchClient } from "../../utils/httpClient";
import type { Platform, PlatformSearchResult, SearchResultItem } from "../../types";
const API_URL = "https://www.tiangal.com/search/";
const REGEX = /<\/i><\/a><h2><a href="(?<URL>.*?)" title="(?<NAME>.*?)"/gs;
async function searchTianYouErCiYuan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "天游二次元",
count: 0,
items: [],
};
try {
const url = new URL(API_URL + encodeURIComponent(game)); // URL path parameter
const response = await fetchClient(url);
if (!response.ok) {
throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`);
}
const html = await response.text();
const matches = html.matchAll(REGEX);
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,
});
}
}
searchResult.items = items;
searchResult.count = items.length;
} catch (error) {
if (error instanceof Error) {
searchResult.error = error.message;
} else {
searchResult.error = "An unknown error occurred";
}
searchResult.count = -1;
}
return searchResult;
}
const TianYouErCiYuan: Platform = {
name: "天游二次元",
color: "gold",
magic: true,
search: searchTianYouErCiYuan,
};
export default TianYouErCiYuan;

View File

@@ -6,7 +6,6 @@ const BASE_URL = "https://www.touchgal.us/";
async function searchTouchGal(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "TouchGal",
count: 0,
items: [],
};
@@ -67,6 +66,7 @@ async function searchTouchGal(game: string): Promise<PlatformSearchResult> {
const TouchGal: Platform = {
name: "TouchGal",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchTouchGal,
};

View File

@@ -6,7 +6,6 @@ const REGEX = /<h2><a target="_blank" href="(?<URL>.*?)">(?<NAME>.*?)<\/a>/gs;
async function searchVikaACG(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "VikaACG",
count: 0,
items: [],
};
@@ -81,7 +80,8 @@ async function searchVikaACG(game: string): Promise<PlatformSearchResult> {
const VikaACG: Platform = {
name: "VikaACG",
color: "gold",
color: "white",
tags: ["LoginPay", "magic", "MixDrive"],
magic: true,
search: searchVikaACG,
};

View File

@@ -19,7 +19,6 @@ interface WeiZhiYunPanResponse {
async function searchWeiZhiYunPan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "未知云盘",
count: 0,
items: [],
};
@@ -75,6 +74,7 @@ async function searchWeiZhiYunPan(game: string): Promise<PlatformSearchResult> {
const WeiZhiYunPan: Platform = {
name: "未知云盘",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchWeiZhiYunPan,
};

View File

@@ -1,59 +0,0 @@
import { fetchClient } from "../../utils/httpClient";
import type { Platform, PlatformSearchResult, SearchResultItem } from "../../types";
const DATA_URL = "https://yinghu.netlify.app/search.xml";
const BASE_URL = "https://yinghu.netlify.app";
const REGEX = /<entry>.*?<title>(.*?)<\/title>.*?<url>(.*?)<\/url>.*?<\/entry>/gs;
async function searchYingZhiGuang(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "萤ノ光",
count: 0,
items: [],
};
try {
const response = await fetchClient(DATA_URL);
if (!response.ok) {
throw new Error(`Failed to fetch data from ${DATA_URL}`);
}
const xmlText = await response.text();
const matches = xmlText.matchAll(REGEX);
const items: SearchResultItem[] = [];
for (const match of matches) {
const title = match[1];
const urlPath = match[2];
if (title && urlPath && title.includes(game)) {
items.push({
name: title.trim(),
url: BASE_URL + urlPath,
});
}
}
searchResult.items = items;
searchResult.count = items.length;
} catch (error) {
if (error instanceof Error) {
searchResult.error = error.message;
} else {
searchResult.error = "An unknown error occurred";
}
searchResult.count = -1;
}
return searchResult;
}
const YingZhiGuang: Platform = {
name: "萤ノ光",
color: "lime",
magic: false,
search: searchYingZhiGuang,
};
export default YingZhiGuang;

View File

@@ -6,7 +6,6 @@ const REGEX = /<p style="text-align: center;"> <a href=".*?" target="_blank">.*?
async function searchYouYuDeloli(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "忧郁的loli",
count: 0,
items: [],
};
@@ -54,6 +53,7 @@ async function searchYouYuDeloli(game: string): Promise<PlatformSearchResult> {
const YouYuDeloli: Platform = {
name: "忧郁的loli",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchYouYuDeloli,
};

View File

@@ -7,7 +7,6 @@ const REGEX = /<entry>.*?<title>(.*?)<\/title>.*?<url>(.*?)<\/url>.*?<\/entry>/g
async function searchYueYao(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "月谣",
count: 0,
items: [],
};
@@ -52,6 +51,7 @@ async function searchYueYao(game: string): Promise<PlatformSearchResult> {
const YueYao: Platform = {
name: "月谣",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchYueYao,
};

View File

@@ -19,7 +19,6 @@ interface ZeroFiveResponse {
async function searchZeroFive(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "05的资源小站",
count: 0,
items: [],
};
@@ -75,6 +74,7 @@ async function searchZeroFive(game: string): Promise<PlatformSearchResult> {
const ZeroFive: Platform = {
name: "05的资源小站",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchZeroFive,
};

View File

@@ -7,7 +7,6 @@ const REGEX = /hover:underline" href="(?<URL>.+?)">\s*(?<NAME>.+?)\s*<\/a>/gs;
async function searchZhenHongXiaoZhan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "真红小站",
count: 0,
items: [],
};
@@ -52,6 +51,7 @@ async function searchZhenHongXiaoZhan(game: string): Promise<PlatformSearchResul
const ZhenHongXiaoZhan: Platform = {
name: "真红小站",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchZhenHongXiaoZhan,
};

View File

@@ -19,7 +19,6 @@ interface ZiLingDeMiaoMiaoWuResponse {
async function searchZiLingDeMiaoMiaoWu(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "梓澪の妙妙屋",
count: 0,
items: [],
};
@@ -75,6 +74,7 @@ async function searchZiLingDeMiaoMiaoWu(game: string): Promise<PlatformSearchRes
const ZiLingDeMiaoMiaoWu: Platform = {
name: "梓澪の妙妙屋",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchZiLingDeMiaoMiaoWu,
};

View File

@@ -5,7 +5,6 @@ const BASE_URL = "https://galzy.eu.org";
async function searchZiYuanShe(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "紫缘Gal",
count: 0,
items: [],
};
@@ -61,6 +60,7 @@ async function searchZiYuanShe(game: string): Promise<PlatformSearchResult> {
const ZiYuanShe: Platform = {
name: "紫缘Gal",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchZiYuanShe,
};

View File

@@ -19,12 +19,10 @@ import Nysoure from "./Nysoure";
import QingJiACG from "./QingJiACG";
import ShenShiTianTang from "./ShenShiTianTang";
import TaoHuaYuan from "./TaoHuaYuan";
import TianYouErCiYuan from "./TianYouErCiYuan";
import TouchGal from "./TouchGal";
import VikaACG from "./VikaACG";
import WeiZhiYunPan from "./WeiZhiYunPan";
import xxacg from "./xxacg";
import YingZhiGuang from "./YingZhiGuang";
import YouYuDeloli from "./YouYuDeloli";
import YueYao from "./YueYao";
import ZeroFive from "./ZeroFive";
@@ -53,12 +51,10 @@ const platforms: Platform[] = [
QingJiACG,
ShenShiTianTang,
TaoHuaYuan,
TianYouErCiYuan,
TouchGal,
VikaACG,
WeiZhiYunPan,
xxacg,
YingZhiGuang,
YouYuDeloli,
YueYao,
ZeroFive,

View File

@@ -9,7 +9,6 @@ function stripHtml(html: string): string {
async function searchXxacg(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "xxacg",
count: 0,
items: [],
};
@@ -56,7 +55,8 @@ async function searchXxacg(game: string): Promise<PlatformSearchResult> {
const xxacg: Platform = {
name: "xxacg",
color: "gold",
color: "white",
tags: ["Login", "magic", "NoSplDrive"],
magic: true,
search: searchXxacg,
};

View File

@@ -15,7 +15,6 @@ interface KunGalgameBuDingResponse {
async function searchKunGalgameBuDing(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "鲲Galgame补丁",
count: 0,
items: [],
};
@@ -69,6 +68,7 @@ async function searchKunGalgameBuDing(game: string): Promise<PlatformSearchResul
const KunGalgameBuDing: Platform = {
name: "鲲Galgame补丁",
color: "lime",
tags: ["NoReq", "SuDrive"],
magic: false,
search: searchKunGalgameBuDing,
};

View File

@@ -7,7 +7,6 @@ const REGEX = /<h4 class="media-heading"><a target="_blank" href="(?<URL>.*?)">(
async function searchTWOdfan(game: string): Promise<PlatformSearchResult> {
const searchResult: PlatformSearchResult = {
name: "2dfan",
count: 0,
items: [],
};
@@ -52,8 +51,9 @@ async function searchTWOdfan(game: string): Promise<PlatformSearchResult> {
const TWOdfan: Platform = {
name: "2dfan",
color: "lime",
magic: false,
color: "white",
tags: ["LoginPay", "magic", "MixDrive"],
magic: true,
search: searchTWOdfan,
};

View File

@@ -2,13 +2,13 @@
export interface SearchResultItem {
name: string;
url: string;
tags?: string[];
}
// 平台搜索的返回值
export interface PlatformSearchResult {
items: SearchResultItem[];
count: number;
name: string;
error?: string;
}
@@ -16,6 +16,7 @@ export interface PlatformSearchResult {
export interface Platform {
name: string;
color: string;
tags: string[];
magic: boolean;
search: (game: string, ...args: any[]) => Promise<PlatformSearchResult>;
}
@@ -24,6 +25,7 @@ export interface Platform {
export interface StreamResult {
name: string;
color: string;
tags: string[];
items: SearchResultItem[];
error?: string;
}