bug:15685、15588、15575

dev
LiuJiaNan 2024-05-31 16:07:31 +08:00
parent dcbe398e58
commit 23126c74e9
7 changed files with 268 additions and 56 deletions

2
hybrid/html/hls/hls.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
hybrid/html/plyr/plyr.js Normal file

File diff suppressed because one or more lines are too long

154
hybrid/html/video.html Normal file
View File

@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<link rel="stylesheet" href="./plyr/plyr.css"/>
<style>
* {
margin: 0;
padding: 0;
}
.plyr {
--plyr-control-icon-size: 5vmin;
--plyr-font-size-time: 4.5vmin;
.plyr__time + .plyr__time {
display: block !important;
}
}
#player {
width: 100%;
height: 100%;
}
</style>
<body>
<div>
<video id="player" playsinline controls data-poster="" style="width: 100vw">
<source src="" type="video/mp4"/>
</video>
</div>
</body>
<script src="./js/hybrid_html_uni.webview.1.5.5.js"></script>
<script src="./plyr/plyr.js"></script>
<script src="./hls/hls.js"></script>
<script>
let player = null
let hls = null
let currentTimeTimer = null
let isSeekEnd = false
let src = ''
let playTime = ''
let poster = ''
function setCurrentTime(type) {
currentTimeTimer && clearTimeout(currentTimeTimer);
currentTimeTimer = setTimeout(() => {
player.currentTime = playTime;
// 如果初始没有设置上开始播放时间,递归调用直到设置上为止
if (type === "ready" && !isSeekEnd && Math.abs(player.currentTime - playTime) > 2) {
setCurrentTime(type);
}
}, 500);
}
function initVideo() {
const video = document.querySelector("#player")
video.setAttribute('data-poster', src)
video.querySelector('source').setAttribute('src', poster)
player = new Plyr(video, {
type: "",
autoplay: true,
controls: [
"play-large",
"play",
"current-time",
"duration",
"mute",
"volume",
"captions",
"fullscreen",
],
seekTime: 0,
ratio: "16:9",
speed: {selected: 1, options: [1]},
});
hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
player.play();
player.on("ready", () => {
setCurrentTime("ready")
document.title = JSON.stringify({
eventName: 'ready',
currentTime: player ? player.currentTime : ''
})
});
player.on("seeking", () => {
isSeekEnd = false;
document.title = JSON.stringify({
eventName: 'seeking',
currentTime: player ? player.currentTime : ''
})
});
player.on("seeked", () => {
isSeekEnd = true;
document.title = JSON.stringify({
eventName: 'seeked',
currentTime: player ? player.currentTime : ''
})
});
player.on("play", () => {
document.title = JSON.stringify({
eventName: 'play',
currentTime: player ? player.currentTime : ''
})
});
player.on("pause", () => {
document.title = JSON.stringify({
eventName: 'pause',
currentTime: player ? player.currentTime : ''
})
});
player.on("ended", () => {
document.title = JSON.stringify({
eventName: 'ended',
currentTime: player ? player.currentTime : ''
})
});
player.on("timeupdate", () => {
if (player.currentTime - playTime > 5) {
playTime = player.currentTime;
document.title = JSON.stringify({
eventName: 'timeupdate',
currentTime: player ? player.currentTime : ''
})
}
});
}
document.addEventListener('UniAppJSBridgeReady', function () {
const data = plus.webview.getWebviewById("video")
src = data.src
poster = data.poster
playTime = +data.playTime
initVideo()
});
function htmlMessage(event) {
if (player) {
switch (event) {
case 'fullscreen.exit':
player.fullscreen.active && player.fullscreen.exit()
break
default:
player[event]()
break
}
}
}
</script>
</html>

View File

