前言
jq事件:https://www.jb51.cc/js/153165.html
示例代碼
<!DOCTYPE html>
<html>
<head>
<script src="./video.js"></script>
<link href="./video-js.css" rel="stylesheet" />
<script src="./videojs-contrib-hls.min.js"></script>
<script src="./jquery-3.5.0.min.js"></script>
</head>
<body>
<video id="myVideo" class="video-js vjs-default-skin" controls preload="auto" width="1000" height="500"
data-setup='{}'>
</video>
<script>
/**
* 邏輯:
* 1.進入頁面,頁面獲取到請求參數
* 2.頁面通過ajax進行請求,獲取到文件信息
* 3.開始播放時,獲取下一集的文件信息
* 4.播放結束時,更新視頻地址,繼續播放下一集
*
*/
// 第一步
var num = 0
var id = GetPar('id')
// 第二步
var url = 'http://localhost:8001/file/playOnline/' + id
$.get(url, function(res) {
if (res.code == 200) {
localStorage.setItem("currentId", id)
localStorage.setItem("currentUrl", res.data)
var player = videojs('myVideo',{
// 播放速度
playbackRates: [0.7, 1.0, 1.5, 2.0],
// 如果true,瀏覽器準備好時開始回放。
autoplay: true,
// 默認情況下將會消除任何音頻。
muted: false,
// 導致視頻一結束就重新開始。
loop: false,
// 建議瀏覽器在<video>加載元素後是否應該開始下載視頻數據。auto瀏覽器選擇最佳行爲,立即開始加載視頻(如果瀏覽器支持)
preload: 'auto',
sources: [
{
// 這裏的種類支持很多種:基本視頻格式、直播、流媒體等,具體可以參看git網址項目
type: 'application/x-mpegURL',
src: ''
}
],
});
videojs("myVideo").ready(function(){
player.ready(function() {
this.src({
src: localStorage.getItem('currentUrl'),
type: 'application/x-mpegURL',
});
this.on("play", function() {
console.log("我走了Play")
num++
if (num == 1) {
// 第三步
getNextEpisode(localStorage.getItem('currentId'))
}
});
this.on("ended", function() {
debugger
// 第四步
num = 0
var nextUrl = localStorage.getItem("nextUrl")
if(nextUrl != null && typeof(nextUrl) != 'undefined' && nextUrl != ''){
this.src({
src: nextUrl,
type: 'application/x-mpegURL',
});
}
});
});
});
}
})
// 獲取下一集信息
function getNextEpisode(id) {
var url = "http://localhost:8001/file/getNextEpisode";
$.ajax({
url: url,
type: "POST",
data: JSON.stringify({
id: id
}),
contentType: "application/json",
dataType: "json",
success: function(res) {
debugger
if(res.code == 200){
if(res.data == null){
localStorage.setItem('nextUrl', '')
}else{
localStorage.setItem('nextUrl', res.data.m3u8Url)
localStorage.setItem('nextId', res.data.id)
}
}
}
});
}
// 獲取瀏覽器請求參數
function GetPar(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
var r = window.location.search.substr(1).match(reg)
if (r != null) return decodeURIComponent(r[2])
return null
}
</script>
</html>
vue中使用video.js。代碼僅做個人備份
功能:點擊進行播放,當前播放完畢自動跳到下一集。全部播放完畢暫停。 PC手機都可以
<template>
<el-card class="box-card">
<div class="block-search">
<el-input
class="width-190"
v-model="queryParam.keyword"
placeholder="請輸入文件名稱"
size="medium"
clearable
></el-input>
<el-select
v-model="queryParam.fileType"
placeholder="全部類型"
class="width-190"
size="medium"
clearable
>
<el-option
v-for="item in optionsByFileType"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<el-button type="primary" plain size="medium" icon="el-icon-search" @click="_search">搜索</el-button>
</div>
<el-table :data="tableData" border style="width: 100%" size="mini">
<el-table-column fixed prop="id" label="文件id" width="60"></el-table-column>
<el-table-column prop="oldName" label="原文件名稱" width="120" show-overflow-tooltip></el-table-column>
<el-table-column prop="newName" label="新文件名稱" width="120" show-overflow-tooltip></el-table-column>
<el-table-column prop="fileSize" label="文件大小" width="120" show-overflow-tooltip>
<template slot-scope="scope">{{scope.row.fileSize + 'kb'}}</template>
</el-table-column>
<el-table-column prop="fileType" label="文件類型" width="120" show-overflow-tooltip>
<template slot-scope="scope">{{_getFileTypeStr(scope.row)}}</template>
</el-table-column>
<el-table-column prop="suffixName" label="擴展名" width="120" v-if="false"></el-table-column>
<el-table-column prop label="二維碼" width="120">
<template slot-scope="scope">
<el-popover placement="bottom" title="手機掃碼在線觀看" width="200" trigger="hover" content>
<canvas :id="'QRCode_'+scope.row.id"></canvas>
<i class="fa fa-qrcode" slot="reference" @mouseenter="generatorQrcode(scope.row)"></i>
</el-popover>
</template>
</el-table-column>
<el-table-column
prop="downloadPath"
label="文件路徑"
width="300"
show-overflow-tooltip
v-if="true"
></el-table-column>
<el-table-column prop="lastEpisodeName" label="上一集名稱" width="120" show-overflow-tooltip>
<template slot-scope="scope">{{scope.row.lastEpisodeName | columnDefauleValue}}</template>
</el-table-column>
<el-table-column prop="nextEpisodeName" label="下一集名稱" width="120" show-overflow-tooltip>
<template slot-scope="scope">{{scope.row.nextEpisodeName | columnDefauleValue}}</template>
</el-table-column>
<el-table-column prop="createDate" label="上傳時間" width="170">
<template slot-scope="scope">{{scope.row.createDate | timestampFormatTime}}</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="400">
<template slot-scope="scope">
<el-button size="mini" type="default" @click="_download(scope.row)">
<i class="fa fa-cloud-download"></i>下載
</el-button>
<el-button
size="mini"
type="default"
@click="_toFileConfig(scope.row)"
v-if="parseInt(scope.row.fileType) === 4"
>
<i class="fa fa-retweet"></i>配置
</el-button>
<el-button
size="mini"
type="primary"
disabled
v-if="parseInt(scope.row.fileType) === 4 && scope.row.transcodingStatus=== 2"
>
<i class="fa fa-spinner fa-pulse"></i>轉碼中
</el-button>
<el-button
size="mini"
type="primary"
@click="_playOnline(scope.row)"
v-else-if="parseInt(scope.row.fileType) === 4 &&(scope.row.transcodingStatus=== 1|| scope.row.transcodingStatus=== 3)"
>
<i class="fa fa-video-camera"></i>在線播放
</el-button>
<el-button size="mini" type="danger" @click="_delete(scope.row)">
<i class="fa fa-trash-o"></i>刪除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 文件配置 -->
<el-dialog
:title="$jsConstants.title"
:visible.sync="dialogVisibleByConfig"
width="30%"
:before-close="handleCloseByConfig"
>
<el-form
ref="fileConfig"
:model="fileConfig"
label-width="100px"
size="small"
v-if="currentFile"
>
<el-form-item label="原文件名稱" class>{{currentFile.oldName}}</el-form-item>
<el-form-item label="新文件名稱" class>{{currentFile.newName}}</el-form-item>
<el-form-item label="上一集">
<el-input v-model="fileConfig.lastEpisode" placeholder="請輸入文件id"></el-input>
</el-form-item>
<el-form-item label="下一集">
<el-input v-model="fileConfig.nextEpisode" placeholder="請輸入文件id"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="handleCloseByConfig">取 消</el-button>
<el-button type="primary" @click="_updateFileInfo">確 定</el-button>
</span>
</el-dialog>
<el-dialog
:title="$jsConstants.title"
:visible.sync="isShow"
width="50%"
:before-close="handleClose"
@open="handleOpen"
>
<video
id="myVideo"
ref="myVideoRef"
controls
preload="auto"
class="video-js vjs-big-play-centered vjs-fluid vjs-16-9 vjs-big-play-centered"
data-setup="{}"
>
<source :src="playVideoUrl" type="application/x-mpegURL" />
</video>
</el-dialog>
<span ref="testRef"></span>
</el-card>
</template>
<script>
import QRCode from 'qrcode'
import { mapState, mapActions } from 'vuex'
import {
getNextEpisode,
updateFileInfo,
delFile,
getFilesByTtanscoded,
playOnline,
queryFileInfoPageList,
download,
getFileTypes
} from '@/api/logic/api-file'
export default {
data() {
return {
// 第一次播放標誌
playFlagForTheFirstTime: 0,
// 播放地址
playVideoUrl: 'http://192.168.17.46:8001/158304068-bkmmp4/ts/bkm.m3u8',
// 當前文件
currentFile: {},
// 文件配置
fileConfig: {
id: null,
lastEpisode: null,
nextEpisode: null
},
dialogVisibleByConfig: false,
// 刷新數組
transcodingArr: [],
// 在線播放
playOnlineUrl: null,
// 查詢參數
queryParam: {
pageInfoDto: {
pageNum: 0,
pageSize: 10
},
keyword: null,
fileType: null
},
// 文件類型
optionsByFileType: [],
// 列表數據
tableData: []
}
},
components: {
QRCode
},
computed: {
...mapState('StoreModuleFile', ['video']),
player() {
return this.$refs.videoPlayer.player
},
isShow: function() {
return this.$store.state.StoreModuleFile.video.isShow
},
getPlayOnlineUrl() {
return this.video.url
}
},
created() {
this._getFileTypes()
this._search()
},
onload() {},
onShow() {
debugger
console.log(this)
},
mounted() {},
methods: {
...mapActions('StoreModuleFile', ['setVideo']),
// 下一集
_getNextEpisode(currentId, currentUrl = null) {
getNextEpisode({ id: currentId }).then(res => {
if (res.code === this.$jsConstants.SUCCESS) {
// 獲取video信息
let video = JSON.parse(
JSON.stringify(this.$store.state.StoreModuleFile.video)
)
// 保存第一集視頻地址
if (video.firstId) {
video.firstId = video.firstId
video.firstUrl = video.firstUrl
} else {
video.firstId = currentId
video.firstUrl = currentUrl
}
if (res.data != null) {
// 如果沒有下一集
video.currentId = res.data.id
video.currentUrl = res.data.m3u8Url
video.nextEpisodeId = null
video.nextEpisodeUrl = null
} else {
video.currentId = null
video.currentUrl = null
video.nextEpisodeId = null
video.nextEpisodeUrl = null
}
this.setVideo(video)
}
})
},
// 在線播放
_playOnline(row) {
this.setVideo({
isShow: true
})
playOnline(row.id).then(res => {
let that = this
if (res.code === this.$jsConstants.SUCCESS) {
that.setVideo({
isShow: true,
currentId: row.id,
currentUrl: res.data,
nextEpisodeId: null,
nextEpisodeUrl: null,
firstId: row.id,
firstUrl: res.data
})
that.playVideoUrl = res.data
// 設置視頻源
let myVideo = document.getElementById('myVideo')
myVideo = myVideo === null ? that.$refs.viodeRef : myVideo
var options = {}
var player = that.$video(myVideo, options, function() {
player.ready(function() {
this.src({
src: res.data,
type: 'application/x-mpegURL'
})
this.on('play', function() {
let video = that.$store.state.StoreModuleFile.video
if (video.isOK) {
myVideo.pause()
// 清空暫停狀態
let newVideo = JSON.parse(JSON.stringify(video))
newVideo.isOK = false
that.setVideo(newVideo)
return false
}
// 播放開始,取當前文件id,查詢下一集的信息,存儲到狀態管理
// 查詢下一集;此處之所以判斷是否第一次播放,是因爲視頻暫停時,也會觸發該事件
that.playFlagForTheFirstTime++
if (that.playFlagForTheFirstTime === 1) {
that._getNextEpisode(video.currentId, video.currentUrl)
}
})
this.on('ended', function() {
let video = that.$store.state.StoreModuleFile.video
// 當前這一集視頻播放結束,play次數更新爲獲0
that.playFlagForTheFirstTime = 0
// 更新播放器視頻地址爲下一集
let currentUrl = video.currentUrl
if (currentUrl) {
player.src({
src: video.currentUrl
})
} else {
// 最後一集播放完畢,把視頻地址更換爲第一集地址;更新狀態管理播放文件信息;然後設置停止播放。
video.currentId = video.firstId
video.currentUrl = video.firstUrl
player.src({
src: video.firstUrl,
autoplay: false
})
// 添加暫停狀態,否則會循環播放
let newVideo = JSON.parse(JSON.stringify(video))
newVideo.isOK = true
that.setVideo(newVideo)
}
})
})
})
}
})
},
// 關閉
handleClose() {
// 更新單個視頻獲取下一集次數
this.playFlagForTheFirstTime = 0
this.setVideo({ isShow: false })
},
// 打開
handleOpen() {
// 更新單個視頻獲取下一集次數
this.playFlagForTheFirstTime = 0
},
// 初始化video
initVideo() {
debugger
this.setVideo({ isShow: true })
// 初始化視頻方法
let myVideo = document.getElementById('myVideo')
myVideo = myVideo === null ? this.$refs.myVideoRef : myVideo
if (myVideo) {
this.$video(myVideo, {
// 確定播放器是否具有用戶可以與之交互的控件。沒有控件,啓動視頻播放的唯一方法是使用autoplay屬性或通過Player API。
controls: true,
// 自動播放屬性,muted:靜音播放
autoplay: 'muted',
// 建議瀏覽器是否應在<video>加載元素後立即開始下載視頻數據。
preload: 'auto',
// 設置視頻播放器的顯示寬度(以像素爲單位)
width: '800px',
// 設置視頻播放器的顯示高度(以像素爲單位)
height: '400px',
hls: {
withCredentials: true
}
})
}
},
// 獲取二維碼地址
generatorQrcode(row) {
// 二維碼地址
let val = 'http://192.168.17.46:8080/static/h5/index.html?id=' + row.id
// 獲取頁面的canvas
var msg = document.getElementById('QRCode_' + row.id)
// 將獲取到的數據(val)畫到msg(canvas)上
QRCode.toCanvas(msg, val, function(error) {
console.log(error)
})
},
// 關閉config
handleCloseByConfig() {
this.dialogVisibleByConfig = false
this.currentFile = {}
this.fileConfig = {}
},
// 文件配置
_updateFileInfo() {
updateFileInfo(this.fileConfig).then(res => {
if (this.$jsConstants.SUCCESS === res.code) {
this.dialogVisibleByConfig = false
this.currentFile = {}
this.fileConfig = {}
this.$notify({
title: '成功',
message: '配置成功',
type: 'success'
})
this._search()
}
})
},
_toFileConfig(row) {
// 文件配置
this.fileConfig.id = row.id
this.fileConfig.lastEpisode = row.lastEpisode
this.fileConfig.nextEpisode = row.nextEpisode
// 當前文件
this.currentFile = row
this.dialogVisibleByConfig = true
},
// 文件下載
_download(row) {
download(row.id)
},
// 查詢
_search() {
queryFileInfoPageList(this.queryParam).then(res => {
console.log('res', res)
if (this.$jsConstants.SUCCESS === res.code) {
this.tableData = res.data.list
// 過濾出轉碼中的數據,然後每10秒刷新一次狀態
this.transcodingArr = []
this.tableData.forEach(obj => {
if (obj.transcodingStatus === 2) {
this.transcodingArr.push(obj.id)
}
})
this._RefreshStatus(this.transcodingArr)
}
})
},
// 刷新狀態
_RefreshStatus(ids) {
let that = this
if (ids === null || ids.length === 0) {
return false
}
let timeId = null
timeId = setInterval(function() {
getFilesByTtanscoded({ fileIds: ids }).then(res => {
if (that.$jsConstants.SUCCESS === res.code) {
if (res.data != null && res.data.length > 0) {
res.data.forEach(obj => {
// 彈框顯示轉碼成功
that.$notify({
title: '成功',
message: obj.newName + ' 轉碼成功',
type: 'success'
})
// 刪除該刷新元素
that.transcodingArr.some((item, index) => {
if (item === obj.id) {
that.transcodingArr.splice(index, 1)
// 如果沒有轉碼中的數據,則關閉定時器
if (that.transcodingArr.length === 0) {
clearInterval(timeId)
}
}
})
// 更新列表狀態
that.tableData.some((updateObj, updateIndex) => {
if ((updateObj.id = obj.id)) {
updateObj.transcodingStatus = 3
}
})
})
}
}
})
}, 3000)
},
// 刪除
_delete(row) {
if (row.transcodingStatus === 2) {
this.$notify({
title: '警告',
message: '文件正在轉碼,請稍後操作',
type: 'warning'
})
return false
}
this.$confirm('你確定要刪除嗎?')
.then(_ => {
// 二次確定
delFile({ id: row.id }).then(res => {
if (this.$jsConstants.SUCCESS === res.code) {
this.$notify({
title: '成功',
message: '刪除成功',
type: 'success'
})
this._search()
}
})
})
.catch(_ => {})
},
// 獲取文件類型
_getFileTypeStr(row) {
let str = ''
this.optionsByFileType.forEach(element => {
if (parseInt(element.value) === parseInt(row.fileType)) {
str = element.label
}
})
return str
},
// 遍歷選項
_getFileTypes() {
getFileTypes().then(res => {
this.optionsByFileType = []
res.data.forEach(element => {
this.optionsByFileType.push({
label: element.typeDescription,
value: element.typeId
})
})
})
}
}
}
</script>
<style lang="less" scoped>
</style>