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