提交 e22f07ca authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 e0e05750
...@@ -3,8 +3,8 @@ const DEFAULT_OPTIONS = { ...@@ -3,8 +3,8 @@ const DEFAULT_OPTIONS = {
controls: true, controls: true,
autoplay: false, autoplay: false,
// fluid: true, // fluid: true,
playbackRates: [0.5, 1, 1.5, 2], responsive: true,
restoreEl: true playbackRates: [0.5, 1, 1.5, 2]
} }
const DEFAULT_EVENTS = [ const DEFAULT_EVENTS = [
'abort', 'abort',
...@@ -101,3 +101,9 @@ onUnmounted(() => { ...@@ -101,3 +101,9 @@ onUnmounted(() => {
<template> <template>
<video class="video-js vjs-default-skin vjs-big-play-centered" ref="videoRef"></video> <video class="video-js vjs-default-skin vjs-big-play-centered" ref="videoRef"></video>
</template> </template>
<style>
.video-js {
font-size: 12px;
}
</style>
...@@ -30,6 +30,39 @@ watchEffect(() => { ...@@ -30,6 +30,39 @@ watchEffect(() => {
semesterId = route.query.semester_id as string semesterId = route.query.semester_id as string
}) })
const skipTime = $ref<number>(6)
/**
* 视频播放器相关
*/
let src = $ref<{ src: string; type: string }>()
// 跳过片头
const isSkip = $ref(useStorage('isSkip', false))
// 连续播放
const isAutoPlayNext = $ref(useStorage('isAutoPlayNext', false))
// 播放器ready
let isReady = $ref<boolean>(false)
let videoJsPlayer = $ref<VideoJsPlayer | null>()
const onReady = (player: VideoJsPlayer) => {
isReady = true
videoJsPlayer = player
}
function changeSrc(data: PlayItemType) {
// src = { src: data.PlayURL, type: 'application/x-mpegURL' }
src = { src: data.PlayURL, type: 'video/mp4' }
}
// 切换视频清晰度
function changeDefinition(data: PlayItemType) {
changeSrc(data)
}
// 切换视频资源
function changeResource(data: CourseResourceType) {
throttledFn && throttledFn.flush()
resourceId = data.resource_id
videoJsPlayer && videoJsPlayer.pause()
}
// 当前章 // 当前章
const chapter = $computed(() => { const chapter = $computed(() => {
return props.chapterList.find(item => item.id === chapterId) return props.chapterList.find(item => item.id === chapterId)
...@@ -44,15 +77,15 @@ const resource = $computed(() => { ...@@ -44,15 +77,15 @@ const resource = $computed(() => {
return section?.resources.find(item => item.resource_id === resourceId && item.resource_type === 2) return section?.resources.find(item => item.resource_id === resourceId && item.resource_type === 2)
}) })
// 资源视频列表 // 资源视频列表
const videoList = $computed<CourseResourceType[]>(() => { const resourceVideoList = $computed<CourseResourceType[]>(() => {
const list = section?.resources ?? [] const list = section?.resources ?? []
return list.filter(item => item.resource_type === 2) return list.filter(item => item.resource_type === 2)
}) })
watchEffect(() => { watchEffect(() => {
if (videoList.length) { if (resourceVideoList.length) {
const found = videoList.find(item => item.resource_id === resourceId) const found = resourceVideoList.find(item => item.resource_id === resourceId)
resourceId = found ? resourceId : videoList[0]?.resource_id resourceId = found ? resourceId : resourceVideoList[0]?.resource_id
} }
}) })
...@@ -66,7 +99,13 @@ const progress = reactive<VideoRecordType>({ ...@@ -66,7 +99,13 @@ const progress = reactive<VideoRecordType>({
let playList = $ref<PlayItemType[]>([]) let playList = $ref<PlayItemType[]>([])
const currentPlayList = $computed<PlayItemType[]>(() => { const currentPlayList = $computed<PlayItemType[]>(() => {
return playList.filter(item => item.StreamType === 'video' && item.Format === 'mp4') return playList
.filter(item => item.StreamType === 'video' && item.Format === 'mp4')
.map(item => {
const DefinitionMap: Record<string, string> = { FD: '流畅', LD: '标清', SD: '高清', HD: '超清' }
const DefinitionName = DefinitionMap[item.Definition] || item.Definition
return { ...item, DefinitionName }
})
}) })
// 获取视频信息 // 获取视频信息
...@@ -90,19 +129,36 @@ async function fetchVideoRecords() { ...@@ -90,19 +129,36 @@ async function fetchVideoRecords() {
progress.max_playing_time = detail.max_playing_time ? parseFloat(detail.max_playing_time) : 0 progress.max_playing_time = detail.max_playing_time ? parseFloat(detail.max_playing_time) : 0
progress.valid_playing_time = detail.valid_playing_time ? parseFloat(detail.valid_playing_time) : 0 progress.valid_playing_time = detail.valid_playing_time ? parseFloat(detail.valid_playing_time) : 0
progress.watchedTimePoint = [] progress.watchedTimePoint = []
console.log('info', progress.current_playing_time)
} }
watchEffect(async () => { watchEffect(async () => {
if (!resource) return if (!resource) return
await fetchVideoRecords() await fetchVideoRecords()
await fetchInfo() await fetchInfo()
console.log('watch', progress.current_playing_time)
changeSrc(currentPlayList[0]) changeSrc(currentPlayList[0])
nextTick(() => {
if (videoJsPlayer && progress.current_playing_time) {
videoJsPlayer.currentTime(progress.current_playing_time)
console.log('seek', progress.current_playing_time)
}
})
}) })
function setVideoInfo() {
if (!videoJsPlayer) return
// 统计,增加默认时间10秒
progress.valid_playing_time = progress.valid_playing_time || 10
// 跳过片头
if (isSkip) {
// 跳过片头
if (progress.current_playing_time < skipTime) {
progress.current_playing_time = skipTime
}
// 有效时间增加跳过片头的时间
if (!progress.valid_playing_time) {
progress.valid_playing_time = skipTime + 10
}
}
// 初始化跳转到上次观看的时间点
videoJsPlayer.currentTime(progress.current_playing_time)
console.log('seek', progress.current_playing_time)
videoJsPlayer.play()
}
const throttledFn = throttle( const throttledFn = throttle(
() => { () => {
uploadVideoRecords({ uploadVideoRecords({
...@@ -122,20 +178,19 @@ const throttledFn = throttle( ...@@ -122,20 +178,19 @@ const throttledFn = throttle(
5000 5000
// { leading: false } // { leading: false }
) )
// 视频加载完成
function onLoadedData() {
console.log('onLoadedData')
setVideoInfo()
}
// 进度更新
function onTimeUpdate() { function onTimeUpdate() {
if (!videoJsPlayer) return const time = Math.floor(videoJsPlayer?.currentTime() ?? 0)
const time = Math.floor(videoJsPlayer.currentTime() ?? 0) if (!time || videoJsPlayer?.paused()) return
if (!time) return
console.log('onTimeUpdate', time) console.log('onTimeUpdate', time)
// 更新观看累计时长 // 更新观看累计时长
if (time !== progress.current_playing_time) { if (time !== progress.current_playing_time) {
// // 增加跳过片头时间
// if (this.isSkip && !this.progress.pt) {
// this.progress.pt = this.skipTime + 20
// }
// 默认增加时间
progress.valid_playing_time = progress.valid_playing_time || 20
progress.valid_playing_time++ progress.valid_playing_time++
} }
// 更新当前播放时间 // 更新当前播放时间
...@@ -148,31 +203,15 @@ function onTimeUpdate() { ...@@ -148,31 +203,15 @@ function onTimeUpdate() {
throttledFn() throttledFn()
} }
// 播放结束
/** function onEnded() {
* 视频播放器相关 console.log('onEnd')
*/ // 自动播放下一个视频
let src = $ref<{ src: string; type: string }>() if (isAutoPlayNext) {
// 跳过片头 const currentIndex = resourceVideoList.findIndex(item => item.resource_id === resourceId)
const isSkip = useStorage('isSkip', false) const next = resourceVideoList[currentIndex + 1]
// 连续播放 next && changeResource(next)
const isAutoPlayNext = useStorage('isAutoPlayNext', false) }
// 播放器ready
let isReady = $ref<boolean>(false)
let videoJsPlayer = $ref<VideoJsPlayer | null>()
const onReady = (player: VideoJsPlayer) => {
isReady = true
videoJsPlayer = player
videoJsPlayer.currentTime(progress.current_playing_time)
console.log('seek', progress.current_playing_time)
}
function changeSrc(data: PlayItemType) {
// src = { src: data.PlayURL, type: 'application/x-mpegURL' }
src = { src: data.PlayURL, type: 'video/mp4' }
}
function changeResource(data: CourseResourceType) {
throttledFn && throttledFn.flush()
resourceId = data.resource_id
} }
</script> </script>
...@@ -182,36 +221,46 @@ function changeResource(data: CourseResourceType) { ...@@ -182,36 +221,46 @@ function changeResource(data: CourseResourceType) {
:src="src" :src="src"
@ready="onReady" @ready="onReady"
@timeupdate="onTimeUpdate" @timeupdate="onTimeUpdate"
style="width: 100%; height: 510px" @ended="onEnded"
@loadeddata="onLoadedData"
height="510"
style="width: 100%"
v-if="src" v-if="src"
></AppVideoPlayer> ></AppVideoPlayer>
<!-- 设置 --> <!-- 设置 -->
<teleport to=".vjs-control-bar" v-if="isReady"> <teleport to=".vjs-control-bar" v-if="isReady">
<el-popover trigger="hover" effect="dark" placement="top" :teleported="false"> <el-popover trigger="hover" effect="dark" placement="top" :teleported="false" width="40px">
<template #reference> <template #reference>
<button class="vjs-hd-control vjs-control vjs-button" type="button"> <button class="vjs-hd-control vjs-control vjs-button" type="button">
<span class="vjs-icon-hd"></span> <span class="vjs-icon-hd"></span>
</button> </button>
</template> </template>
<ul> <ul class="video-definition">
<li v-for="(item, index) in currentPlayList" :key="index" @click="changeSrc(item)">{{ item.Definition }}</li> <li
v-for="(item, index) in currentPlayList"
:key="index"
:class="{ 'is-active': item.PlayURL === src.src }"
@click="changeDefinition(item)"
>
{{ item.DefinitionName }}
</li>
</ul> </ul>
</el-popover> </el-popover>
<el-popover trigger="hover" effect="dark" placement="top" :teleported="false"> <el-popover trigger="hover" effect="dark" placement="top" :teleported="false" width="140px">
<template #reference> <template #reference>
<button class="vjs-cog-control vjs-control vjs-button" type="button"> <button class="vjs-cog-control vjs-control vjs-button" type="button">
<span class="vjs-icon-cog"></span> <span class="vjs-icon-cog"></span>
</button> </button>
</template> </template>
<ul> <ul class="video-actions">
<li>始终跳过片头 <el-switch v-model="isSkip"></el-switch></li> <li>始终跳过片头 <el-switch size="small" v-model="isSkip"></el-switch></li>
<li>连续播放 <el-switch v-model="isAutoPlayNext"></el-switch></li> <li>连续播放 <el-switch size="small" v-model="isAutoPlayNext"></el-switch></li>
</ul> </ul>
</el-popover> </el-popover>
</teleport> </teleport>
<swiper :slidesPerView="'auto'" :spaceBetween="30"> <swiper :slidesPerView="'auto'" :spaceBetween="30">
<swiper-slide <swiper-slide
v-for="item in videoList" v-for="item in resourceVideoList"
:key="item.id" :key="item.id"
class="video-item" class="video-item"
:class="{ 'is-active': item.resource_id === resourceId }" :class="{ 'is-active': item.resource_id === resourceId }"
...@@ -261,4 +310,41 @@ function changeResource(data: CourseResourceType) { ...@@ -261,4 +310,41 @@ function changeResource(data: CourseResourceType) {
border: 2px solid var(--main-color); border: 2px solid var(--main-color);
} }
} }
.video-definition {
font-size: 12px;
li {
display: flex;
align-items: center;
justify-content: center;
padding: 0.2em 0;
font-size: 1.2em;
line-height: 1.4em;
&.is-active {
color: #2b333f;
background-color: #fff;
}
}
}
.video-actions {
font-size: 12px;
li {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.2em 5px;
font-size: 1.2em;
line-height: 1.4em;
}
}
:deep(.el-popover) {
min-width: auto;
background-color: rgba(43, 51, 63, 0.7);
border: 0;
border-radius: 0;
padding: 0;
margin-bottom: -12px !important;
.el-popper__arrow {
display: none;
}
}
</style> </style>
...@@ -82,4 +82,5 @@ export interface PlayItemType { ...@@ -82,4 +82,5 @@ export interface PlayItemType {
Status: string Status: string
StreamType: string StreamType: string
Width: number Width: number
DefinitionName: string
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论