| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- <template>
- <div class="video-player-wrapper">
- <div id="dplayer" ref="dplayerContainer"></div>
- </div>
- </template>
- <script>
- import flvjs from 'flv.js'
- import DPlayer from 'dplayer'
- import { videoUrl } from '@/api/video'
- export default {
- name: 'VideoPlayerCard',
- props: {
- videoId: { type: String, required: true },
- videoData: { type: Object, required: true },
- userToken: { type: String, default: null },
- sendEvent: { type: Boolean, default: true }
- },
- data() {
- return {
- player: null,
- wsClient: null,
- wsUrl: null,
- wsReconnectLock: false,
- intervalEvent: null,
- danmakuApi: process.env.VUE_APP_SERVER_URL + '/api/comment/danmaku/'
- }
- },
- watch: {
- videoId: {
- immediate: true,
- handler(val) {
- if (val) this.loadVideoSource()
- }
- }
- },
- beforeDestroy() {
- this.destroyResources()
- },
- methods: {
- destroyResources() {
- if (this.player) this.player.destroy()
- if (this.wsClient) this.wsClient.close()
- if (this.intervalEvent) clearInterval(this.intervalEvent)
- },
- loadVideoSource() {
- this.destroyResources()
- videoUrl(this.videoId).then(res => {
- if (res.code === 0) {
- // 初始化 WebSocket
- if (this.userToken && this.sendEvent) {
- this.wsUrl = this.getWsUrl(this.videoId)
- this.initWebSocket()
- }
- // 初始化播放器
- const { type, urls, currentTime } = res.data
- if (type === 'mp4') {
- urls.forEach(u => u.type = 'normal')
- this.initMp4Player(urls, currentTime)
- } else if (type === 'flv') {
- this.$message.error('flv player not implement')
- }
- }
- })
- },
- getWsUrl(videoId) {
- const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
- const host = window.location.host
- return `${protocol}${host}/ws/media?userToken=${this.userToken}&videoId=${videoId}`
- },
- initMp4Player(urls, pos) {
- this.player = new DPlayer({
- container: this.$refs.dplayerContainer,
- lang: 'zh-cn',
- screenshot: true,
- volume: 0.1,
- video: { pic: this.videoData.coverUrl, defaultQuality: 0, quality: urls },
- danmaku: { id: this.videoId, api: this.danmakuApi, token: this.userToken, bottom: '15%', unlimited: true }
- })
- this.player.seek(pos)
- this.bindEvents()
- },
- bindEvents() {
- let ended = false
- this.player.on('play', () => {
- if (this.sendEvent) {
- clearInterval(this.intervalEvent)
- this.intervalEvent = setInterval(() => {
- if (!ended) this.sendProgress(false)
- }, 5000)
- }
- })
- this.player.on('ended', () => {
- ended = true
- clearInterval(this.intervalEvent)
- this.sendProgress(true)
- })
- },
- sendProgress(isEnded) {
- const jsonData = {
- type: 'progress',
- direction: 'c2s',
- data: {
- mediaId: this.videoId,
- mediaType: 1,
- currentTime: this.player.video.currentTime,
- ended: isEnded
- }
- }
- if (this.wsClient && this.wsClient.readyState === WebSocket.OPEN) {
- this.wsClient.send(JSON.stringify(jsonData))
- }
- },
- initWebSocket() {
- this.wsClient = new WebSocket(this.wsUrl)
- this.wsClient.onopen = () => this.$emit('ws-status', true)
- this.wsClient.onclose = () => {
- this.$emit('ws-status', false)
- this.reconnect()
- }
- this.wsClient.onerror = () => this.reconnect()
- this.wsClient.onmessage = (evt) => {
- const msg = JSON.parse(evt.data)
- this.$emit('update-view-count', msg.viewCount)
- }
- },
- reconnect() {
- if (this.wsReconnectLock) return
- this.wsReconnectLock = true
- setTimeout(() => {
- this.initWebSocket()
- this.wsReconnectLock = false
- }, 5000)
- }
- }
- }
- </script>
- <style scoped>
- .video-player-wrapper {
- background: #000;
- border-radius: 4px;
- overflow: hidden;
- width: 100%;
- }
- #dplayer {
- height: 520px;
- }
- @media screen and (max-width: 768px) {
- #dplayer { height: 240px; }
- }
- /* 隐藏网页全屏按钮(左侧那个按钮) */
- ::v-deep .dplayer-full-in-icon {
- display: none !important;
- }
- /* 调整剩下的全屏按钮间距,防止右侧留白过大 */
- ::v-deep .dplayer-full-icon {
- margin-right: 5px !important;
- }
- </style>
|