diff --git a/src/components/UserMenu.tsx b/src/components/UserMenu.tsx index c4e07a1..7ffdfa5 100644 --- a/src/components/UserMenu.tsx +++ b/src/components/UserMenu.tsx @@ -130,6 +130,7 @@ export const UserMenu: React.FC = () => { const [searchTraditionalToSimplified, setSearchTraditionalToSimplified] = useState(false); const [exactSearch, setExactSearch] = useState(true); const [maxConcurrentDownloads, setMaxConcurrentDownloads] = useState(6); + const [downloadThreadsPerTask, setDownloadThreadsPerTask] = useState(6); // 邮件通知设置 const [userEmail, setUserEmail] = useState(''); @@ -161,6 +162,7 @@ export const UserMenu: React.FC = () => { // TMDB 图片设置 const [tmdbImageBaseUrl, setTmdbImageBaseUrl] = useState('https://image.tmdb.org'); const [isUsageSectionOpen, setIsUsageSectionOpen] = useState(false); + const [isDownloadSectionOpen, setIsDownloadSectionOpen] = useState(false); const [isBufferSectionOpen, setIsBufferSectionOpen] = useState(false); const [isDanmakuSectionOpen, setIsDanmakuSectionOpen] = useState(false); const [isHomepageSectionOpen, setIsHomepageSectionOpen] = useState(false); @@ -562,6 +564,12 @@ export const UserMenu: React.FC = () => { if (savedMaxConcurrentDownloads !== null) { setMaxConcurrentDownloads(Number(savedMaxConcurrentDownloads)); } + + // 加载单任务线程数设置 + const savedDownloadThreadsPerTask = localStorage.getItem('downloadThreadsPerTask'); + if (savedDownloadThreadsPerTask !== null) { + setDownloadThreadsPerTask(Number(savedDownloadThreadsPerTask)); + } } }, []); @@ -917,6 +925,13 @@ export const UserMenu: React.FC = () => { } }; + const handleDownloadThreadsPerTaskChange = (value: number) => { + setDownloadThreadsPerTask(value); + if (typeof window !== 'undefined') { + localStorage.setItem('downloadThreadsPerTask', String(value)); + } + }; + const handleFluidSearchToggle = (value: boolean) => { setFluidSearch(value); if (typeof window !== 'undefined') { @@ -2034,10 +2049,30 @@ export const UserMenu: React.FC = () => { + + )} + - {/* 分割线 */} -
- + {/* 下载设置 */} +
+ + {isDownloadSectionOpen && ( +
{/* 最大同时下载限制 */}
@@ -2097,6 +2132,72 @@ export const UserMenu: React.FC = () => {
+ + {/* 单任务线程数 */} +
+
+

+ 单任务线程数 +

+

+ 控制每个下载任务使用的线程数量,线程越多下载越快但占用资源越多 +

+
+
+ + 线程数量 + + + {downloadThreadsPerTask}个 + +
+
+ handleDownloadThreadsPerTaskChange(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 ${((downloadThreadsPerTask - 1) / (32 - 1)) * 100}%, #e5e7eb ${((downloadThreadsPerTask - 1) / (32 - 1)) * 100}%, #e5e7eb 100%)` + }} + /> +
+
+ + + + + +
+
)}
diff --git a/src/lib/m3u8-downloader.ts b/src/lib/m3u8-downloader.ts index 3e792aa..a3fc4d8 100644 --- a/src/lib/m3u8-downloader.ts +++ b/src/lib/m3u8-downloader.ts @@ -347,8 +347,13 @@ export class M3U8Downloader { } }; - // 并发下载 6 个片段 - const concurrency = Math.min(6, task.rangeDownload.targetSegment - task.finishNum); + // 从localStorage读取单任务线程数设置,默认6个 + const threadsPerTask = typeof window !== 'undefined' + ? Number(localStorage.getItem('downloadThreadsPerTask') || 6) + : 6; + + // 并发下载片段 + const concurrency = Math.min(threadsPerTask, task.rangeDownload.targetSegment - task.finishNum); for (let i = 0; i < concurrency; i++) { download(); }