Commit 4458cd983103fc0648cd3ce99ea90397da61b875

Authored by fengtao
Committed by xp.Huang
1 parent db42c0a2

fix: 单个视频切换视频源,切换后未播放为新的视频源

1 <template> 1 <template>
2 - <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }"> 2 + <div class="go-content-box" :style="{ width: baseSize?.w + 'px', height: baseSize?.h + 'px' }">
3 <video 3 <video
4 crossOrigin="anonymous" 4 crossOrigin="anonymous"
5 :id="`my-player`" 5 :id="`my-player`"
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 </div> 9 </div>
10 </template> 10 </template>
11 <script setup lang="ts"> 11 <script setup lang="ts">
12 -import { onMounted, ref, onUnmounted, watch, unref } from 'vue' 12 +import { onMounted, ref, onUnmounted, watch, unref, PropType } from 'vue'
13 import videojs from 'video.js' 13 import videojs from 'video.js'
14 import 'videojs-flvjs-es6' 14 import 'videojs-flvjs-es6'
15 import type { VideoJsPlayerOptions } from 'video.js' 15 import type { VideoJsPlayerOptions } from 'video.js'
@@ -19,6 +19,7 @@ import { isShareMode } from '@/views/share/hook' @@ -19,6 +19,7 @@ import { isShareMode } from '@/views/share/hook'
19 import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay' 19 import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay'
20 import { useFingerprint } from '@/utils/external/useFingerprint' 20 import { useFingerprint } from '@/utils/external/useFingerprint'
21 import { GetResult } from '@fingerprintjs/fingerprintjs' 21 import { GetResult } from '@fingerprintjs/fingerprintjs'
  22 +import { VideoPlayerTypeEnum } from '../config'
