增加测速超时设置

This commit is contained in:
mtvpls
2026-01-17 00:28:51 +08:00
parent ce6d02c023
commit 2587479970
3 changed files with 104 additions and 20 deletions
+28 -17
View File
@@ -140,6 +140,32 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
// 集数过滤设置弹窗状态 // 集数过滤设置弹窗状态
const [showFilterSettings, setShowFilterSettings] = useState<boolean>(false); const [showFilterSettings, setShowFilterSettings] = useState<boolean>(false);
// 读取本地"优选和测速"开关,默认开启
const [optimizationEnabled] = useState<boolean>(() => {
if (typeof window !== 'undefined') {
const saved = localStorage.getItem('enableOptimization');
if (saved !== null) {
try {
return JSON.parse(saved);
} catch {
/* ignore */
}
}
}
return true;
});
// 读取测速超时设置,默认4秒
const [speedTestTimeout] = useState<number>(() => {
if (typeof window !== 'undefined') {
const saved = localStorage.getItem('speedTestTimeout');
if (saved !== null) {
return Number(saved);
}
}
return 4000;
});
// 集数过滤逻辑 // 集数过滤逻辑
const isEpisodeFiltered = useCallback( const isEpisodeFiltered = useCallback(
(episodeNumber: number): boolean => { (episodeNumber: number): boolean => {
@@ -205,7 +231,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
setAttemptedSources((prev) => new Set(prev).add(sourceKey)); setAttemptedSources((prev) => new Set(prev).add(sourceKey));
try { try {
const info = await getVideoResolutionFromM3u8(episodeUrl); const info = await getVideoResolutionFromM3u8(episodeUrl, speedTestTimeout);
setVideoInfoMap((prev) => new Map(prev).set(sourceKey, info)); setVideoInfoMap((prev) => new Map(prev).set(sourceKey, info));
} catch (error) { } catch (error) {
// 失败时保存错误状态 // 失败时保存错误状态
@@ -219,7 +245,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
}) })
); );
} }
}, []); }, [speedTestTimeout]);
// 当有预计算结果时,先合并到videoInfoMap中 // 当有预计算结果时,先合并到videoInfoMap中
useEffect(() => { useEffect(() => {
@@ -252,21 +278,6 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
} }
}, [precomputedVideoInfo]); }, [precomputedVideoInfo]);
// 读取本地"优选和测速"开关,默认开启
const [optimizationEnabled] = useState<boolean>(() => {
if (typeof window !== 'undefined') {
const saved = localStorage.getItem('enableOptimization');
if (saved !== null) {
try {
return JSON.parse(saved);
} catch {
/* ignore */
}
}
}
return true;
});
// 当切换到换源tab并且有源数据时,异步获取视频信息 - 移除 attemptedSources 依赖避免循环触发 // 当切换到换源tab并且有源数据时,异步获取视频信息 - 移除 attemptedSources 依赖避免循环触发
useEffect(() => { useEffect(() => {
const fetchVideoInfosInBatches = async () => { const fetchVideoInfosInBatches = async () => {
+70
View File
@@ -95,6 +95,7 @@ export const UserMenu: React.FC = () => {
const [defaultAggregateSearch, setDefaultAggregateSearch] = useState(true); const [defaultAggregateSearch, setDefaultAggregateSearch] = useState(true);
const [doubanProxyUrl, setDoubanProxyUrl] = useState(''); const [doubanProxyUrl, setDoubanProxyUrl] = useState('');
const [enableOptimization, setEnableOptimization] = useState(true); const [enableOptimization, setEnableOptimization] = useState(true);
const [speedTestTimeout, setSpeedTestTimeout] = useState(4000); // 测速超时时间(毫秒)
const [fluidSearch, setFluidSearch] = useState(true); const [fluidSearch, setFluidSearch] = useState(true);
const [liveDirectConnect, setLiveDirectConnect] = useState(false); const [liveDirectConnect, setLiveDirectConnect] = useState(false);
const [tmdbBackdropDisabled, setTmdbBackdropDisabled] = useState(false); const [tmdbBackdropDisabled, setTmdbBackdropDisabled] = useState(false);
@@ -344,6 +345,11 @@ export const UserMenu: React.FC = () => {
setEnableOptimization(JSON.parse(savedEnableOptimization)); setEnableOptimization(JSON.parse(savedEnableOptimization));
} }
const savedSpeedTestTimeout = localStorage.getItem('speedTestTimeout');
if (savedSpeedTestTimeout !== null) {
setSpeedTestTimeout(Number(savedSpeedTestTimeout));
}
const savedFluidSearch = localStorage.getItem('fluidSearch'); const savedFluidSearch = localStorage.getItem('fluidSearch');
const defaultFluidSearch = const defaultFluidSearch =
(window as any).RUNTIME_CONFIG?.FLUID_SEARCH !== false; (window as any).RUNTIME_CONFIG?.FLUID_SEARCH !== false;
@@ -642,6 +648,13 @@ export const UserMenu: React.FC = () => {
} }
}; };
const handleSpeedTestTimeoutChange = (value: number) => {
setSpeedTestTimeout(value);
if (typeof window !== 'undefined') {
localStorage.setItem('speedTestTimeout', String(value));
}
};
const handleFluidSearchToggle = (value: boolean) => { const handleFluidSearchToggle = (value: boolean) => {
setFluidSearch(value); setFluidSearch(value);
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@@ -1439,6 +1452,63 @@ export const UserMenu: React.FC = () => {
</label> </label>
</div> </div>
{/* 测速超时设置 */}
{enableOptimization && (
<div className='ml-4 mt-2 space-y-2'>
<div className='flex items-center justify-between'>
<span className='text-xs text-gray-600 dark:text-gray-400'>
</span>
<span className='text-xs font-medium text-gray-700 dark:text-gray-300'>
{speedTestTimeout / 1000}
</span>
</div>
<div className='flex items-center gap-2'>
<input
type='range'
min='4000'
max='30000'
step='1000'
value={speedTestTimeout}
onChange={(e) => handleSpeedTestTimeoutChange(Number(e.target.value))}
className='flex-1 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700'
style={{
background: `linear-gradient(to right, #10b981 0%, #10b981 ${((speedTestTimeout - 4000) / (30000 - 4000)) * 100}%, #e5e7eb ${((speedTestTimeout - 4000) / (30000 - 4000)) * 100}%, #e5e7eb 100%)`
}}
/>
</div>
<div className='flex justify-between text-xs text-gray-500 dark:text-gray-400'>
<button
onClick={() => handleSpeedTestTimeoutChange(4000)}
className={`px-2 py-0.5 rounded ${speedTestTimeout === 4000 ? 'bg-green-500 text-white' : 'hover:bg-gray-200 dark:hover:bg-gray-700'}`}
>
4
</button>
<button
onClick={() => handleSpeedTestTimeoutChange(10000)}
className={`px-2 py-0.5 rounded ${speedTestTimeout === 10000 ? 'bg-green-500 text-white' : 'hover:bg-gray-200 dark:hover:bg-gray-700'}`}
>
10
</button>
<button
onClick={() => handleSpeedTestTimeoutChange(20000)}
className={`px-2 py-0.5 rounded ${speedTestTimeout === 20000 ? 'bg-green-500 text-white' : 'hover:bg-gray-200 dark:hover:bg-gray-700'}`}
>
20
</button>
<button
onClick={() => handleSpeedTestTimeoutChange(30000)}
className={`px-2 py-0.5 rounded ${speedTestTimeout === 30000 ? 'bg-green-500 text-white' : 'hover:bg-gray-200 dark:hover:bg-gray-700'}`}
>
30
</button>
</div>
<p className='text-xs text-gray-500 dark:text-gray-400 italic'>
使4
</p>
</div>
)}
{/* 流式搜索 */} {/* 流式搜索 */}
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
<div> <div>
+6 -3
View File
@@ -138,7 +138,10 @@ export function processVideoUrl(originalUrl: string): string {
* @param m3u8Url m3u8播放列表的URL * @param m3u8Url m3u8播放列表的URL
* @returns Promise<{quality: string, loadSpeed: string, pingTime: number, bitrate: string}> 视频质量等级和网络信息 * @returns Promise<{quality: string, loadSpeed: string, pingTime: number, bitrate: string}> 视频质量等级和网络信息
*/ */
export async function getVideoResolutionFromM3u8(m3u8Url: string): Promise<{ export async function getVideoResolutionFromM3u8(
m3u8Url: string,
timeoutMs: number = 4000
): Promise<{
quality: string; // 如720p、1080p等 quality: string; // 如720p、1080p等
loadSpeed: string; // 自动转换为KB/s或MB/s loadSpeed: string; // 自动转换为KB/s或MB/s
pingTime: number; // 网络延迟(毫秒) pingTime: number; // 网络延迟(毫秒)
@@ -167,12 +170,12 @@ export async function getVideoResolutionFromM3u8(m3u8Url: string): Promise<{
// 固定使用hls.js加载 // 固定使用hls.js加载
const hls = new Hls(); const hls = new Hls();
// 设置超时处理 // 设置超时处理 - 使用传入的超时时间
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
hls.destroy(); hls.destroy();
video.remove(); video.remove();
reject(new Error('Timeout loading video metadata')); reject(new Error('Timeout loading video metadata'));
}, 4000); }, timeoutMs);
video.onerror = () => { video.onerror = () => {
clearTimeout(timeout); clearTimeout(timeout);