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

126 lines
3.1 KiB
TypeScript

import { Image } from 'expo-image'
import { memo, useEffect, useState } from 'react'
import { type ViewStyle } from 'react-native'
import Video from 'react-native-video'
import { videoUrlCache } from '@/utils/storage'
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
}
const setRedirectUrl = async (url?: string) => {
const isImg2 = isImg(url)
if (isImg2) {
setUrlFinal(url!)
return
}
try {
// 先尝试从缓存获取
const cachedUrl = await videoUrlCache.get(url!, width)
// console.log('getRedirectUrl cachedUrl-----------', url, cachedUrl)
if (cachedUrl) {
setUrlFinal(cachedUrl)
return
}
// 缓存未命中,进行网络请求
const webpUrl = createUrl(url!)
const finalUrl = await resolveRedirect(webpUrl)
// 缓存结果
await videoUrlCache.set(url!, finalUrl, width)
// console.log('setRedirectUrl finalUrl-----------', finalUrl)
setUrlFinal(finalUrl!)
} catch (error) {
console.warn('获取视频URL失败:', error)
// 错误时尝试使用原始URL
setUrlFinal(url!)
}
}
useEffect(() => {
if (!url) return
setRedirectUrl(url!)
// const finalUrl = createUrl(url)
return
}, [url])
if (!url) return null
// 本地文件全部用video组件播放
const isLocal = !url.startsWith('http://') && !url.startsWith('https://')
const isImageFile = isImg(url)
if (isLocal) {
if (!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 (
<Image
cachePolicy="memory-disk"
source={{ uri: url }}
style={style as any}
transition={{ duration: 200, effect: 'cross-dissolve' }}
/>
)
}
return (
// 移除 key 避免组件重建导致闪烁,使用 transition 实现平滑切换
<Image
// cachePolicy="memory-disk"
cachePolicy="disk"
source={{ uri: urlFinal }}
style={style as any}
transition={{ duration: 200, effect: 'cross-dissolve' }}
/>
)
}
export default memo(VideoBox)