Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/locales/de-DE/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "Vielleicht später",
"settings_hint": "Sie können diese Option jederzeit in den Einstellungen ändern."
},
"favicon_service": "Favicon-Dienst",
"favicon_service_desc": "Dienst zum Abrufen von Website-Symbolen auswählen",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "Lokal abrufen",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "Skriptliste anzeigen",
"hide_script_list": "Skriptliste ausblenden"
Expand Down
5 changes: 5 additions & 0 deletions src/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "Maybe Later",
"settings_hint": "You can change this option in settings at any time."
},
"favicon_service": "Favicon Service",
"favicon_service_desc": "Choose the service for fetching website icons",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "Local Fetch",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "Show Script List",
"hide_script_list": "Hide Script List"
Expand Down
5 changes: 5 additions & 0 deletions src/locales/ja-JP/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "後で",
"settings_hint": "設定ページでいつでも変更できます。"
},
"favicon_service": "Favicon サービス",
"favicon_service_desc": "ウェブサイトアイコンの取得サービスを選択",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "ローカル取得",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "スクリプトリストを表示",
"hide_script_list": "スクリプトリストを非表示"
Expand Down
5 changes: 5 additions & 0 deletions src/locales/ru-RU/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "Может быть позже",
"settings_hint": "Вы можете изменить эту опцию в настройках в любое время."
},
"favicon_service": "Сервис Favicon",
"favicon_service_desc": "Выберите сервис для получения значков сайтов",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "Локальное получение",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "Показать список скриптов",
"hide_script_list": "Скрыть список скриптов"
Expand Down
5 changes: 5 additions & 0 deletions src/locales/vi-VN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "Để sau",
"settings_hint": "Bạn có thể thay đổi tùy chọn này trong cài đặt bất kỳ lúc nào."
},
"favicon_service": "Dịch vụ Favicon",
"favicon_service_desc": "Chọn dịch vụ để lấy biểu tượng trang web",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "Lấy cục bộ",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "Hiển thị danh sách script",
"hide_script_list": "Ẩn danh sách script"
Expand Down
5 changes: 5 additions & 0 deletions src/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "暂不启用",
"settings_hint": "你可以随时在设置中修改此选项。"
},
"favicon_service": "图标服务",
"favicon_service_desc": "选择获取网站图标的服务",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "本地获取",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "显示脚本列表",
"hide_script_list": "隐藏脚本列表"
Expand Down
5 changes: 5 additions & 0 deletions src/locales/zh-TW/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"maybe_later": "暫不啟用",
"settings_hint": "你可以隨時在設定中修改此選項。"
},
"favicon_service": "圖示服務",
"favicon_service_desc": "選擇取得網站圖示的服務",
"favicon_service_scriptcat": "ScriptCat",
"favicon_service_local": "本地取得",
"favicon_service_google": "Google",
"editor": {
"show_script_list": "顯示腳本列表",
"hide_script_list": "隱藏腳本列表"
Expand Down
4 changes: 3 additions & 1 deletion src/pages/options/routes/ScriptList/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "@App/app/repo/scripts";
import { fetchScript, fetchScriptList } from "@App/pages/store/features/script";
import { loadScriptFavicons } from "@App/pages/store/favicons";
import { systemConfig } from "@App/pages/store/global";
import { parseTags } from "@App/app/repo/metadata";
import { getCombinedMeta } from "@App/app/service/service_worker/utils";
import { cacheInstance } from "@App/app/cache";
Expand Down Expand Up @@ -76,7 +77,8 @@ export function useScriptDataManagement() {
setLoadingList(false);
cacheInstance.tx("faviconOPFSControl", async () => {
if (!mounted) return;
for await (const { chunkResults } of loadScriptFavicons(list)) {
const faviconService = await systemConfig.getFaviconService();
for await (const { chunkResults } of loadScriptFavicons(list, faviconService)) {
if (!mounted) return;
setScriptList((prev) => {
const favMap = new Map(chunkResults.map((r) => [r.uuid, r]));
Expand Down
34 changes: 34 additions & 0 deletions src/pages/options/routes/Setting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CustomTrans from "@App/pages/components/CustomTrans";
import { useSystemConfig } from "./utils";
import { subscribeMessage } from "@App/pages/store/global";
import { SystemConfigChange, type SystemConfigKey } from "@App/pkg/config/config";
import { FaviconDAO } from "@App/app/repo/favicon";
import { type TKeyValue } from "@Packages/message/message_queue";
import { useEffect, useMemo } from "react";
import { systemConfig } from "@App/pages/store/global";
Expand All @@ -41,6 +42,7 @@ function Setting() {
const [badgeTextColor, setBadgeTextColor, submitBadgeTextColor] = useSystemConfig("badge_text_color");
const [scriptMenuDisplayType, setScriptMenuDisplayType, submitScriptMenuDisplayType] =
useSystemConfig("script_menu_display_type");
const [faviconService, setFaviconService, submitFaviconService] = useSystemConfig("favicon_service");

const [editorTypeDefinition, setEditorTypeDefinition, submitEditorTypeDefinition] =
useSystemConfig("editor_type_definition");
Expand Down Expand Up @@ -81,6 +83,7 @@ function Setting() {
badge_background_color: setBadgeBackgroundColor,
badge_text_color: setBadgeTextColor,
script_menu_display_type: setScriptMenuDisplayType,
favicon_service: setFaviconService,
editor_type_definition: setEditorTypeDefinition,
} as const;
const hookMgr = new HookManager();
Expand Down Expand Up @@ -306,6 +309,37 @@ function Setting() {
</div>
</Space>
</div>

{/* Favicon 服务 */}
<div className="tw-flex tw-items-center tw-justify-between tw-min-h-9">
<div className="tw-flex tw-items-center tw-gap-4 tw-flex-1">
<span className="tw-min-w-20">{t("favicon_service")}</span>
<Select
value={faviconService}
className="tw-w-40 tw-max-w-50"
onChange={async (value) => {
submitFaviconService(value);
// 清除 favicon 缓存
try {
const faviconDAO = new FaviconDAO();
const allFavicons = await faviconDAO.find();
await Promise.all(allFavicons.map((f) => faviconDAO.delete(f.uuid)));
// 清除 OPFS 缓存:删除并重建目录
const opfsRoot = await navigator.storage.getDirectory();
await opfsRoot.removeEntry("cached_favicons", { recursive: true }).catch(() => {});
} catch {
// 忽略清除缓存的错误
}
Message.success(t("save_success")!);
}}
>
<Select.Option value="scriptcat">{t("favicon_service_scriptcat")}</Select.Option>
<Select.Option value="local">{t("favicon_service_local")}</Select.Option>
<Select.Option value="google">{t("favicon_service_google")}</Select.Option>
</Select>
</div>
<span className="tw-text-xs tw-ml-6 tw-flex-shrink-0">{t("favicon_service_desc")}</span>
</div>
</Space>
</Card>

Expand Down
32 changes: 26 additions & 6 deletions src/pages/store/favicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { v5 as uuidv5 } from "uuid";
import { getFaviconRootFolder } from "@App/app/service/service_worker/utils";
import { readBlobContent } from "@App/pkg/utils/encoding";
import type { FaviconService } from "@App/pkg/config/config";

let scriptDAO: ScriptDAO | null = null;
let faviconDAO: FaviconDAO | null = null;
Expand Down Expand Up @@ -179,8 +180,22 @@
return urls.filter((url) => !!url) as string[];
}

/**
* 根据服务类型获取favicon URL列表
*/
export async function fetchIconByService(domain: string, service: FaviconService): Promise<string[]> {
switch (service) {
case "scriptcat":
return [`https://ext.scriptcat.org/api/v1/open/favicons?domain=${encodeURIComponent(domain)}&sz=64`];
case "google":
return [`https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=64`];
case "local":
return fetchIconByDomain(domain);
}
}

// 获取脚本的favicon
export const getScriptFavicon = async (uuid: string): Promise<FaviconRecord[]> => {
export const getScriptFavicon = async (uuid: string, service: FaviconService = "scriptcat"): Promise<FaviconRecord[]> => {

Check failure on line 198 in src/pages/store/favicons.ts

View workflow job for this annotation

GitHub Actions / Run tests

Replace `uuid:·string,·service:·FaviconService·=·"scriptcat"` with `⏎··uuid:·string,⏎··service:·FaviconService·=·"scriptcat"⏎`
scriptDAO ||= new ScriptDAO();
faviconDAO ||= new FaviconDAO();
const script = await scriptDAO.get(uuid);
Expand All @@ -199,7 +214,7 @@
domains.map(async (domain) => {
try {
if (domain.domain) {
const icons = await fetchIconByDomain(domain.domain);
const icons = await fetchIconByService(domain.domain, service);
const icon = icons.length > 0 ? icons[0] : "";
return { match: domain.match, website: "http://" + domain.domain, icon };
}
Expand Down Expand Up @@ -233,6 +248,11 @@
// 文件不存在,下载并保存
const newFileHandle = await directoryHandle.getFileHandle(filename, { create: true });
const response = await fetch(iconUrl);
if (response.status >= 300) {
// 状态码异常,删除创建的空文件并抛出错误
await directoryHandle.removeEntry(filename).catch(() => {});
throw new Error(`Favicon fetch failed with status ${response.status}`);
}
const blob = await response.blob();
const writable = await newFileHandle.createWritable();
await writable.write(blob);
Expand All @@ -256,9 +276,9 @@
};

// 处理单个脚本的favicon
const processScriptFavicon = async (script: Script) => {
const processScriptFavicon = async (script: Script, service: FaviconService = "scriptcat") => {
const favFnAsync = async () => {
const icons = await getScriptFavicon(script.uuid); // 恒久。不会因SW重启而失效
const icons = await getScriptFavicon(script.uuid, service); // 恒久。不会因SW重启而失效
if (icons.length === 0) return [];
const newIcons = await Promise.all(
icons.map(async (icon) => {
Expand Down Expand Up @@ -305,7 +325,7 @@
type TFaviconStack = { chunkResults: FavIconResult[]; pendingCount: number };

// 处理favicon加载,以批次方式处理
export const loadScriptFavicons = async function* (scripts: Script[]) {
export const loadScriptFavicons = async function* (scripts: Script[], service: FaviconService = "scriptcat") {
const stack: TFaviconStack[] = [];
const asyncWaiter: { promise?: any; resolve?: any } = {};
const createPromise = () => {
Expand All @@ -319,7 +339,7 @@
const results: FavIconResult[] = [];
let waiting = false;
for (const script of scripts) {
processScriptFavicon(script).then((result: FavIconResult) => {
processScriptFavicon(script, service).then((result: FavIconResult) => {
results.push(result);
// 下一个 MacroTask 执行。
// 使用 requestAnimationFrame 而非setTimeout 是因为前台才要显示。而且网页绘画中时会延后这个
Expand Down
10 changes: 10 additions & 0 deletions src/pkg/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export type CloudSyncConfig = {
params: { [key: string]: any };
};

export type FaviconService = "scriptcat" | "local" | "google";

export type CATFileStorage = {
filesystem: FileSystemType;
params: { [key: string]: any };
Expand Down Expand Up @@ -474,6 +476,14 @@ export class SystemConfig {
getScriptMenuDisplayType(): Promise<"no_browser" | "all"> {
return this._get("script_menu_display_type", "all");
}

getFaviconService() {
return this._get<FaviconService>("favicon_service", "scriptcat");
}

setFaviconService(val: FaviconService) {
this._set("favicon_service", val);
}
}

let lazyScriptNamePrefix: string = "";
Expand Down
Loading