更新:课程视频播放改成原生video组件
This commit is contained in:
189
components/video-player/composables/useVideoPlayer.ts
Normal file
189
components/video-player/composables/useVideoPlayer.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
// components/video-player/composables/useVideoPlayer.ts
|
||||
import { ref, computed } from 'vue'
|
||||
import { useVideoAPI } from './useVideoAPI'
|
||||
import { useVideoProgress } from './useVideoProgress'
|
||||
import type { IVideoInfo, IVideoPlayerProps, VideoErrorType } from '@/types/video'
|
||||
|
||||
/**
|
||||
* 视频播放器核心逻辑
|
||||
*/
|
||||
export function useVideoPlayer(props: IVideoPlayerProps) {
|
||||
const videoAPI = useVideoAPI()
|
||||
const videoProgress = useVideoProgress()
|
||||
|
||||
// 状态
|
||||
const currentVideoData = ref<IVideoInfo | null>(null)
|
||||
const currentIndex = ref(props.currentIndex)
|
||||
const isSetFirstTime = ref(false)
|
||||
const isChanging = ref(false)
|
||||
const showError = ref(false)
|
||||
const errorMessage = ref('')
|
||||
const canRetry = ref(false)
|
||||
const platform = ref('')
|
||||
|
||||
// 获取平台信息
|
||||
// #ifdef APP-PLUS
|
||||
platform.value = uni.getSystemInfoSync().platform
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
platform.value = 'h5'
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 计算视频 URL
|
||||
*/
|
||||
const videoUrl = computed(() => {
|
||||
if (!currentVideoData.value) return ''
|
||||
|
||||
if (currentVideoData.value.type === 0) {
|
||||
// MP4 视频
|
||||
return currentVideoData.value.mp4Url || currentVideoData.value.videoUrl || ''
|
||||
} else {
|
||||
// M3U8 视频
|
||||
return currentVideoData.value.m3u8Url || ''
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算封面图 URL
|
||||
*/
|
||||
const posterUrl = computed(() => {
|
||||
if (!currentVideoData.value) return ''
|
||||
|
||||
// 如果是 MP4 视频,可以使用 OSS 视频截图功能
|
||||
if (currentVideoData.value.type === 0 && currentVideoData.value.mp4Url) {
|
||||
return `${currentVideoData.value.mp4Url}?x-oss-process=video/snapshot,t_1,f_jpg`
|
||||
}
|
||||
|
||||
return ''
|
||||
})
|
||||
|
||||
/**
|
||||
* 检查视频是否可以播放
|
||||
*/
|
||||
const canPlayVideo = (videoInfo: IVideoInfo): boolean => {
|
||||
// iOS 平台检查
|
||||
if (platform.value === 'ios') {
|
||||
if (videoInfo.type === 1 && !videoInfo.m3u8Url) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载视频
|
||||
*/
|
||||
const loadVideo = async (index: number) => {
|
||||
if (index < 0 || index >= props.videoList.length) {
|
||||
showError.value = true
|
||||
errorMessage.value = '视频索引超出范围'
|
||||
canRetry.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const videoItem = props.videoList[index]
|
||||
currentIndex.value = index
|
||||
|
||||
// 重置状态
|
||||
isSetFirstTime.value = false
|
||||
showError.value = false
|
||||
errorMessage.value = ''
|
||||
canRetry.value = false
|
||||
|
||||
// 调用 API 获取视频信息
|
||||
const videoInfo = await videoAPI.fetchVideoInfo({
|
||||
id: videoItem.id
|
||||
})
|
||||
|
||||
if (!videoInfo) {
|
||||
showError.value = true
|
||||
errorMessage.value = videoAPI.error.value?.message || '获取视频信息失败'
|
||||
canRetry.value = true
|
||||
return
|
||||
}
|
||||
|
||||
// 检查视频类型和 URL
|
||||
if (videoInfo.type === 1 && !videoInfo.m3u8Url) {
|
||||
// M3U8 视频但没有 URL,报告错误
|
||||
await videoAPI.reportErrorVideo({
|
||||
chapterId: videoInfo.chapterId,
|
||||
videoId: videoInfo.id,
|
||||
sort: videoInfo.sort
|
||||
})
|
||||
|
||||
showError.value = true
|
||||
errorMessage.value = '抱歉,本视频加密信息错误,已经提交错误信息,请过一段时间再来观看或联系客服'
|
||||
canRetry.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// 设置当前视频数据
|
||||
currentVideoData.value = videoInfo
|
||||
|
||||
// 获取初始播放位置
|
||||
const initialPosition = videoProgress.getInitialPosition(videoInfo)
|
||||
videoProgress.updateCurrentTime(initialPosition)
|
||||
|
||||
return videoInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换视频
|
||||
*/
|
||||
const changeVideo = async (newIndex: number) => {
|
||||
if (isChanging.value) return
|
||||
|
||||
isChanging.value = true
|
||||
|
||||
try {
|
||||
// 保存当前视频进度
|
||||
if (currentVideoData.value) {
|
||||
await videoProgress.saveNow(currentVideoData.value)
|
||||
}
|
||||
|
||||
// 停止进度保存
|
||||
videoProgress.stopSaving()
|
||||
|
||||
// 加载新视频
|
||||
await loadVideo(newIndex)
|
||||
} finally {
|
||||
isChanging.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试加载视频
|
||||
*/
|
||||
const retry = () => {
|
||||
loadVideo(currentIndex.value)
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
currentVideoData,
|
||||
currentIndex,
|
||||
isSetFirstTime,
|
||||
isChanging,
|
||||
showError,
|
||||
errorMessage,
|
||||
canRetry,
|
||||
platform,
|
||||
isLoading: videoAPI.isLoading,
|
||||
|
||||
// 计算属性
|
||||
videoUrl,
|
||||
posterUrl,
|
||||
|
||||
// 方法
|
||||
loadVideo,
|
||||
changeVideo,
|
||||
retry,
|
||||
canPlayVideo,
|
||||
|
||||
// 进度管理
|
||||
videoProgress
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user