diff --git a/src/main/config/profile.ts b/src/main/config/profile.ts index 9dae81c..68b2643 100644 --- a/src/main/config/profile.ts +++ b/src/main/config/profile.ts @@ -218,10 +218,10 @@ async function fetchAndValidateSubscription(options: FetchOptions): Promise): Promise { const id = item.id || new Date().getTime().toString(16) - const newItem = { + const newItem: IProfileItem = { id, name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'), - type: item.type, + type: item.type!, url: item.url, substore: item.substore || false, interval: item.interval || 0, @@ -230,85 +230,66 @@ export async function createProfile(item: Partial): Promise = { - url: item.url, - mixedPort, - userAgent: userAgent || `mihomo.party/v${app.getVersion()} (clash.meta)`, - authToken: item.authToken, - substore: newItem.substore || false - } + // Remote + if (!item.url) throw new Error('Empty URL') - let result: FetchResult - let finalUseProxy = newItem.useProxy + const { userAgent, subscriptionTimeout = 30000 } = await getAppConfig() + const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig() + const userItemTimeoutMs = (newItem.updateTimeout || 5) * 1000 - if (newItem.useProxy) { - result = await fetchAndValidateSubscription({ - ...baseOptions, - useProxy: true, - timeout: subscriptionTimeout - }) - } else if (newItem.substore) { - // SubStore requests (especially collections) need more time as they fetch and merge multiple subscriptions - // Use the full subscriptionTimeout since SubStore is a local server and doesn't need smart fallback - result = await fetchAndValidateSubscription({ - ...baseOptions, - useProxy: false, - timeout: subscriptionTimeout - }) - } else { - const smartTimeout = 5000 - try { - result = await fetchAndValidateSubscription({ - ...baseOptions, - useProxy: false, - timeout: smartTimeout - }) - } catch (directError) { - try { - result = await fetchAndValidateSubscription({ - ...baseOptions, - useProxy: true, - timeout: smartTimeout - }) - finalUseProxy = true - } catch { - throw directError - } - } - } + const baseOptions: Omit = { + url: item.url, + mixedPort, + userAgent: userAgent || `mihomo.party/v${app.getVersion()} (clash.meta)`, + authToken: item.authToken, + substore: newItem.substore || false + } - newItem.useProxy = finalUseProxy - const { data, headers } = result + const fetchSub = (useProxy: boolean, timeout: number) => + fetchAndValidateSubscription({ ...baseOptions, useProxy, timeout }) - if (headers['content-disposition'] && newItem.name === 'Remote File') { - newItem.name = parseFilename(headers['content-disposition']) + let result: FetchResult + if (newItem.useProxy || newItem.substore) { + result = await fetchSub(newItem.useProxy!, userItemTimeoutMs) + } else { + try { + result = await fetchSub(false, userItemTimeoutMs) + } catch (directError) { + try { + // smart fallback + result = await fetchSub(true, subscriptionTimeout) + } catch { + throw directError } - if (headers['profile-web-page-url']) { - newItem.home = headers['profile-web-page-url'] - } - if (headers['profile-update-interval'] && !item.allowFixedInterval) { - newItem.interval = parseInt(headers['profile-update-interval']) * 60 - } - if (headers['subscription-userinfo']) { - newItem.extra = parseSubinfo(headers['subscription-userinfo']) - } - await setProfileStr(id, data) - break - } - case 'local': { - await setProfileStr(id, item.file || '') - break } } + + const { data, headers } = result + + if (headers['content-disposition'] && newItem.name === 'Remote File') { + newItem.name = parseFilename(headers['content-disposition']) + } + if (headers['profile-web-page-url']) { + newItem.home = headers['profile-web-page-url'] + } + if (headers['profile-update-interval'] && !item.allowFixedInterval) { + newItem.interval = parseInt(headers['profile-update-interval']) * 60 + } + if (headers['subscription-userinfo']) { + newItem.extra = parseSubinfo(headers['subscription-userinfo']) + } + + await setProfileStr(id, data) return newItem } diff --git a/src/renderer/src/components/profiles/edit-info-modal.tsx b/src/renderer/src/components/profiles/edit-info-modal.tsx index 54cbcbc..e0d3082 100644 --- a/src/renderer/src/components/profiles/edit-info-modal.tsx +++ b/src/renderer/src/components/profiles/edit-info-modal.tsx @@ -32,7 +32,10 @@ const EditInfoModal: React.FC = (props) => { const { item, updateProfileItem, onClose } = props const { overrideConfig } = useOverrideConfig() const { items: overrideItems = [] } = overrideConfig || {} - const [values, setValues] = useState(item) + const [values, setValues] = useState({ + ...item, + updateTimeout: item.updateTimeout ?? 5 + }) const inputWidth = 'w-[400px] md:w-[400px] lg:w-[600px] xl:w-[800px]' const { t } = useTranslation() @@ -40,6 +43,7 @@ const EditInfoModal: React.FC = (props) => { try { const updatedItem = { ...values, + updateTimeout: values.updateTimeout ?? 5, override: values.override?.filter( (i) => overrideItems.find((t) => t.id === i) && !overrideItems.find((t) => t.id === i)?.global @@ -192,6 +196,24 @@ const EditInfoModal: React.FC = (props) => { )} )} + + { + if (v === '') { + setValues({ ...values, updateTimeout: undefined as unknown as number }) + return + } + if (/^\d+$/.test(v)) { + setValues({ ...values, updateTimeout: parseInt(v, 10) }) + } + }} + placeholder={t('profiles.editInfo.updateTimeoutPlaceholder')} + /> +
{overrideItems diff --git a/src/renderer/src/locales/en-US.json b/src/renderer/src/locales/en-US.json index ce1c916..4f50eea 100644 --- a/src/renderer/src/locales/en-US.json +++ b/src/renderer/src/locales/en-US.json @@ -484,6 +484,8 @@ "profiles.editInfo.override.global": "Global", "profiles.editInfo.override.noAvailable": "No available overrides", "profiles.editInfo.override.add": "Add Override", + "profiles.editInfo.updateTimeout": "Update Timeout", + "profiles.editInfo.updateTimeoutPlaceholder": "Timeout in seconds", "profiles.editFile.title": "Edit Profile", "profiles.editFile.notice": "Note: Changes made here will be reset after profile update. For custom configurations, please use", "profiles.editFile.override": "Override", diff --git a/src/renderer/src/locales/fa-IR.json b/src/renderer/src/locales/fa-IR.json index a986340..585edb3 100644 --- a/src/renderer/src/locales/fa-IR.json +++ b/src/renderer/src/locales/fa-IR.json @@ -454,6 +454,8 @@ "profiles.editInfo.override.global": "جهانی", "profiles.editInfo.override.noAvailable": "جایگزینی‌ای در دسترس نیست", "profiles.editInfo.override.add": "افزودن جایگزینی", + "profiles.editInfo.updateTimeout": "زمان انتظار به‌روزرسانی", + "profiles.editInfo.updateTimeoutPlaceholder": "زمان پایان دادن به ثانیه", "profiles.editInfo.intervalPlaceholder": "مثال: 30 یا '0 * * * *'", "profiles.editInfo.intervalInvalid": "نامعتبر", "profiles.editInfo.intervalMinutes": "فاصله زمانی ثابت به دقیقه", diff --git a/src/renderer/src/locales/ru-RU.json b/src/renderer/src/locales/ru-RU.json index ffc001c..75da680 100644 --- a/src/renderer/src/locales/ru-RU.json +++ b/src/renderer/src/locales/ru-RU.json @@ -454,6 +454,8 @@ "profiles.editInfo.override.global": "Глобальный", "profiles.editInfo.override.noAvailable": "Нет доступных переопределений", "profiles.editInfo.override.add": "Добавить переопределение", + "profiles.editInfo.updateTimeout": "Таймаут обновления", + "profiles.editInfo.updateTimeoutPlaceholder": "Время ожидания в секундах", "profiles.editInfo.intervalPlaceholder": "например: 30 или '0 * * * *'", "profiles.editInfo.intervalInvalid": "Недействительно", "profiles.editInfo.intervalMinutes": "Фиксированный интервал в минутах", diff --git a/src/renderer/src/locales/zh-CN.json b/src/renderer/src/locales/zh-CN.json index 6ba2b2d..d26077d 100644 --- a/src/renderer/src/locales/zh-CN.json +++ b/src/renderer/src/locales/zh-CN.json @@ -489,6 +489,8 @@ "profiles.editInfo.override.global": "全局", "profiles.editInfo.override.noAvailable": "没有可用的覆写", "profiles.editInfo.override.add": "添加覆写", + "profiles.editInfo.updateTimeout": "更新超时时间", + "profiles.editInfo.updateTimeoutPlaceholder": "以秒为单位的超时时间", "profiles.editFile.title": "编辑订阅", "profiles.editFile.notice": "注意:此处编辑配置更新订阅后会还原,如需要自定义配置请使用", "profiles.editFile.override": "覆写", diff --git a/src/renderer/src/locales/zh-TW.json b/src/renderer/src/locales/zh-TW.json index f493950..62c1fe3 100644 --- a/src/renderer/src/locales/zh-TW.json +++ b/src/renderer/src/locales/zh-TW.json @@ -489,6 +489,8 @@ "profiles.editInfo.override.global": "全局", "profiles.editInfo.override.noAvailable": "沒有可用的覆寫", "profiles.editInfo.override.add": "添加覆寫", + "profiles.editInfo.updateTimeout": "更新逾時時間", + "profiles.editInfo.updateTimeoutPlaceholder": "以秒為單位的超時時間", "profiles.editFile.title": "編輯訂閱", "profiles.editFile.notice": "注意:此處編輯配置更新訂閱後會還原,如需要自定義配置請使用", "profiles.editFile.override": "覆寫", diff --git a/src/shared/types.d.ts b/src/shared/types.d.ts index 0eadc19..da520f0 100644 --- a/src/shared/types.d.ts +++ b/src/shared/types.d.ts @@ -497,6 +497,7 @@ interface IProfileItem { allowFixedInterval?: boolean autoUpdate?: boolean authToken?: string + updateTimeout?: number } interface ISubStoreSub {