增加测速超时设置
This commit is contained in:
@@ -140,6 +140,32 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
// 集数过滤设置弹窗状态
|
||||
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(
|
||||
(episodeNumber: number): boolean => {
|
||||
@@ -205,7 +231,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
setAttemptedSources((prev) => new Set(prev).add(sourceKey));
|
||||
|
||||
try {
|
||||
const info = await getVideoResolutionFromM3u8(episodeUrl);
|
||||
const info = await getVideoResolutionFromM3u8(episodeUrl, speedTestTimeout);
|
||||
setVideoInfoMap((prev) => new Map(prev).set(sourceKey, info));
|
||||
} catch (error) {
|
||||
// 失败时保存错误状态
|
||||
@@ -219,7 +245,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
})
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
}, [speedTestTimeout]);
|
||||
|
||||
// 当有预计算结果时,先合并到videoInfoMap中
|
||||
useEffect(() => {
|
||||
@@ -252,21 +278,6 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
}
|
||||
}, [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 依赖避免循环触发
|
||||
useEffect(() => {
|
||||
const fetchVideoInfosInBatches = async () => {
|
||||
|
||||
@@ -95,6 +95,7 @@ export const UserMenu: React.FC = () => {
|
||||
const [defaultAggregateSearch, setDefaultAggregateSearch] = useState(true);
|
||||
const [doubanProxyUrl, setDoubanProxyUrl] = useState('');
|
||||
const [enableOptimization, setEnableOptimization] = useState(true);
|
||||
const [speedTestTimeout, setSpeedTestTimeout] = useState(4000); // 测速超时时间(毫秒)
|
||||
const [fluidSearch, setFluidSearch] = useState(true);
|
||||
const [liveDirectConnect, setLiveDirectConnect] = useState(false);
|
||||
const [tmdbBackdropDisabled, setTmdbBackdropDisabled] = useState(false);
|
||||
@@ -344,6 +345,11 @@ export const UserMenu: React.FC = () => {
|
||||
setEnableOptimization(JSON.parse(savedEnableOptimization));
|
||||
}
|
||||
|
||||
const savedSpeedTestTimeout = localStorage.getItem('speedTestTimeout');
|
||||
if (savedSpeedTestTimeout !== null) {
|
||||
setSpeedTestTimeout(Number(savedSpeedTestTimeout));
|
||||
}
|
||||
|
||||
const savedFluidSearch = localStorage.getItem('fluidSearch');
|
||||
const defaultFluidSearch =
|
||||
(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) => {
|
||||
setFluidSearch(value);
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -1439,6 +1452,63 @@ export const UserMenu: React.FC = () => {
|
||||
</label>
|
||||
</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>
|
||||
|
||||
@@ -138,7 +138,10 @@ export function processVideoUrl(originalUrl: string): string {
|
||||
* @param m3u8Url m3u8播放列表的URL
|
||||
* @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等
|
||||
loadSpeed: string; // 自动转换为KB/s或MB/s
|
||||
pingTime: number; // 网络延迟(毫秒)
|
||||
@@ -167,12 +170,12 @@ export async function getVideoResolutionFromM3u8(m3u8Url: string): Promise<{
|
||||
// 固定使用hls.js加载
|
||||
const hls = new Hls();
|
||||
|
||||
// 设置超时处理
|
||||
// 设置超时处理 - 使用传入的超时时间
|
||||
const timeout = setTimeout(() => {
|
||||
hls.destroy();
|
||||
video.remove();
|
||||
reject(new Error('Timeout loading video metadata'));
|
||||
}, 4000);
|
||||
}, timeoutMs);
|
||||
|
||||
video.onerror = () => {
|
||||
clearTimeout(timeout);
|
||||
|
||||
Reference in New Issue
Block a user