344 lines
9.6 KiB
Vue

<template>
<div class="preview" v-if="show">
<van-overlay :show="show">
<div class="wrapper">
<div class="block">
<el-button v-show="controlState" class="close-bar" icon="el-icon-close" size="mini" title="关闭" circle @click="close"></el-button>
<Artplayer @get-instance="getInstance" :option="option" :style="style"/>
</div>
</div>
</van-overlay>
</div>
</template>
<script>
import fileConfig from '@/utils/file-config'
import Artplayer from "@/components/preview/Artplayer.vue";
import artplayerPluginVttThumbnail from './artplayer-plugin-vtt-thumbnail';
import Hls from "hls.js";
import FlvJs from "flv.js";
export default {
name: 'VideoPreview',
components: {
Artplayer
},
props: {
file: {
type: Object,
default: function () {
return {}
}
},
shareId: {
type: String,
default: undefined
},
status: {
type: Boolean,
default: false
}
},
computed: {},
data() {
return {
pc: this.$pc,
art: null,
controlState: true,
switchQuality: false,
option: {
url: '',
title: '', // 视频标题,目前会出现在 视频截图 和 迷你模式 下
fullscreen: true,
fullscreenWeb: this.$pc,
//pip: true, // 是否在底部控制栏里显示 画中画 的开关按钮
flip: true, // 是否显示视频翻转功能,目前只出现在 设置面板 和 右键菜单 里
playbackRate: true, // 是否显示视频播放速度功能,会出现在 设置面板 和 右键菜单 里
aspectRatio: true, // 是否显示视频长宽比功能,会出现在 设置面板 和 右键菜单 里
screenshot: true, // 是否在底部控制栏里显示 视频截图 功能
setting: true, // 是否在底部控制栏里显示 设置面板 的开关按钮
autoplay: true, // 是否自动播放
autoPlayback: true, // 是否使用自动 回放功能
theme: '#23ade5',
hotkey: true, // 是否使用快捷键
controls: [
{
name: 'iina',
position: 'right',
tooltip: 'IINA',
style: {
marginRight: this.$pc ? '20px' : '0px',
},
html: `<a><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/iina.webp")}"></a>`,
index: 1,
},
{
name: 'nplayer',
position: 'right',
tooltip: 'nPlayer',
style: {
marginRight: this.$pc ? '20px' : '0px',
},
html: `<a><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/nplayer.webp")}"></a>`,
index: 2,
},
{
name: 'infuse',
position: 'right',
tooltip: 'Infuse',
style: {
marginRight: this.$pc ? '20px' : '0px',
},
html: `<a><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/infuse.webp")}"></a>`,
index: 3,
},
{
name: 'copyLink',
position: 'right',
html: '复制链接',
index: 5,
style: {
marginRight: this.$pc ? '20px' : '0px',
},
click: () => this.copyToClipboard(this.videoLink),
}
]
},
videoLink: '',
style: {
width: '100vw',
height: '60vh',
maxWidth: '900px',
maxHeight: '600px',
},
show: this.status,
timeout: null,
playing: false,
}
},
mounted() {
document.addEventListener('keyup', this.escape)
},
destroyed() {
document.removeEventListener('keyup', this.escape)
},
watch: {
status: function (visible) {
if (visible) {
if (!this.$pc) {
// 去掉this.option.controls中name为 name: 'iina'
this.option.controls = this.option.controls.filter(item => item.name === 'nplayer' || item.name === 'infuse')
}
let videoUrl = fileConfig.previewUrl(this.$store.state.user.name, this.file, this.$store.getters.token)
if (this.shareId) {
videoUrl = fileConfig.publicPreviewUrl(this.file, window.shareId, this.$store.getters.shareToken)
}
const originUrl = videoUrl
if (this.file.m3u8) {
videoUrl = `${fileConfig.baseUrl}/video/hls/${this.file.m3u8}`
if (this.shareId) {
const shareToken = this.$store.getters.shareToken ? this.$store.getters.shareToken : 'token'
videoUrl = `${fileConfig.baseUrl}/public/video/hls/${window.shareId}/${shareToken}/${this.file.m3u8}`
}
}
this.option.url = videoUrl
if (this.file.m3u8) {
const supported = ['mp4', 'ogg', 'mkv', 'webm', 'hls', 'mov']
if (supported.includes(this.file.suffix.toLowerCase())) {
this.option.quality = [
{
default: true,
html: 'HD',
url: this.option.url,
},
{
html: '原文件',
url: originUrl,
}
]
}
this.option.customType = {
m3u8: this.playM3u8
}
this.option.plugins = [
artplayerPluginVttThumbnail({
vtt: videoUrl.replace(/m3u8/g, 'vtt'),
})
]
}
if (this.file.contentType.indexOf('flv') > -1) {
this.option.url = originUrl
this.option.quality = []
this.option.customType = {
flv: this.playFlv
}
}
this.videoLink = window.location.origin + originUrl
this.title = this.file.name
this.option.id = this.file.id
this.show = true
this.switchQuality = true
} else {
this.option.url = ''
this.videoLink = ''
this.option.quality = []
}
}
},
methods: {
copyToClipboard(text) {
// 复制文本
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
textArea.remove();
this.$message({
message: '复制成功',
type: 'success',
duration: 1000,
});
},
escape(event) {
// 监听ESC键
if (event.code === 'Escape') {
this.close()
}
},
close() {
this.show = false
this.$emit('update:status', false)
},
getInstance(art) {
this.art = art
let doc = this.art.controls.$parent
if (this.$pc) {
// iina
const iina = doc.querySelector('[data-index="1"]').querySelector('a');
iina.href = `iina://weblink?url=${this.videoLink}`
// nplayer
const nplayer = doc.querySelector('[data-index="2"]').querySelector('a');
nplayer.href = `nplayer-${this.videoLink}`
// infuse
const infuse = doc.querySelector('[data-index="3"]').querySelector('a');
infuse.href = `infuse://x-callback-url/play?url=${this.videoLink}`
} else {
// nplayer
const nplayer = doc.querySelector('[data-index="2"]').querySelector('a');
nplayer.href = `nplayer-${this.videoLink}`
// infuse
const infuse = doc.querySelector('[data-index="3"]').querySelector('a');
infuse.href = `infuse://x-callback-url/play?url=${this.videoLink}`
}
this.art.on('control', (state) => {
if (this.controlState !== state) {
this.controlState = state
}
});
},
playM3u8(video, url, art) {
if (Hls.isSupported()) {
if (art.hls) art.hls.destroy();
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);
art.hls = hls;
art.on('destroy', () => hls.destroy());
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = url;
} else {
art.notice.show = 'Unsupported playback format: m3u8';
}
},
playFlv(video, url, art) {
if (FlvJs.isSupported()) {
if (art.flv) art.flv.destroy();
const flv = FlvJs.createPlayer({ type: 'flv', url });
flv.attachMediaElement(video);
flv.load();
art.flv = flv;
art.on('destroy', () => flv.destroy());
} else {
art.notice.show = 'Unsupported playback format: flv';
}
}
}
}
</script>
<style lang="scss" scoped>
.preview {
position: fixed;
top: 0;
left: 0;
z-index: 1002;
width: 100%;
height: 100%;
}
.van-overlay {
position: fixed;
top: 0;
left: 0;
z-index: 1003;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .7);
}
.block {
.close-bar {
z-index: 2006;
position: relative;
top: -30px;
right: -30px;
float: right;
font-size: 18px;
color: #fff0f0;
background: #d4d4d475;
border-color: rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) #69696975;
}
.close-bar:hover {
cursor: pointer;
border-color: #fff0f0;
}
}
@media screen and (max-width: 768px) {
.block {
.close-bar {
z-index: 2006;
position: relative;
top: 0;
right: 0;
font-size: 18px;
float: right;
color: #fff0f0;
background: #d4d4d475;
border-color: rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) #69696975;
}
.close-bar:hover {
cursor: pointer;
border-color: #fff0f0;
}
}
}
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>