mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-21 09:14:42 +08:00
feat: add optimizationEnabled switch
This commit is contained in:
@@ -129,6 +129,21 @@ function PlayPageClient() {
|
||||
null
|
||||
);
|
||||
|
||||
// 优选和测速开关
|
||||
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;
|
||||
});
|
||||
|
||||
// 保存优选时的测速结果,避免EpisodeSelector重复测速
|
||||
const [precomputedVideoInfo, setPrecomputedVideoInfo] = useState<
|
||||
Map<string, { quality: string; loadSpeed: string; pingTime: number }>
|
||||
@@ -524,35 +539,25 @@ function PlayPageClient() {
|
||||
return;
|
||||
}
|
||||
|
||||
let needLoadSource = currentSource;
|
||||
let needLoadId = currentId;
|
||||
if ((!currentSource && !currentId) || needPreferRef.current) {
|
||||
let detailData: SearchResult = sourcesInfo[0];
|
||||
if (
|
||||
((!currentSource && !currentId) || needPreferRef.current) &&
|
||||
optimizationEnabled
|
||||
) {
|
||||
setLoadingStage('preferring');
|
||||
setLoadingMessage('⚡ 正在优选最佳播放源...');
|
||||
|
||||
const preferredSource = await preferBestSource(sourcesInfo);
|
||||
setNeedPrefer(false);
|
||||
setCurrentSource(preferredSource.source);
|
||||
setCurrentId(preferredSource.id);
|
||||
setVideoYear(preferredSource.year);
|
||||
needLoadSource = preferredSource.source;
|
||||
needLoadId = preferredSource.id;
|
||||
detailData = await preferBestSource(sourcesInfo);
|
||||
}
|
||||
|
||||
console.log(sourcesInfo);
|
||||
console.log(needLoadSource, needLoadId);
|
||||
const detailData = sourcesInfo.find(
|
||||
(source) =>
|
||||
source.source === needLoadSource &&
|
||||
source.id.toString() === needLoadId.toString()
|
||||
);
|
||||
if (!detailData) {
|
||||
setError('未找到匹配结果');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setVideoTitle(detailData.title || videoTitleRef.current);
|
||||
console.log(detailData.source, detailData.id);
|
||||
|
||||
setNeedPrefer(false);
|
||||
setCurrentSource(detailData.source);
|
||||
setCurrentId(detailData.id);
|
||||
setVideoYear(detailData.year);
|
||||
setVideoTitle(detailData.title || videoTitleRef.current);
|
||||
setVideoCover(detailData.poster);
|
||||
setDetail(detailData);
|
||||
if (currentEpisodeIndex >= detailData.episodes.length) {
|
||||
@@ -561,8 +566,8 @@ function PlayPageClient() {
|
||||
|
||||
// 规范URL参数
|
||||
const newUrl = new URL(window.location.href);
|
||||
newUrl.searchParams.set('source', needLoadSource);
|
||||
newUrl.searchParams.set('id', needLoadId);
|
||||
newUrl.searchParams.set('source', detailData.source);
|
||||
newUrl.searchParams.set('id', detailData.id);
|
||||
newUrl.searchParams.set('year', detailData.year);
|
||||
newUrl.searchParams.set('title', detailData.title);
|
||||
newUrl.searchParams.delete('prefer');
|
||||
|
||||
@@ -162,10 +162,30 @@ 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 () => {
|
||||
if (activeTab !== 'sources' || availableSources.length === 0) return;
|
||||
if (
|
||||
!optimizationEnabled || // 若关闭测速则直接退出
|
||||
activeTab !== 'sources' ||
|
||||
availableSources.length === 0
|
||||
)
|
||||
return;
|
||||
|
||||
// 筛选出尚未测速的播放源
|
||||
const pendingSources = availableSources.filter((source) => {
|
||||
@@ -185,7 +205,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
|
||||
fetchVideoInfosInBatches();
|
||||
// 依赖项保持与之前一致
|
||||
}, [activeTab, availableSources, getVideoInfo]);
|
||||
}, [activeTab, availableSources, getVideoInfo, optimizationEnabled]);
|
||||
|
||||
// 升序分页标签
|
||||
const categoriesAsc = useMemo(() => {
|
||||
@@ -511,11 +531,11 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
}
|
||||
|
||||
// 始终显示占位符,确保布局一致且分辨率信息始终可见
|
||||
return (
|
||||
return optimizationEnabled ? (
|
||||
<div className='bg-gray-500/10 dark:bg-gray-400/20 text-gray-500 dark:text-gray-400 px-1.5 py-0 rounded text-xs flex-shrink-0 min-w-[50px] text-center'>
|
||||
检测中
|
||||
</div>
|
||||
);
|
||||
) : null;
|
||||
})()}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export const SettingsButton: React.FC = () => {
|
||||
const [defaultAggregateSearch, setDefaultAggregateSearch] = useState(true);
|
||||
const [doubanProxyUrl, setDoubanProxyUrl] = useState('');
|
||||
const [imageProxyUrl, setImageProxyUrl] = useState('');
|
||||
const [enableOptimization, setEnableOptimization] = useState(true);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// 确保组件已挂载
|
||||
@@ -35,6 +36,12 @@ export const SettingsButton: React.FC = () => {
|
||||
if (savedImageProxyUrl !== null) {
|
||||
setImageProxyUrl(savedImageProxyUrl);
|
||||
}
|
||||
|
||||
const savedEnableOptimization =
|
||||
localStorage.getItem('enableOptimization');
|
||||
if (savedEnableOptimization !== null) {
|
||||
setEnableOptimization(JSON.parse(savedEnableOptimization));
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -60,6 +67,13 @@ export const SettingsButton: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleOptimizationToggle = (value: boolean) => {
|
||||
setEnableOptimization(value);
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('enableOptimization', JSON.stringify(value));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSettingsClick = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
@@ -119,6 +133,30 @@ export const SettingsButton: React.FC = () => {
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* 优选和测速 */}
|
||||
<div className='flex items-center justify-between'>
|
||||
<div>
|
||||
<h4 className='text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
启用优选和测速
|
||||
</h4>
|
||||
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||
如出现播放器劫持问题可关闭
|
||||
</p>
|
||||
</div>
|
||||
<label className='flex items-center cursor-pointer'>
|
||||
<div className='relative'>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='sr-only peer'
|
||||
checked={enableOptimization}
|
||||
onChange={(e) => handleOptimizationToggle(e.target.checked)}
|
||||
/>
|
||||
<div className='w-11 h-6 bg-gray-300 rounded-full peer-checked:bg-green-500 transition-colors dark:bg-gray-600'></div>
|
||||
<div className='absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full transition-transform peer-checked:translate-x-5'></div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* 豆瓣代理设置 */}
|
||||
<div className='space-y-3'>
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user