expo-duooomi-app/@share/components/Video.tsx

111 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Image } from 'expo-image'
import { memo, useEffect, useState } from 'react'
import { type ViewStyle } from 'react-native'
import Video from 'react-native-video'
type Props = {
url?: string
needWeb?: boolean
style?: ViewStyle
width?: number
} & React.ComponentProps<typeof Video>
// 默认宽度256半屏宽度
const VideoBox = ({ url, needWeb = true, width = 256, style, ...videoProps }: Props) => {
const [urlFinal, setUrlFinal] = useState('')
const createUrl = (url: string) => {
return `https://modal-dev.bowong.cc/api/custom/video/converter/v2?media_url=${encodeURI(url)}&options=compression_level=3,quality=70,loop=true,resolution=${width}x${width},fps=24`
}
const isImg = (url: any) => {
if (!url) return false
const lowerUrl = url.toLowerCase()
return lowerUrl?.match(/\.(jpg|jpeg|png|gif|webp|bmp|tiff|svg)(\?.*)?$/i)
}
async function resolveRedirect(url: string) {
const res = await fetch(url, {
method: 'GET',
headers: {
Range: 'bytes=0-0',
},
})
return res.url
}
// 自动从 URL 提取稳定的缓存键
const extractCacheKey = (url: string): string => {
try {
const urlObj = new URL(url)
// 使用路径部分作为缓存键忽略查询参数token、签名等
return urlObj.pathname
} catch {
// URL 解析失败时使用原始 URL
return url
}
}
const setRedirectUrl = async (url?: string) => {
const isImg2 = isImg(url)
if (isImg2) {
setUrlFinal(url!)
return
}
const webpUrl = createUrl(url!)
const finalUrl = await resolveRedirect(webpUrl)
// console.log('setRedirectUrl finalUrl-----------', finalUrl)
setUrlFinal(finalUrl!)
}
useEffect(() => {
if (!url) return
setRedirectUrl(url!)
// const finalUrl = createUrl(url)
// console.log('finalUrl-----------', finalUrl)
// setUrlFinal(finalUrl)
return
}, [url])
// console.log('urlFinal--------- ', urlFinal, url)
if (!url) return null
// 本地文件全部用video组件播放
const isLocal = !url.startsWith('http://') && !url.startsWith('https://')
const isImageFile = isImg(url)
if (isLocal && !isImageFile) {
return (
<Video
key={url ?? 'no-url'}
muted
repeat
controls={false}
paused={false}
poster={url}
resizeMode="cover"
source={{ uri: url }}
style={style as any}
viewType={0}
volume={0}
{...videoProps}
/>
)
}
return (
// 移除 key 避免组件重建导致闪烁,使用 transition 实现平滑切换
<Image
cachePolicy="memory-disk"
source={{ uri: urlFinal, cacheKey: extractCacheKey(urlFinal) }}
style={style as any}
transition={{ duration: 200, effect: 'cross-dissolve' }}
/>
)
}
export default memo(VideoBox)