Commit 207f6eea by lijiabin

feat: 视频上传前检测 H.265 编码并提示转码

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 4a583bc0
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
accept="video/*" accept="video/*"
class="upload-area" class="upload-area"
> >
<div v-if="!uploading && !videoInfo" class="upload-placeholder"> <div v-if="!uploading && !videoInfo && !codecChecking" class="upload-placeholder">
<el-icon class="upload-icon"><IEpVideoCamera /></el-icon> <el-icon class="upload-icon"><IEpVideoCamera /></el-icon>
<div class="upload-text"> <div class="upload-text">
<p>拖拽视频文件到此处,或点击选择文件</p> <p>拖拽视频文件到此处,或点击选择文件</p>
...@@ -20,6 +20,17 @@ ...@@ -20,6 +20,17 @@
</div> </div>
</div> </div>
<!-- 上传前的视频编码检测 -->
<div v-if="codecChecking" class="upload-progress">
<div class="progress-info">
<el-icon class="progress-icon is-loading"><IEpLoading /></el-icon>
<div class="progress-details">
<p class="file-name">{{ currentFile?.name }}</p>
<p class="progress-text">正在检测视频编码格式...</p>
</div>
</div>
</div>
<!-- 上传进度 --> <!-- 上传进度 -->
<div v-if="uploading" class="upload-progress"> <div v-if="uploading" class="upload-progress">
<div class="progress-info"> <div class="progress-info">
...@@ -71,13 +82,18 @@ ...@@ -71,13 +82,18 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="tsx">
import { uploadFile as uploadFileApi } from '@/api/common' import { uploadFile as uploadFileApi } from '@/api/common'
import type { UploadFile } from 'element-plus' import type { UploadFile } from 'element-plus'
import type { UploadVideoProps } from './types' import type { UploadVideoProps } from './types'
import { getVideoMetadata } from '@/utils' import { getVideoMetadata } from '@/utils'
import { useMediaInfo, useMessageBox } from '@/hooks'
import { push } from 'notivue' import { push } from 'notivue'
// 视频转码工具下载地址 安装包放在 public/downloads 下
// 测试环境打包带了 base 前缀 所以要拼 BASE_URL 不能写死根路径
const H265_TOOL_DOWNLOAD_URL = `${import.meta.env.BASE_URL}downloads/YswgToolbox_0.3.0_x64-setup.exe`
interface VideoInfo { interface VideoInfo {
url: string url: string
name: string name: string
...@@ -92,6 +108,7 @@ const { ...@@ -92,6 +108,7 @@ const {
maxSize = 1000, maxSize = 1000,
acceptFormats = ['mp4', 'avi', 'mov', 'wmv', 'flv'], acceptFormats = ['mp4', 'avi', 'mov', 'wmv', 'flv'],
btnClass = '', btnClass = '',
checkVideoCodec = true,
} = defineProps<UploadVideoProps>() } = defineProps<UploadVideoProps>()
const modelValue = defineModel<string>('modelValue', { required: true }) const modelValue = defineModel<string>('modelValue', { required: true })
const videoInfo = defineModel<VideoInfo | null>('videoInfo', { required: false, default: null }) const videoInfo = defineModel<VideoInfo | null>('videoInfo', { required: false, default: null })
...@@ -106,6 +123,12 @@ const uploading = ref(false) ...@@ -106,6 +123,12 @@ const uploading = ref(false)
const uploadProgress = ref(0) const uploadProgress = ref(0)
const uploadError = ref('') const uploadError = ref('')
const currentFile = ref<File | null>(null) const currentFile = ref<File | null>(null)
// 正在检测视频编码格式
const codecChecking = ref(false)
const { isH265Video, cleanup: cleanupMediaInfo } = useMediaInfo()
const { confirm } = useMessageBox()
onBeforeUnmount(cleanupMediaInfo)
// 文件大小格式化 // 文件大小格式化
const formatFileSize = (bytes: number): string => { const formatFileSize = (bytes: number): string => {
...@@ -138,12 +161,67 @@ const beforeUpload = (file: File): boolean => { ...@@ -138,12 +161,67 @@ const beforeUpload = (file: File): boolean => {
return true return true
} }
/**
* 上传前检测视频编码格式
* H.265(HEVC) 编码的视频浏览器普遍无法解码播放 上传上去也是放不了的
* 所以先提示用户去转成 H.264 再传 用户坚持的话也允许继续上传
* 返回 true 表示继续上传
*/
const confirmVideoCodec = async (file: File) => {
let isH265 = false
try {
codecChecking.value = true
isH265 = await isH265Video(file)
} catch (error) {
// 检测失败(如 wasm 加载不到)不阻塞上传 按原流程走
console.error('视频编码检测失败', error)
} finally {
codecChecking.value = false
}
if (!isH265) return true
try {
await confirm({
title: '视频编码格式提示',
type: 'warning',
confirmText: '仍然上传',
cancelText: '取消上传',
message: () => (
<span>
<span class="block mb-2">检测到该视频为 H.265(HEVC) 编码,部分浏览器可能无法正常播放。</span>
<span class="block mb-2">建议使用转码工具将视频转换为 H.264 编码后再重新上传。</span>
<a
href={H265_TOOL_DOWNLOAD_URL}
target="_blank"
rel="noopener"
class="text-[#7083FF] underline"
>
点击下载视频转码工具
</a>
</span>
),
})
return true
} catch {
return false
}
}
// 文件选择处理 - 选择后自动上传 // 文件选择处理 - 选择后自动上传
const handleFileChange = async (file: UploadFile) => { const handleFileChange = async (file: UploadFile) => {
if (!file.raw) return if (!file.raw) return
currentFile.value = file.raw currentFile.value = file.raw
// H.265 检测不通过且用户选择取消时 不进入上传
if (checkVideoCodec && !(await confirmVideoCodec(file.raw))) {
currentFile.value = null
// 清掉内部文件列表 否则再次选择同一个文件不会触发 change
uploadRef.value?.clearFiles()
return
}
await startUpload() await startUpload()
} }
...@@ -272,6 +350,7 @@ const retryUpload = () => { ...@@ -272,6 +350,7 @@ const retryUpload = () => {
const reset = () => { const reset = () => {
uploadRef.value = null uploadRef.value = null
uploading.value = false uploading.value = false
codecChecking.value = false
uploadProgress.value = 0 uploadProgress.value = 0
uploadError.value = '' uploadError.value = ''
currentFile.value = null currentFile.value = null
......
...@@ -2,4 +2,6 @@ export interface UploadVideoProps { ...@@ -2,4 +2,6 @@ export interface UploadVideoProps {
maxSize?: number // MB maxSize?: number // MB
acceptFormats?: string[] acceptFormats?: string[]
btnClass?: string btnClass?: string
/** 上传前是否检测视频编码 检测到 H.265(HEVC) 时提示用户先转成 H.264 默认开启 */
checkVideoCodec?: boolean
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment