Skip to content

Commit 8e0210b

Browse files
author
Taois
committed
feat: 修复了播放器较为严重的bug
1 parent 455398f commit 8e0210b

File tree

2 files changed

+139
-15
lines changed

2 files changed

+139
-15
lines changed

dashboard/src/components/players/ArtVideoPlayer.vue

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,22 @@ const isRetrying = ref(false) // 是否正在重连
171171
const dynamicHeight = ref(450) // 动态计算的高度
172172
173173
// 自动下一集功能相关数据
174-
const autoNextEnabled = ref(true) // 自动下一集开关,默认关闭
175-
const loopEnabled = ref(JSON.parse(localStorage.getItem('loopEnabled') || 'false')) // 循环播放开关,从本地存储读取
174+
// 初始化互斥逻辑:自动连播和循环播放不能同时开启
175+
const savedLoopEnabled = JSON.parse(localStorage.getItem('loopEnabled') || 'false')
176+
const savedAutoNextEnabled = JSON.parse(localStorage.getItem('autoNextEnabled') || 'true')
177+
178+
// 确保互斥:如果循环播放开启,则关闭自动连播
179+
const autoNextEnabled = ref(savedLoopEnabled ? false : savedAutoNextEnabled)
180+
const loopEnabled = ref(savedLoopEnabled)
176181
const autoNextCountdown = ref(0) // 自动下一集倒计时
177182
const autoNextTimer = ref(null) // 自动下一集定时器
178183
const showAutoNextDialog = ref(false) // 显示自动下一集对话框
179184
const countdownEnabled = ref(false) // 倒计时开关,默认关闭
180185
186+
// 防抖标志,防止重复触发
187+
const isProcessingAutoNext = ref(false) // 防止自动下一集重复触发
188+
const isProcessingLoop = ref(false) // 防止循环播放重复触发
189+
181190
// 调试相关
182191
const showDebugDialog = ref(false)
183192
const detectedFormat = ref('')
@@ -324,6 +333,9 @@ const initArtPlayer = async (url) => {
324333
// 重置片头片尾状态
325334
resetSkipState()
326335
336+
// 重置防抖标志
337+
resetDebounceFlags()
338+
327339
// 等待 DOM 更新后计算动态高度
328340
await nextTick()
329341
dynamicHeight.value = calculateDynamicHeight()
@@ -620,6 +632,9 @@ const initArtPlayer = async (url) => {
620632
// 视频开始播放时,重置重连计数器
621633
resetRetryState()
622634
635+
// 重置防抖标志,确保新的播放周期可以正常处理自动下一集和循环播放
636+
resetDebounceFlags()
637+
623638
// 立即尝试片头跳过(针对视频刚开始播放的情况)
624639
const immediateSkipped = applyIntroSkipImmediate()
625640
@@ -665,9 +680,17 @@ const initArtPlayer = async (url) => {
665680
try {
666681
console.log('视频播放结束')
667682
683+
// 防抖检查:如果正在处理自动下一集或循环播放,则忽略
684+
if (isProcessingAutoNext.value || isProcessingLoop.value) {
685+
console.log('正在处理中,忽略重复的视频结束事件')
686+
return
687+
}
688+
668689
// 优先处理循环播放
669690
if (loopEnabled.value) {
670691
console.log('循环播放:重新播放当前选集')
692+
isProcessingLoop.value = true // 设置防抖标志
693+
671694
// 重新执行当前选集的播放逻辑
672695
setTimeout(() => {
673696
try {
@@ -676,20 +699,25 @@ const initArtPlayer = async (url) => {
676699
} catch (error) {
677700
console.error('循环播放触发选集事件失败:', error)
678701
Message.error('循环播放失败,请重试')
702+
isProcessingLoop.value = false // 出错时重置标志
679703
}
680704
}, 1000)
681705
return
682706
}
683707
684708
// 视频结束时启动自动下一集
685709
if (autoNextEnabled.value && hasNextEpisode()) {
710+
isProcessingAutoNext.value = true // 设置防抖标志
686711
startAutoNextCountdown()
687712
} else if (!hasNextEpisode()) {
688713
Message.info('全部播放完毕')
689714
}
690715
} catch (error) {
691716
console.error('视频结束事件处理失败:', error)
692717
Message.error('视频结束处理失败')
718+
// 出错时重置防抖标志
719+
isProcessingAutoNext.value = false
720+
isProcessingLoop.value = false
693721
}
694722
})
695723
@@ -908,7 +936,7 @@ const saveSkipSettings = (settings) => {
908936
}
909937
910938
// 处理重连逻辑
911-
const handleRetry = (url) => {
939+
const handleRetry = (originalUrl) => {
912940
if (isRetrying.value) {
913941
return // 如果正在重连,避免重复触发
914942
}
@@ -924,13 +952,22 @@ const handleRetry = (url) => {
924952
setTimeout(() => {
925953
if (artPlayerInstance.value) {
926954
try {
955+
// 重连时也要使用代理处理后的URL,确保代理设置生效
956+
const headers = props.headers || {}
957+
const processedUrl = processVideoUrl(originalUrl, headers)
958+
959+
console.log('重连使用URL:', processedUrl)
960+
if (processedUrl !== originalUrl) {
961+
console.log('🔄 [代理播放] 重连时使用代理地址')
962+
}
963+
927964
// 重新加载视频
928-
artPlayerInstance.value.switchUrl(url)
965+
artPlayerInstance.value.switchUrl(processedUrl)
929966
isRetrying.value = false
930967
} catch (error) {
931968
console.error('重连时出错:', error)
932969
isRetrying.value = false
933-
handleRetry(url) // 递归重试
970+
handleRetry(originalUrl) // 递归重试
934971
}
935972
}
936973
}, 2000 * retryCount.value) // 递增延迟:2秒、4秒、6秒
@@ -973,6 +1010,12 @@ const calculateDynamicHeight = () => {
9731010
return Math.round(calculatedHeight)
9741011
}
9751012
1013+
// 防抖标志重置函数
1014+
const resetDebounceFlags = () => {
1015+
isProcessingAutoNext.value = false
1016+
isProcessingLoop.value = false
1017+
}
1018+
9761019
// 自动下一集功能相关函数
9771020
9781021
// 检查是否有下一集
@@ -1021,13 +1064,18 @@ const cancelAutoNext = () => {
10211064
}
10221065
autoNextCountdown.value = 0
10231066
showAutoNextDialog.value = false
1067+
1068+
// 重置防抖标志
1069+
resetDebounceFlags()
1070+
10241071
console.log('用户取消自动下一集')
10251072
}
10261073
10271074
// 立即播放下一集
10281075
const playNextEpisode = () => {
10291076
if (!hasNextEpisode()) {
10301077
Message.info('已经是最后一集了')
1078+
resetDebounceFlags() // 重置防抖标志
10311079
return
10321080
}
10331081
@@ -1036,6 +1084,9 @@ const playNextEpisode = () => {
10361084
// 清理倒计时
10371085
cancelAutoNext()
10381086
1087+
// 重置防抖标志
1088+
resetDebounceFlags()
1089+
10391090
// 通知父组件切换到下一集
10401091
emit('next-episode', props.currentEpisodeIndex + 1)
10411092
@@ -1046,6 +1097,8 @@ const playNextEpisode = () => {
10461097
// 切换自动下一集开关
10471098
const toggleAutoNext = () => {
10481099
autoNextEnabled.value = !autoNextEnabled.value
1100+
// 保存自动连播状态到本地存储
1101+
localStorage.setItem('autoNextEnabled', JSON.stringify(autoNextEnabled.value))
10491102
10501103
// 如果开启自动连播,则关闭循环播放
10511104
if (autoNextEnabled.value) {
@@ -1068,6 +1121,7 @@ const toggleLoop = () => {
10681121
// 如果开启循环播放,则关闭自动连播
10691122
if (loopEnabled.value) {
10701123
autoNextEnabled.value = false
1124+
localStorage.setItem('autoNextEnabled', 'false')
10711125
cancelAutoNext()
10721126
}
10731127
@@ -1498,11 +1552,23 @@ const handleResize = () => {
14981552
}
14991553
}
15001554
1555+
// 处理代理设置变化
1556+
const handleAddressSettingsChange = () => {
1557+
console.log('检测到代理设置变化,重新初始化播放器')
1558+
if (props.videoUrl && props.visible) {
1559+
nextTick(() => {
1560+
initArtPlayer(props.videoUrl)
1561+
})
1562+
}
1563+
}
1564+
15011565
// 组件挂载时的初始化
15021566
onMounted(() => {
15031567
console.log('ArtVideoPlayer 组件已挂载 - 动态高度版本')
15041568
// 添加窗口大小变化监听
15051569
window.addEventListener('resize', handleResize)
1570+
// 添加代理设置变化监听
1571+
window.addEventListener('addressSettingsChanged', handleAddressSettingsChange)
15061572
// 初始化片头片尾设置
15071573
initSkipSettings()
15081574
// 初始化画质数据
@@ -1515,6 +1581,8 @@ onUnmounted(() => {
15151581
15161582
// 移除窗口大小变化监听器
15171583
window.removeEventListener('resize', handleResize)
1584+
// 移除代理设置变化监听器
1585+
window.removeEventListener('addressSettingsChanged', handleAddressSettingsChange)
15181586
15191587
// 清理自动下一集相关资源
15201588
cancelAutoNext()

dashboard/src/components/players/VideoPlayer.vue

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,20 @@ const emit = defineEmits(['close', 'error', 'player-change', 'next-episode', 'ep
183183
// 响应式数据
184184
const videoPlayer = ref(null)
185185
const mediaPlayerManager = ref(null)
186-
const autoNext = ref(true) // 默认开启自动连播
187-
const loopEnabled = ref(JSON.parse(localStorage.getItem('loopEnabled') || 'false')) // 循环播放开关,从本地存储读取
186+
187+
// 初始化互斥逻辑:自动连播和循环播放不能同时开启
188+
const savedLoopEnabled = JSON.parse(localStorage.getItem('loopEnabled') || 'false')
189+
const savedAutoNextEnabled = JSON.parse(localStorage.getItem('autoNextEnabled') || 'true')
190+
191+
// 确保互斥:如果循环播放开启,则关闭自动连播
192+
const autoNext = ref(savedLoopEnabled ? false : savedAutoNextEnabled)
193+
const loopEnabled = ref(savedLoopEnabled)
188194
const showCountdown = ref(false)
189195
const showAutoNextDialog = ref(false)
190196
const autoNextCountdown = ref(10)
191197
const countdownTimer = ref(null)
192198
const isProcessingAutoNext = ref(false) // 防止重复触发自动连播
199+
const isProcessingLoop = ref(false) // 防止重复触发循环播放
193200
const currentSpeed = ref(1) // 当前播放倍速
194201
195202
// 调试相关
@@ -273,12 +280,19 @@ const switchVideoSource = (newUrl, seekTime = 0, autoPlay = false) => {
273280
})
274281
275282
try {
283+
// 处理代理播放地址
284+
const headers = props.headers || {}
285+
const finalUrl = processVideoUrl(newUrl, headers)
286+
if (finalUrl !== newUrl) {
287+
console.log('🔄 [代理播放] 切换视频源使用代理地址')
288+
}
289+
276290
// 使用MediaPlayerManager切换视频
277291
if (mediaPlayerManager.value) {
278-
mediaPlayerManager.value.switchVideo(newUrl)
292+
mediaPlayerManager.value.switchVideo(finalUrl)
279293
} else {
280-
// 原生播放器直接切换
281-
videoPlayer.value.src = newUrl
294+
// 原生播放器使用代理处理后的地址
295+
videoPlayer.value.src = finalUrl
282296
}
283297
284298
// 等待视频加载后跳转到指定时间
@@ -343,6 +357,13 @@ const hideAutoNextDialog = () => {
343357
}
344358
}
345359
360+
// 重置所有防抖标志
361+
const resetDebounceFlags = () => {
362+
isProcessingAutoNext.value = false
363+
isProcessingLoop.value = false
364+
console.log('所有防抖标志已重置')
365+
}
366+
346367
// 播放下一集
347368
const playNextEpisode = () => {
348369
if (hasNextEpisode()) {
@@ -351,7 +372,7 @@ const playNextEpisode = () => {
351372
hideAutoNextDialog()
352373
// 重置防抖标志
353374
setTimeout(() => {
354-
isProcessingAutoNext.value = false
375+
resetDebounceFlags()
355376
}, 2000) // 2秒后重置,给视频切换足够的时间
356377
}
357378
}
@@ -389,6 +410,9 @@ const {
389410
// 切换自动连播
390411
const toggleAutoNext = () => {
391412
autoNext.value = !autoNext.value
413+
// 保存自动连播状态到本地存储
414+
localStorage.setItem('autoNextEnabled', JSON.stringify(autoNext.value))
415+
392416
// 如果开启自动连播,则关闭循环播放
393417
if (autoNext.value) {
394418
loopEnabled.value = false
@@ -406,6 +430,7 @@ const toggleLoop = () => {
406430
// 如果开启循环播放,则关闭自动连播
407431
if (loopEnabled.value) {
408432
autoNext.value = false
433+
localStorage.setItem('autoNextEnabled', 'false')
409434
}
410435
411436
console.log('循环播放开关:', loopEnabled.value ? '开启' : '关闭')
@@ -508,6 +533,9 @@ const initVideoPlayer = (url) => {
508533
// 重置片头片尾跳过状态
509534
resetSkipState()
510535
536+
// 重置所有防抖标志
537+
resetDebounceFlags()
538+
511539
// 首先判断链接类型
512540
if (!isDirectVideoLink(url)) {
513541
Message.info('检测到网页链接,正在新窗口打开...')
@@ -539,22 +567,33 @@ const initVideoPlayer = (url) => {
539567
// 视频结束事件处理函数
540568
const handleVideoEnded = () => {
541569
try {
542-
// 防抖:如果正在处理自动连播,则忽略
543-
if (isProcessingAutoNext.value) {
570+
// 防抖:如果正在处理自动连播或循环播放,则忽略
571+
if (isProcessingAutoNext.value || isProcessingLoop.value) {
572+
console.log('防抖:忽略重复的视频结束事件')
544573
return
545574
}
546575
547576
// 优先处理循环播放
548577
if (loopEnabled.value) {
549578
console.log('循环播放:重新播放当前选集')
579+
isProcessingLoop.value = true // 设置循环播放防抖标志
580+
550581
// 重新执行当前选集的播放逻辑
551582
setTimeout(() => {
552583
try {
553584
// 触发重新选择当前选集,这会重新获取播放链接
554585
emit('episode-selected', props.currentEpisodeIndex)
586+
587+
// 延迟重置防抖标志,给视频切换足够的时间
588+
setTimeout(() => {
589+
isProcessingLoop.value = false
590+
console.log('循环播放防抖标志已重置')
591+
}, 3000) // 3秒后重置,确保视频加载完成
592+
555593
} catch (error) {
556594
console.error('循环播放触发选集事件失败:', error)
557595
Message.error('循环播放失败,请重试')
596+
isProcessingLoop.value = false // 出错时立即重置
558597
}
559598
}, 1000)
560599
return
@@ -575,6 +614,8 @@ const initVideoPlayer = (url) => {
575614
} catch (error) {
576615
console.error('视频结束事件处理失败:', error)
577616
Message.error('视频结束处理失败')
617+
// 出错时重置所有防抖标志
618+
resetDebounceFlags()
578619
}
579620
}
580621
@@ -688,8 +729,8 @@ const initVideoPlayer = (url) => {
688729
video.addEventListener('seeking', handleSeeking)
689730
video.addEventListener('seeked', handleSeeked)
690731
} else {
691-
// 原生视频播放 - 直接设置src
692-
video.src = url
732+
// 原生视频播放 - 使用代理处理后的地址
733+
video.src = finalUrl
693734
}
694735
695736
} catch (error) {
@@ -822,16 +863,31 @@ watch(() => props.initialQuality, (newQuality) => {
822863
}
823864
}, { immediate: true })
824865
866+
// 处理代理设置变化
867+
const handleAddressSettingsChange = () => {
868+
console.log('检测到代理设置变化,重新初始化播放器')
869+
if (props.videoUrl && props.visible) {
870+
nextTick(() => {
871+
initVideoPlayer(props.videoUrl)
872+
})
873+
}
874+
}
875+
825876
// 组件挂载时初始化
826877
onMounted(() => {
827878
initSkipSettings()
828879
initQualityData()
880+
// 添加代理设置变化监听
881+
window.addEventListener('addressSettingsChanged', handleAddressSettingsChange)
829882
})
830883
831884
// 组件卸载时清理资源
832885
onUnmounted(() => {
833886
console.log('VideoPlayer组件卸载,清理播放器资源')
834887
888+
// 移除代理设置变化监听器
889+
window.removeEventListener('addressSettingsChanged', handleAddressSettingsChange)
890+
835891
// 清理视频播放器
836892
if (videoPlayer.value) {
837893
videoPlayer.value.pause()

0 commit comments

Comments
 (0)