@ -6,20 +6,30 @@
<!-- </cu-custom>--> <!-- </cu-custom>-->
<view id="fixed"> <view id="fixed">
<view v-if="videoSrc"> <view v-if="videoSrc">
<video <web-view-video
id="video" ref="video"
:src="videoSrc" :src="videoSrc"
:poster="videoPoster" :poster="videoPoster"
autoplay :play-time="serverVideoPlayTime"
controls
style="width: 100vw"
:show-progress="false"
:enable-progress-gesture="false"
@play="fnPlay" @play="fnPlay"
@pause="fnPause" @pause="fnPause"
@ended="fnEnded" @ended="fnEnded"
@timeupdate="fnTimeUpdate" @timeupdate="fnTimeUpdate"
/> />
<!-- <video-->
<!-- id="video"-->
<!-- :src="videoSrc"-->
<!-- :poster="videoPoster"-->
<!-- autoplay-->
<!-- controls-->
<!-- style="width: 100vw"-->
<!-- :show-progress="false"-->
<!-- :enable-progress-gesture="false"-->
<!-- @play="fnPlay"-->
<!-- @pause="fnPause"-->
<!-- @ended="fnEnded"-->
<!-- @timeupdate="fnTimeUpdate"-->
<!-- />-->
</view> </view>
<image <image
v-if="!videoSrc" v-if="!videoSrc"
@ -110,11 +120,15 @@
<script> <script>
import {basePath, loginUser, baseImgPath} from "@/common/tool"; import {basePath, loginUser, baseImgPath} from "@/common/tool";
import WebViewVideo from './web_view_video.vue'
let faceAuthTimer; // let faceAuthTimer; //
let throttleTimer; let throttleTimer;
let throttleFlag; let throttleFlag;
export default { export default {
components: {
WebViewVideo,
},
data() { data() {
return { return {
baseImgPath, baseImgPath,
@ -141,7 +155,6 @@ export default {
videoSrc: "", // videoSrc: "", //
videoPoster: "", // videoPoster: "", //
scrollHeight: '0px', // scroll-view scrollHeight: '0px', // scroll-view
videoContext: null, //
submitTimeWaitForCount: 0, // submitTimeWaitForCount: 0, //
verification:false,// verification:false,//
} }
@ -151,6 +164,7 @@ export default {
this.CLASS_ID = options.CLASS_ID; this.CLASS_ID = options.CLASS_ID;
this.STUDENT_ID = options.STUDENT_ID; this.STUDENT_ID = options.STUDENT_ID;
this.ISFACE = options.ISFACE; this.ISFACE = options.ISFACE;
// this.ISFACE = '0';
this.fnInit(); this.fnInit();
}, },
onReady() { onReady() {
@ -168,6 +182,7 @@ export default {
}, },
beforeDestroy() { beforeDestroy() {
uni.$off('verification') uni.$off('verification')
this.fnClearInterval()
}, },
watch: { watch: {
verification() { verification() {
@ -326,7 +341,9 @@ export default {
this.videoList = resData.pd.VIDEOLIST; this.videoList = resData.pd.VIDEOLIST;
}, },
async fnVideoSwitching(videoData, hasNodes, index, index1) { async fnVideoSwitching(videoData, hasNodes, index, index1) {
this.videoContext && this.videoContext.pause(); this.$refs.video && this.$refs.video.fn('destroy')
this.videoSrc = "";
this.videoPoster = "";
this.submitTimeWaitForCount = 0; this.submitTimeWaitForCount = 0;
if (this.changeVideoPlayTime !== 0) { if (this.changeVideoPlayTime !== 0) {
await this.fnSubmitPlayTime("0", this.changeVideoPlayTime); await this.fnSubmitPlayTime("0", this.changeVideoPlayTime);
@ -339,8 +356,6 @@ export default {
if (this.ISFACE === "1") { if (this.ISFACE === "1") {
await this.fnNavigationFaceAuth(); await this.fnNavigationFaceAuth();
} else { } else {
this.videoSrc = "";
this.videoPoster = "";
await this.fnGetVideoPlayInfo(); await this.fnGetVideoPlayInfo();
} }
}, },
@ -349,11 +364,9 @@ export default {
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID, VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
CURRICULUM_ID: this.videoData.CURRICULUM_ID, CURRICULUM_ID: this.videoData.CURRICULUM_ID,
}); });
this.videoSrc = resData.PLAYURLMP4 || resData.PLAYURL; await this.fnGetVideoPlayProgress(resData.PLAYURL, resData.COVERURL);
this.videoPoster = resData.COVERURL;
await this.fnGetVideoPlayProgress();
}, },
async fnGetVideoPlayProgress() { async fnGetVideoPlayProgress(PLAYURL, COVERURL) {
const resData = await this.post('/app/coursestudyvideorecord/getVideoProgress',{ const resData = await this.post('/app/coursestudyvideorecord/getVideoProgress',{
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID, VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
CURRICULUM_ID: this.videoData.CURRICULUM_ID, CURRICULUM_ID: this.videoData.CURRICULUM_ID,
@ -364,51 +377,23 @@ export default {
await this.fnSubmitPlayTime("0", 0); await this.fnSubmitPlayTime("0", 0);
} }
this.serverVideoPlayTime = Math.floor(resData.pd.RESOURCETIME) || 0 this.serverVideoPlayTime = Math.floor(resData.pd.RESOURCETIME) || 0
if (this.ISFACE === "1") { this.videoSrc = PLAYURL;
if (this.verification) { this.videoPoster = COVERURL;
this.fnCreateVideo();
} else {
this.fnCreateVideo();
}
} else {
this.fnCreateVideo();
}
},
fnCreateVideo() {
if (!this.videoContext) {
this.videoContext = uni.createVideoContext("video");
}
this.videoContext.play();
this.serverVideoPlayTime !== 0 && this.videoContext.seek(this.serverVideoPlayTime);
}, },
fnTimeUpdate(event) { fnTimeUpdate(event) {
this.throttle(async () => { this.throttle(async () => {
const currentTime = event.detail.currentTime; if (event - this.serverVideoPlayTime >= 10) {
if (currentTime - this.serverVideoPlayTime >= 10) {
uni.navigateBack(); uni.navigateBack();
return; return;
} }
//appbug10退 this.changeVideoPlayTime = event;
// if(currentTime !== 0 && Math.abs(currentTime - this.serverVideoPlayTime) > 10) { if (event - this.serverVideoPlayTime >= 5) {
// this.videoContext.pause(); this.serverVideoPlayTime = event;
// uni.showModal({
// title: "",
// content: "app退",
// showCancel:false,
// success: (res) => {
// uni.navigateBack();
// return;
// },
// });
// }
this.changeVideoPlayTime = currentTime;
if (currentTime - this.serverVideoPlayTime >= 5) {
this.serverVideoPlayTime = currentTime;
this.submitTimeWaitForCount = ++this.submitTimeWaitForCount; this.submitTimeWaitForCount = ++this.submitTimeWaitForCount;
} }
if (this.submitTimeWaitForCount === 6) { if (this.submitTimeWaitForCount === 6) {
this.submitTimeWaitForCount = 0; this.submitTimeWaitForCount = 0;
await this.fnSubmitPlayTime("0", currentTime); await this.fnSubmitPlayTime("0", event);
} }
}, 1000); }, 1000);
}, },
@ -462,7 +447,8 @@ export default {
this.videoList[this.firstIndex].percent = percent; this.videoList[this.firstIndex].percent = percent;
} }
if (resData.pd.CANEXAM === "1") { if (resData.pd.CANEXAM === "1") {
this.videoContext.pause(); this.$refs.video && this.$refs.video.fn('pause')
this.$refs.video && this.$refs.video.fn('fullscreen.exit')
uni.showModal({ uni.showModal({
title: "提示", title: "提示",
content: "当前班级内所有课程均已学完,是否直接参加考试?", content: "当前班级内所有课程均已学完,是否直接参加考试?",
@ -470,12 +456,11 @@ export default {
cancelButtonText: "否", cancelButtonText: "否",
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
this.videoContext && this.videoContext.exitFullScreen()
uni.navigateTo({ uni.navigateTo({
url: '/pages/application/onlinexxks/course_exam?STAGEEXAMPAPERINPUT_ID=' + resData.paper.STAGEEXAMPAPERINPUT_ID + '&STAGEEXAMPAPER_ID=' + resData.paper.STAGEEXAMPAPERINPUT_ID + '&CLASS_ID=' + this.CLASS_ID + '&POST_ID=' + resData.pd.POST_ID + '&STUDENT_ID=' + this.STUDENT_ID + '&NUMBEROFEXAMS=' + resData.pd.NUMBEROFEXAMS + '&entrySite=video_study' url: '/pages/application/onlinexxks/course_exam?STAGEEXAMPAPERINPUT_ID=' + resData.paper.STAGEEXAMPAPERINPUT_ID + '&STAGEEXAMPAPER_ID=' + resData.paper.STAGEEXAMPAPERINPUT_ID + '&CLASS_ID=' + this.CLASS_ID + '&POST_ID=' + resData.pd.POST_ID + '&STUDENT_ID=' + this.STUDENT_ID + '&NUMBEROFEXAMS=' + resData.pd.NUMBEROFEXAMS + '&entrySite=video_study'
}) })
} else if (res.cancel) { } else if (res.cancel) {
this.videoContext.play(); this.$refs.video && this.$refs.video.fn('play')
} }
}, },
}); });
@ -488,7 +473,6 @@ export default {
} }
this.changeVideoPlayTime = 0; this.changeVideoPlayTime = 0;
this.fnSubmitPlayTime("1", 0); this.fnSubmitPlayTime("1", 0);
this.videoContext.pause();
this.fnClearInterval(); this.fnClearInterval();
}, },
fnPlay() { fnPlay() {
@ -499,6 +483,10 @@ export default {
} }
faceAuthTimer = setInterval(async () => { faceAuthTimer = setInterval(async () => {
if (new Date().getTime() >= this.randomTime) { if (new Date().getTime() >= this.randomTime) {
this.fnClearInterval();
this.$refs.video && this.$refs.video.fn('fullscreen.exit')
this.videoSrc = ''
this.videoPoster = ''
this.randomTime = 0; this.randomTime = 0;
await this.fnSubmitPlayTime("0", this.changeVideoPlayTime); await this.fnSubmitPlayTime("0", this.changeVideoPlayTime);
await this.fnNavigationFaceAuth(); await this.fnNavigationFaceAuth();
@ -522,7 +510,6 @@ export default {
CHAPTER_ID: this.videoData.CHAPTER_ID, CHAPTER_ID: this.videoData.CHAPTER_ID,
VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID, VIDEOCOURSEWARE_ID: this.videoData.VIDEOCOURSEWARE_ID,
}) })
this.videoContext && this.videoContext.exitFullScreen()
const resData = await this.post('/app/user/getUserFace', { const resData = await this.post('/app/user/getUserFace', {
USERNAME: loginUser.NAME, USERNAME: loginUser.NAME,
USER_ID: loginUser.USER_ID, USER_ID: loginUser.USER_ID,
@ -551,7 +538,7 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.video_bg, #video { .video_bg, #video {
width: 100%; width: 100%;
height: 450upx; height: 250px;
} }
.title { .title {

View File

@ -0,0 +1,66 @@
<template>
<view style="height: 250px"></view>
</template>
<script>
export default {
props: {
src: {
type: String,
required: true
},
poster: {
type: String,
default: ''
},
playTime: {
type: Number,
default: 0
},
},
data() {
return {
webview: null
}
},
mounted() {
this.createWebView()
},
beforeDestroy() {
plus.webview.close('video')
this.webview.removeEventListener('titleUpdate', this.handlePostMessage)
},
methods: {
createWebView() {
this.webview = plus.webview.create('/hybrid/html/video.html', 'video', {
top: uni.getSystemInfoSync().statusBarHeight + 44 + 'px',
height: '250px',
videoFullscreen: "landscape"
}, {
src: this.src,
poster: this.poster,
playTime: this.playTime
});
this.webview.show()
this.webview.addEventListener('titleUpdate', this.handlePostMessage)
},
handlePostMessage(event) {
const {eventName, currentTime} = JSON.parse(event.title)
const eventNameKeys = ['ready', 'seeking', 'seeked', 'play', 'pause', 'ended', 'timeupdate']
if (eventNameKeys.includes(eventName)) {
this.$emit(eventName, currentTime)
}
},
fn(event) {
const eventKeys = ['play', 'pause', 'destroy', 'fullscreen.exit']
if (eventKeys.includes(event)) {
this.webview.evalJS(`htmlMessage('${event}')`);
}
}
}
}
</script>
<style scoped>
</style>