Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-learn
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-learn
Commits
e22f07ca
提交
e22f07ca
authored
8月 03, 2022
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: update
上级
e0e05750
隐藏空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
149 行增加
和
56 行删除
+149
-56
AppVideoPlayer.vue
src/components/base/AppVideoPlayer.vue
+8
-2
CoursePlayerVideo.vue
src/modules/course/components/CoursePlayerVideo.vue
+140
-54
types.ts
src/modules/course/types.ts
+1
-0
没有找到文件。
src/components/base/AppVideoPlayer.vue
浏览文件 @
e22f07ca
...
@@ -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
>
src/modules/course/components/CoursePlayerVideo.vue
浏览文件 @
e22f07ca
...
@@ -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
v
ideoList
=
$computed
<
CourseResourceType
[]
>
(()
=>
{
const
resourceV
ideoList
=
$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
(
v
ideoList
.
length
)
{
if
(
resourceV
ideoList
.
length
)
{
const
found
=
v
ideoList
.
find
(
item
=>
item
.
resource_id
===
resourceId
)
const
found
=
resourceV
ideoList
.
find
(
item
=>
item
.
resource_id
===
resourceId
)
resourceId
=
found
?
resourceId
:
v
ideoList
[
0
]?.
resource_id
resourceId
=
found
?
resourceId
:
resourceV
ideoList
[
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
v
ideoList"
v-for=
"item in
resourceV
ideoList"
: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
>
src/modules/course/types.ts
浏览文件 @
e22f07ca
...
@@ -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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论