22 23
23 const props = defineProps({ 24 const props = defineProps({
24 sourceSrc: { 25 sourceSrc: {
@@ -33,23 +34,11 @@ const props = defineProps({ @@ -33,23 +34,11 @@ const props = defineProps({
33 avatar: { 34 avatar: {
34 type: String 35 type: String
35 }, 36 },
36 - w: {  
37 - type: Number,  
38 - default: 300  
39 - },  
40 - h: {  
41 - type: Number,  
42 - default: 300 37 + baseSize: {
  38 + type: Object as PropType<{ w: number; h: number }>
43 } 39 }
44 }) 40 })
45 41
46 -enum VideoPlayerType {  
47 - m3u8 = 'application/x-mpegURL',  
48 - mp4 = 'video/mp4',  
49 - webm = 'video/webm',  
50 - flv = 'video/x-flv'  
51 -}  
52 -  
53 const isRtspProtocol = (url: string) => { 42 const isRtspProtocol = (url: string) => {
54 const reg = /^rtsp:\/\//g 43 const reg = /^rtsp:\/\//g
55 return reg.test(url) 44 return reg.test(url)
@@ -58,15 +47,15 @@ const isRtspProtocol = (url: string) => { @@ -58,15 +47,15 @@ const isRtspProtocol = (url: string) => {
58 const getVideoTypeByUrl = (url = '') => { 47 const getVideoTypeByUrl = (url = '') => {
59 try { 48 try {
60 const { protocol, pathname } = new URL(url) 49 const { protocol, pathname } = new URL(url)
61 - if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv 50 + if (protocol.startsWith('rtsp:')) return VideoPlayerTypeEnum.flv
62 const reg = /[^.]\w*$/ 51 const reg = /[^.]\w*$/
63 const mathValue = pathname.match(reg) || [] 52 const mathValue = pathname.match(reg) || []
64 - const ext = (mathValue[0] as keyof typeof VideoPlayerType) || 'webm'  
65 - const type = VideoPlayerType[ext]  
66 - return type ? type : VideoPlayerType.webm 53 + const ext = (mathValue[0] as keyof typeof VideoPlayerTypeEnum) || 'webm'
  54 + const type = VideoPlayerTypeEnum[ext]
  55 + return type ? type : VideoPlayerTypeEnum.webm
67 } catch (error) { 56 } catch (error) {
68 console.error(error) 57 console.error(error)
69 - return VideoPlayerType.webm 58 + return VideoPlayerTypeEnum.webm
70 } 59 }
71 } 60 }
72 61
@@ -82,11 +71,9 @@ const fingerprintResult = ref<Nullable<GetResult>>(null) @@ -82,11 +71,9 @@ const fingerprintResult = ref<Nullable<GetResult>>(null)
82 const options: VideoJsPlayerOptions & Recordable = { 71 const options: VideoJsPlayerOptions & Recordable = {
83 language: 'zh-CN', // 设置语言 72 language: 'zh-CN', // 设置语言
84 controls: true, // 是否显示控制条 73 controls: true, // 是否显示控制条
85 - // preload: 'auto', // 预加载  
86 autoplay: props.autoPlay ? true : false, // 是否自动播放 74 autoplay: props.autoPlay ? true : false, // 是否自动播放
87 fluid: false, // 自适应宽高 75 fluid: false, // 自适应宽高
88 poster: props?.avatar || '', 76 poster: props?.avatar || '',
89 - // src: getSource() || '', // 要嵌入的视频源的源 URL  
90 sources: [], 77 sources: [],
91 muted: props.autoPlay ? true : false, 78 muted: props.autoPlay ? true : false,
92 userActions: { 79 userActions: {
@@ -107,6 +94,7 @@ const options: VideoJsPlayerOptions & Recordable = { @@ -107,6 +94,7 @@ const options: VideoJsPlayerOptions & Recordable = {
107 } 94 }
108 95
109 const { getResult } = useFingerprint() 96 const { getResult } = useFingerprint()
  97 +
110 async function getSource() { 98 async function getSource() {
111 fingerprintResult.value = await getResult() 99 fingerprintResult.value = await getResult()
112 let src = props.sourceSrc || '' 100 let src = props.sourceSrc || ''
@@ -144,11 +132,11 @@ const initVideo = async () => { @@ -144,11 +132,11 @@ const initVideo = async () => {
144 132
145 watch( 133 watch(
146 () => props.sourceSrc, 134 () => props.sourceSrc,
147 - async (newData: any) => { 135 + async () => {
  136 + videoPlayer?.src('')
148 const result = await getSource() 137 const result = await getSource()
149 - // props.sourceSrc = newData  
150 - if(props.autoPlay){  
151 - videoPlayer?.src(result) as any 138 + if (props.autoPlay) {
  139 + videoPlayer?.src(result)
152 videoPlayer?.play() 140 videoPlayer?.play()
153 } 141 }
154 }, 142 },
@@ -157,6 +145,20 @@ watch( @@ -157,6 +145,20 @@ watch(
157 } 145 }
158 ) 146 )
159 147
  148 +watch(
  149 + () => props.autoPlay,
  150 + async (newData: boolean) => {
  151 + if (newData) {
  152 + handleVideoPlay()
  153 + } else {
  154 + videoPlayer?.pause()
  155 + }
  156 + },
  157 + {
  158 + immediate: true
  159 + }
  160 +)
  161 +
160 onMounted(() => { 162 onMounted(() => {
161 initVideo() 163 initVideo()
162 }) 164 })
@@ -171,8 +173,8 @@ onUnmounted(() => { @@ -171,8 +173,8 @@ onUnmounted(() => {
171 //播放 173 //播放
172 const handleVideoPlay = () => videoPlayer?.play() 174 const handleVideoPlay = () => videoPlayer?.play()
173 175
  176 +//暂停和销毁
174 const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause() 177 const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause()
175 -//暂停  
176 defineExpose({ 178 defineExpose({
177 handleVideoPlay, 179 handleVideoPlay,
178 handleVideoDispose 180 handleVideoDispose
@@ -8,10 +8,30 @@ export enum sourceTypeEnum { @@ -8,10 +8,30 @@ export enum sourceTypeEnum {
8 PLATFORM = 'platform' 8 PLATFORM = 'platform'
9 } 9 }
10 10
  11 +export enum VideoPlayerTypeEnum {
  12 + m3u8 = 'application/x-mpegURL',
  13 + mp4 = 'video/mp4',
  14 + webm = 'video/webm',
  15 + flv = 'video/x-flv'
  16 +}
  17 +
  18 +export interface videoListInterface {
  19 + name: string
  20 + accessMode: number
  21 + id: string
  22 + videoUrl: string
  23 + label: string
  24 + value: string
  25 +}
  26 +
  27 +export enum AccessMode {
  28 + ManuallyEnter = 0,
  29 + Streaming = 1
  30 +}
  31 +
11 export const option = { 32 export const option = {
12 - url:'',  
13 dataset: '', 33 dataset: '',
14 - autoplay: false, 34 + autoplay: true,
15 poster: '', 35 poster: '',
16 sourceType: 'custom', 36 sourceType: 'custom',
17 organization: '' 37 organization: ''
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 <setting-item> 9 <setting-item>
10 <n-radio-group @update:value="handleChecked" v-model:value="optionData.sourceType" name="radiogroup"> 10 <n-radio-group @update:value="handleChecked" v-model:value="optionData.sourceType" name="radiogroup">
11 <n-space> 11 <n-space>
12 - <n-radio v-for="(item, index) in sourceTypes" :key="item.value" :value="item.value"> 12 + <n-radio v-for="(item, index) in sourceTypes" :key="item.value + index" :value="item.value">
13 {{ item.label }} 13 {{ item.label }}
14 </n-radio> 14 </n-radio>
15 </n-space> 15 </n-space>
@@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
40 <setting-item> 40 <setting-item>
41 <n-select 41 <n-select
42 @update:value="handleSelect" 42 @update:value="handleSelect"
43 - v-model:value="optionData.dataset" 43 + v-model:value="url"
44 :options="videoOptions" 44 :options="videoOptions"
45 placeholder="请选择视频地址" 45 placeholder="请选择视频地址"
46 /> 46 />
@@ -56,21 +56,12 @@ @@ -56,21 +56,12 @@
56 56
57 <script setup lang="ts"> 57 <script setup lang="ts">
58 import { PropType, ref, onMounted } from 'vue' 58 import { PropType, ref, onMounted } from 'vue'
59 -import { option, sourceTypeEnum } from './config' 59 +import { AccessMode, option, sourceTypeEnum, videoListInterface } from './config'
60 import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' 60 import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
61 import { NTreeSelect } from 'naive-ui' 61 import { NTreeSelect } from 'naive-ui'
62 import { getOrganizationList, getVideoList, getVideoUrl } from '@/api/external/common/index' 62 import { getOrganizationList, getVideoList, getVideoUrl } from '@/api/external/common/index'
63 import { TKUpload } from '@/components/external/Common/TKUpload' 63 import { TKUpload } from '@/components/external/Common/TKUpload'
64 64
65 -interface videoListIF {  
66 - name: string  
67 - accessMode: number  
68 - id: string  
69 - videoUrl: string  
70 - label: string  
71 - value: string  
72 -}  
73 -  
74 const props = defineProps({ 65 const props = defineProps({
75 optionData: { 66 optionData: {
76 type: Object as PropType<typeof option>, 67 type: Object as PropType<typeof option>,
@@ -91,7 +82,9 @@ const sourceTypes = [ @@ -91,7 +82,9 @@ const sourceTypes = [
91 82
92 const originationOption = ref([]) 83 const originationOption = ref([])
93 84
94 -const videoOptions = ref<videoListIF[]>([]) 85 +const url = ref('')
  86 +
  87 +const videoOptions = ref<videoListInterface[]>([])
95 88
96 const getOriginationList = async () => { 89 const getOriginationList = async () => {
97 const res = await getOrganizationList() 90 const res = await getOrganizationList()
@@ -106,9 +99,9 @@ const handleUpdateTreeValue = (value: string) => { @@ -106,9 +99,9 @@ const handleUpdateTreeValue = (value: string) => {
106 const getVideoLists = async (organizationId: string) => { 99 const getVideoLists = async (organizationId: string) => {
107 const res = await getVideoList({ organizationId }) 100 const res = await getVideoList({ organizationId })
108 if (!res) return 101 if (!res) return
109 - videoOptions.value = res?.data?.map((item: videoListIF) => ({ 102 + videoOptions.value = res?.data?.map((item: videoListInterface) => ({
110 label: item.name, 103 label: item.name,
111 - value: item.accessMode === 1 ? item.id : item.videoUrl, 104 + value: item.accessMode === AccessMode.Streaming ? item.id : item.videoUrl,
112 id: item.id, 105 id: item.id,
113 accessMode: item.accessMode 106 accessMode: item.accessMode
114 })) 107 }))
@@ -118,7 +111,7 @@ const getVideoUrlById = async (id: string) => { @@ -118,7 +111,7 @@ const getVideoUrlById = async (id: string) => {
118 const res = await getVideoUrl(id) 111 const res = await getVideoUrl(id)
119 if (!res) return 112 if (!res) return
120 const { url } = res.data 113 const { url } = res.data
121 - props.optionData.url = url 114 + props.optionData.dataset = url
122 } 115 }
123 116
124 const handleChecked = (value: string) => { 117 const handleChecked = (value: string) => {
@@ -128,13 +121,13 @@ const handleChecked = (value: string) => { @@ -128,13 +121,13 @@ const handleChecked = (value: string) => {
128 } 121 }
129 } 122 }
130 123
131 -const handleSelect = (_: string, e: videoListIF) => {  
132 - const { accessMode, id } = e 124 +const handleSelect = (_: string, e: videoListInterface) => {
  125 + const { accessMode, id, value } = e
133 //1表示,需要从服务端调取接口换取播放的地址,0则不需要 126 //1表示,需要从服务端调取接口换取播放的地址,0则不需要
134 - if (accessMode === 1) { 127 + if (accessMode === AccessMode.Streaming) {
135 getVideoUrlById(id) 128 getVideoUrlById(id)
136 } else { 129 } else {
137 - props.optionData.url = '' 130 + props.optionData.dataset = value as string
138 } 131 }
139 } 132 }
140 133
1 <template> 1 <template>
2 <div> 2 <div>
3 - <VideoPlay :w="w" :h="h" :sourceSrc="option.dataset" :autoPlay="option.autoplay" :avatar="option.poster" /> 3 + <VideoPlay :baseSize="{ w, h }" :sourceSrc="option.dataset" :autoPlay="option.autoplay" :avatar="option.poster" />
4 </div> 4 </div>
5 </template> 5 </template>
6 <script setup lang="ts"> 6 <script setup lang="ts">
@@ -18,22 +18,18 @@ const props = defineProps({ @@ -18,22 +18,18 @@ const props = defineProps({
18 18
19 const { w, h } = toRefs(props.chartConfig.attr) 19 const { w, h } = toRefs(props.chartConfig.attr)
20 20
21 -const { autoplay, dataset, poster, url } = toRefs(props.chartConfig.option) 21 +const { autoplay, dataset, poster } = toRefs(props.chartConfig.option as typeof configOption)
22 22
23 const option = shallowReactive({ 23 const option = shallowReactive({
24 dataset: configOption.dataset, 24 dataset: configOption.dataset,
25 poster: configOption.poster, 25 poster: configOption.poster,
26 - autoplay:configOption.autoplay 26 + autoplay: configOption.autoplay
27 }) 27 })
28 28
29 watch( 29 watch(
30 () => dataset?.value, 30 () => dataset?.value,
31 (newData: string) => { 31 (newData: string) => {
32 - if (url?.value) {  
33 - option.dataset = url?.value  
34 - } else {  
35 - option.dataset = newData  
36 - } 32 + option.dataset = newData
37 }, 33 },
38 { 34 {
39 immediate: true 35 immediate: true
@@ -41,19 +37,10 @@ watch( @@ -41,19 +37,10 @@ watch(
41 ) 37 )
42 38
43 watch( 39 watch(
44 - () => poster?.value,  
45 - (newData: string) => {  
46 - option.poster = newData  
47 - },  
48 - {  
49 - immediate: true  
50 - }  
51 -)  
52 -  
53 -watch(  
54 - () => autoplay?.value,  
55 - (newData:boolean) => {  
56 - option.autoplay = newData 40 + () => [poster.value, autoplay.value],
  41 + newData => {
  42 + option.poster = newData.at(-2) as string
  43 + option.autoplay = newData.at(-1) as boolean
57 }, 44 },
58 { 45 {
59 immediate: true 46 immediate: true