From 604c5f9374ec89edf38d96e1f6cf83cef0fcdc06 Mon Sep 17 00:00:00 2001 From: mtvpls <247332661+mtvpls@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:06:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BF=AB=E6=8D=B7=E5=BF=AB?= =?UTF-8?q?=E8=BF=9B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/play/page.tsx | 144 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index dfd7213..b1ef832 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -165,6 +165,7 @@ const PLAY_SHORTCUT_GROUPS = [ items: [ { keys: ['空格'], description: '播放 / 暂停' }, { keys: ['←', '→'], description: '快退 / 快进 10 秒' }, + { keys: ['P'], description: '快捷快进' }, { keys: ['↑', '↓'], description: '音量增加 / 减少' }, { keys: ['F'], description: '切换全屏' }, ], @@ -377,6 +378,18 @@ function PlayPageClient() { skipConfig.outro_time, ]); + // 快捷快进设置(默认 1 分 30 秒) + const DEFAULT_QUICK_FORWARD_SECONDS = 90; + const [quickForwardSeconds, setQuickForwardSeconds] = useState(() => { + if (typeof window === 'undefined') return DEFAULT_QUICK_FORWARD_SECONDS; + const saved = Number(localStorage.getItem('quickForwardSeconds')); + return Number.isFinite(saved) && saved > 0 ? saved : DEFAULT_QUICK_FORWARD_SECONDS; + }); + const quickForwardSecondsRef = useRef(quickForwardSeconds); + useEffect(() => { + quickForwardSecondsRef.current = quickForwardSeconds; + }, [quickForwardSeconds]); + // 跳过检查的时间间隔控制 const lastSkipCheckRef = useRef(0); @@ -1768,6 +1781,26 @@ function PlayPageClient() { return true; }; + const formatQuickForwardDuration = (seconds: number) => { + if (seconds >= 60) { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return remainingSeconds ? `${minutes}分${remainingSeconds}秒` : `${minutes}分钟`; + } + return `${seconds}秒`; + }; + + const seekQuickForward = () => { + const player = artPlayerRef.current; + if (!player) return false; + + const duration = Number.isFinite(player.duration) ? player.duration : Infinity; + const nextTime = Math.min(duration, (player.currentTime || 0) + quickForwardSecondsRef.current); + player.currentTime = nextTime; + player.notice.show = `快进 ${formatQuickForwardDuration(quickForwardSecondsRef.current)}`; + return true; + }; + const isDanmakuAutoLoadDisabled = () => { if (typeof window === 'undefined') { return false; @@ -6315,6 +6348,13 @@ function PlayPageClient() { } } + // P = 使用当前配置快捷快进 + if (!e.altKey && e.key.toLowerCase() === 'p') { + if (seekQuickForward()) { + e.preventDefault(); + } + } + // 左箭头 = 快退 if (!e.altKey && e.key === 'ArrowLeft') { if (artPlayerRef.current && artPlayerRef.current.currentTime > 5) { @@ -7394,6 +7434,86 @@ function PlayPageClient() { return !item.switch; }, }, + { + name: '快捷快进', + html: '快捷快进', + icon: '', + tooltip: `当前:${formatQuickForwardDuration(quickForwardSecondsRef.current)},点击修改`, + onClick: async function () { + const player = artPlayerRef.current; + if (player?.fullscreen) { + player.fullscreen = false; + await new Promise(resolve => setTimeout(resolve, 300)); + } + + const existingDialog = document.querySelector('.quick-forward-settings-dialog'); + existingDialog?.remove(); + + const container = document.createElement('div'); + container.className = 'quick-forward-settings-dialog'; + container.style.cssText = ` + position: fixed; + inset: 0; + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(3px); + `; + container.innerHTML = ` +
+
快捷快进设置
+
设置点击底部按钮或按 P 键时向前跳转的时间。
+ + +
+ + +
+
+ `; + document.body.appendChild(container); + + const input = container.querySelector('#quick-forward-input') as HTMLInputElement; + const cancelButton = container.querySelector('[data-action="cancel"]'); + const confirmButton = container.querySelector('[data-action="confirm"]'); + const cleanup = () => container.remove(); + const save = () => { + const nextSeconds = Number(input.value); + if (!Number.isFinite(nextSeconds) || nextSeconds <= 0) { + input.focus(); + if (artPlayerRef.current) { + artPlayerRef.current.notice.show = '请输入大于 0 的有效秒数'; + } + return; + } + + const normalizedSeconds = Math.max(1, Math.round(nextSeconds)); + setQuickForwardSeconds(normalizedSeconds); + quickForwardSecondsRef.current = normalizedSeconds; + localStorage.setItem('quickForwardSeconds', String(normalizedSeconds)); + if (artPlayerRef.current) { + artPlayerRef.current.notice.show = `快捷快进:${formatQuickForwardDuration(normalizedSeconds)}`; + } + cleanup(); + }; + + cancelButton?.addEventListener('click', cleanup); + confirmButton?.addEventListener('click', save); + container.addEventListener('click', (event) => { + if (event.target === container) cleanup(); + }); + input.addEventListener('keydown', (event) => { + if (event.key === 'Enter') save(); + if (event.key === 'Escape') cleanup(); + }); + input.focus(); + input.select(); + return '打开设置'; + }, + }, { name: '跳过配置', html: '跳过配置', @@ -7562,6 +7682,30 @@ function PlayPageClient() { ], // 控制栏配置 controls: [ + { + position: 'left', + index: 40, + html: ``, + tooltip: '快捷快进', + mounted: ($el: HTMLElement) => { + $el.classList.add('quick-forward-control-wrapper'); + if (!document.getElementById('quick-forward-control-style')) { + const style = document.createElement('style'); + style.id = 'quick-forward-control-style'; + style.textContent = ` + @media (max-width: 767px) and (orientation: portrait) { + .quick-forward-control-wrapper { + display: none !important; + } + } + `; + document.head.appendChild(style); + } + }, + click: function () { + seekQuickForward(); + }, + }, { position: 'left', index: 13,