feat: show search page for multiple search results
This commit is contained in:
@@ -6,37 +6,45 @@ import { SiteItem, siteList } from "@/utils/siteList";
|
||||
import type { LibItem } from "@/utils/libSites";
|
||||
|
||||
const App = memo(function ({ libItem, CODE }: { libItem: LibItem; CODE: string }) {
|
||||
// 默认不显示
|
||||
// 默认不显示
|
||||
const DEF_DIS = [
|
||||
...["AvJoy", "baihuse", "GGJAV", "AV01", "18sex", "highporn"],
|
||||
...["JavBus", "JavDB", "JAVLib", "MISSAV_"],
|
||||
];
|
||||
const [disables, setDisables] = useState(GM_getValue<SiteItem["name"][]>("disable", DEF_DIS));
|
||||
const [multipleNavi, setMultipleNavi] = useState(GM_getValue<boolean>("multipleNavi", true));
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="jop-list">
|
||||
{siteList.map((siteItem) => {
|
||||
const hidden = disables.find((disItem) => disItem === siteItem.name) === undefined;
|
||||
const sameSite = libItem.name !== siteItem.disableLibItemName;
|
||||
if (hidden && sameSite) {
|
||||
return <SiteBtn siteItem={siteItem} CODE={CODE} key={siteItem.name} />;
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
})}
|
||||
{siteList
|
||||
.filter(
|
||||
(siteItem) =>
|
||||
!disables.includes(siteItem.name) && libItem.name !== siteItem.disableLibItemName,
|
||||
)
|
||||
.map((siteItem) => (
|
||||
<SiteBtn
|
||||
siteItem={siteItem}
|
||||
CODE={CODE}
|
||||
key={siteItem.name}
|
||||
multipleNavi={multipleNavi}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Setting
|
||||
siteList={siteList}
|
||||
setDisables={(disable) => {
|
||||
setDisables(disable);
|
||||
GM_setValue("disable", disable);
|
||||
}}
|
||||
disables={disables}
|
||||
/>
|
||||
</div>
|
||||
<Setting
|
||||
siteList={siteList}
|
||||
setDisables={(disable) => {
|
||||
setDisables(disable);
|
||||
GM_setValue("disable", disable);
|
||||
}}
|
||||
multipleNavi={multipleNavi}
|
||||
setMultipleNavi={(multipleNavi) => {
|
||||
setMultipleNavi(multipleNavi);
|
||||
GM_setValue("multipleNavi", multipleNavi);
|
||||
}}
|
||||
disables={disables}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { Dispatch, StateUpdater, useState } from "preact/hooks";
|
||||
import { GM_setValue } from "$";
|
||||
import { SiteItem } from "@/utils/siteList";
|
||||
import Checkbox from "./Checkbox";
|
||||
|
||||
const Setting = ({
|
||||
siteList,
|
||||
setDisables,
|
||||
disables,
|
||||
}: {
|
||||
type Props = {
|
||||
siteList: SiteItem[];
|
||||
setDisables: Dispatch<StateUpdater<string[]>>;
|
||||
disables: SiteItem["name"][];
|
||||
}) => {
|
||||
multipleNavi: boolean;
|
||||
setMultipleNavi: Dispatch<StateUpdater<boolean>>;
|
||||
};
|
||||
|
||||
const Setting = ({ siteList, setDisables, disables, multipleNavi, setMultipleNavi }: Props) => {
|
||||
const [showSetting, setShowSetting] = useState(true);
|
||||
|
||||
const hanleListChange = (item: SiteItem, isHidden: boolean) => {
|
||||
@@ -21,6 +22,11 @@ const Setting = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleNaviChange = (checked: boolean) => {
|
||||
setMultipleNavi(checked);
|
||||
GM_setValue("multipleNavi", checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!showSetting && (
|
||||
@@ -31,9 +37,10 @@ const Setting = ({
|
||||
{showSetting && (
|
||||
<>
|
||||
<div className="jop-setting">
|
||||
<Group title="勾选默认显示的网站:">
|
||||
<Group title="勾选默认展示">
|
||||
{siteList.map((item) => {
|
||||
const isHidden = disables.includes(item.name);
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
label={item.name}
|
||||
@@ -43,6 +50,16 @@ const Setting = ({
|
||||
);
|
||||
})}
|
||||
</Group>
|
||||
|
||||
<Group title="其他设置">
|
||||
<Checkbox
|
||||
label="展示多个搜索结果"
|
||||
value={multipleNavi}
|
||||
tip="一个站点内出现多条匹配结果时,打开后跳转搜索结果页"
|
||||
onChange={handleNaviChange}
|
||||
/>
|
||||
{/* <Checkbox label="简洁模式" value={true} onChange={() => {}} /> */}
|
||||
</Group>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { handleFetch, handleFetchJavBle } from "@/utils/xhr";
|
||||
import { fetcher } from "@/utils/xhr";
|
||||
import { SiteItem } from "@/utils/siteList";
|
||||
import { memo, useEffect, useState } from "preact/compat";
|
||||
|
||||
interface Status {
|
||||
isSuccess: "pedding" | "rejected" | "fulfilled";
|
||||
hasSubtitle: boolean;
|
||||
hasLeakage: boolean;
|
||||
targetLink: string;
|
||||
resultLink?: string;
|
||||
tag?: string;
|
||||
}
|
||||
const SiteBtn = memo(({ siteItem, CODE }: { siteItem: SiteItem; CODE: string }) => {
|
||||
type Props = {
|
||||
siteItem: SiteItem;
|
||||
CODE: string;
|
||||
multipleNavi?: boolean;
|
||||
};
|
||||
|
||||
const SiteBtn = memo(({ siteItem, CODE, multipleNavi }: Props) => {
|
||||
const { name, codeFormater } = siteItem;
|
||||
/** 格式化 CODE */
|
||||
const formatCode = codeFormater ? codeFormater(CODE) : CODE;
|
||||
@@ -16,23 +21,22 @@ const SiteBtn = memo(({ siteItem, CODE }: { siteItem: SiteItem; CODE: string })
|
||||
|
||||
const [status, setStatus] = useState<Status>({
|
||||
isSuccess: "pedding",
|
||||
hasSubtitle: false,
|
||||
hasLeakage: false,
|
||||
targetLink: "",
|
||||
tag: "",
|
||||
resultLink: "",
|
||||
});
|
||||
const { isSuccess, hasSubtitle, hasLeakage, targetLink } = status;
|
||||
const { isSuccess, tag, resultLink } = status;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMethod = name === "Jable" ? handleFetchJavBle : handleFetch;
|
||||
fetchMethod(siteItem, link, formatCode).then((res) => {
|
||||
fetcher({ siteItem, targetLink: link, CODE: formatCode }).then((res) => {
|
||||
const resultLink = multipleNavi && res.multipleRes ? res.multipResLink : res.targetLink;
|
||||
|
||||
setStatus({
|
||||
isSuccess: res.isSuccess ? "fulfilled" : "rejected",
|
||||
hasSubtitle: res.hasSubtitle,
|
||||
hasLeakage: res.hasLeakage,
|
||||
targetLink: res.targetLink,
|
||||
tag: multipleNavi && res.multipleRes ? "多结果" : res.tag,
|
||||
resultLink,
|
||||
});
|
||||
});
|
||||
}, [handleFetch, siteItem, CODE, link]);
|
||||
}, [fetcher, siteItem, CODE, link, multipleNavi]);
|
||||
|
||||
const colorClass =
|
||||
isSuccess === "pedding"
|
||||
@@ -45,14 +49,9 @@ const SiteBtn = memo(({ siteItem, CODE }: { siteItem: SiteItem; CODE: string })
|
||||
<a
|
||||
className={"jop-button " + colorClass}
|
||||
target="_blank"
|
||||
href={targetLink === "" ? link : targetLink}
|
||||
href={resultLink === "" ? link : resultLink}
|
||||
>
|
||||
{(hasSubtitle || hasLeakage) && (
|
||||
<div className="jop-button_label">
|
||||
{hasSubtitle && <span>字幕 </span>}
|
||||
{hasLeakage && <span> 无码</span>}
|
||||
</div>
|
||||
)}
|
||||
{tag && <div className="jop-button_label">{tag}</div>}
|
||||
|
||||
{/* 加载动画 */}
|
||||
{/* {isSuccess === "pedding" && <span className="jop-loading"> </span>} */}
|
||||
|
||||
@@ -3,9 +3,10 @@ import type { DomQuery_get, DomQuery_parser, SiteItem } from "./siteList";
|
||||
|
||||
export type FetchResult = {
|
||||
isSuccess: boolean;
|
||||
targetLink: string;
|
||||
hasSubtitle: boolean;
|
||||
hasLeakage: boolean;
|
||||
targetLink?: string;
|
||||
tag?: string;
|
||||
multipResLink?: string;
|
||||
multipleRes?: boolean;
|
||||
};
|
||||
|
||||
/** 针对视频播放页进行解析,寻找字幕等信息 */
|
||||
@@ -38,35 +39,47 @@ function serachPageParser(
|
||||
{ linkQuery, titleQuery, listIndex = 0 }: DomQuery_parser,
|
||||
siteHostName: string,
|
||||
CODE: string,
|
||||
searchPageLink: string,
|
||||
) {
|
||||
const doc = new DOMParser().parseFromString(responseText, "text/html");
|
||||
|
||||
const linkNode = linkQuery ? doc.querySelectorAll<HTMLAnchorElement>(linkQuery)[listIndex] : null;
|
||||
const titleNode = titleQuery ? doc.querySelectorAll(titleQuery)[listIndex] : null;
|
||||
const titleNodes = titleQuery ? doc.querySelectorAll(titleQuery) : [];
|
||||
const linkNodes = linkQuery ? doc.querySelectorAll<HTMLAnchorElement>(linkQuery) : [];
|
||||
|
||||
const titleNode = titleNodes[listIndex];
|
||||
|
||||
const linkNode = linkNodes[listIndex];
|
||||
const titleNodeText = titleNode ? titleNode?.outerHTML : "";
|
||||
|
||||
const codeRegex = /[a-zA-Z]{3,5}-\d{3,5}/;
|
||||
const matchCode = titleNodeText.match(codeRegex);
|
||||
const isSuccess =
|
||||
linkNode && titleNode && matchCode && isCaseInsensitiveEqual(matchCode[0], CODE);
|
||||
if (isSuccess) {
|
||||
const targetLinkText = linkNode.href.replace(linkNode.hostname, siteHostName);
|
||||
return {
|
||||
isSuccess: true,
|
||||
targetLink: targetLinkText,
|
||||
hasLeakage: regEnum.leakage.test(titleNodeText),
|
||||
hasSubtitle: regEnum.subtitle.test(titleNodeText),
|
||||
};
|
||||
} else {
|
||||
return { targetLink: "", isSuccess: false, hasSubtitle: false, hasLeakage: false };
|
||||
|
||||
if (!isSuccess) {
|
||||
return { isSuccess: false };
|
||||
}
|
||||
const targetLinkText = linkNode.href.replace(linkNode.hostname, siteHostName);
|
||||
const hasLeakage = regEnum.leakage.test(titleNodeText);
|
||||
const hasSubtitle = regEnum.subtitle.test(titleNodeText);
|
||||
const tags = [];
|
||||
if (hasLeakage) tags.push("无码");
|
||||
if (hasSubtitle) tags.push("字幕");
|
||||
return {
|
||||
isSuccess: true,
|
||||
targetLink: targetLinkText,
|
||||
multipResLink: searchPageLink,
|
||||
multipleRes: titleNodes.length > 1,
|
||||
tag: tags.join(" "),
|
||||
};
|
||||
}
|
||||
|
||||
export const handleFetch = async (
|
||||
siteItem: SiteItem,
|
||||
targetLink: string,
|
||||
CODE: string,
|
||||
): Promise<FetchResult> => {
|
||||
type Args = {
|
||||
siteItem: SiteItem;
|
||||
targetLink: string;
|
||||
CODE: string;
|
||||
};
|
||||
export const baseFetcher = async ({ siteItem, targetLink, CODE }: Args): Promise<FetchResult> => {
|
||||
try {
|
||||
const response = await gmGet({ url: targetLink });
|
||||
if (isErrorCode(response.status)) {
|
||||
@@ -82,26 +95,35 @@ export const handleFetch = async (
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...serachPageParser(response.responseText, siteItem.domQuery, siteItem.hostname, CODE),
|
||||
...serachPageParser(
|
||||
response.responseText,
|
||||
siteItem.domQuery,
|
||||
siteItem.hostname,
|
||||
CODE,
|
||||
targetLink,
|
||||
),
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
isSuccess: false,
|
||||
targetLink: targetLink,
|
||||
hasSubtitle: false,
|
||||
hasLeakage: false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/** jable 有些域名是带 -c */
|
||||
export const handleFetchJavBle = async (
|
||||
siteItem: SiteItem,
|
||||
targetLink: string,
|
||||
CODE: string,
|
||||
): Promise<FetchResult> => {
|
||||
const res = await handleFetch(siteItem, targetLink, CODE);
|
||||
const newLink = targetLink.slice(0, -1) + "-c/";
|
||||
return res.isSuccess ? res : await handleFetch(siteItem, newLink, CODE);
|
||||
export const javbleFetcher = async (args: Args): Promise<FetchResult> => {
|
||||
const res = await baseFetcher(args);
|
||||
if (res.isSuccess) return res;
|
||||
const newLink = args.targetLink.slice(0, -1) + "-c/";
|
||||
return await baseFetcher({ ...args, targetLink: newLink });
|
||||
};
|
||||
|
||||
export const fetcher = (args: Args) => {
|
||||
if (args.siteItem.name === "Jable") {
|
||||
return javbleFetcher(args);
|
||||
}
|
||||
|
||||
return baseFetcher(args);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user