{"version":3,"file":"index-yjE3oT3H.mjs","sources":["../../src/renderers/Video/index.tsx"],"sourcesContent":["import { useRef, useEffect, useState, forwardRef, useImperativeHandle } from 'react';\nimport videojs from 'video.js';\nimport 'video.js/dist/video-js.css';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\n\ntype VideoJsPlayer = ReturnType<typeof videojs>;\n\ninterface VideoRendererProps {\n  url: string;\n  fileName?: string;\n}\n\n// 浏览器原生不支持的视频容器（无论编码，<video> 都无法播放）\n// — 这些扩展名直接短路 videojs 初始化，给出明确提示\nconst BROWSER_UNSUPPORTED_EXTS = new Set(['avi', 'wmv', 'flv']);\n\n// 根据 URL 获取视频 MIME 类型\nconst getVideoType = (url: string): string => {\n  const ext = url.split('.').pop()?.toLowerCase().split('?')[0] || '';\n  const typeMap: Record<string, string> = {\n    mp4: 'video/mp4',\n    webm: 'video/webm',\n    ogg: 'video/ogg',\n    ogv: 'video/ogg',\n    mov: 'video/quicktime', // MOV 使用 QuickTime MIME 类型\n    avi: 'video/x-msvideo',\n    mkv: 'video/x-matroska',\n    m4v: 'video/mp4',\n    '3gp': 'video/3gpp',\n    flv: 'video/x-flv',\n  };\n  return typeMap[ext] || 'video/mp4';\n};\n\n// 获取视频文件扩展名（优先用文件名，blob/HTTP URL 都拿不到真扩展名）\nconst getVideoExt = (url: string, fileName?: string): string => {\n  const source = fileName || url;\n  return source.split('.').pop()?.toLowerCase().split('?')[0] || '';\n};\n\ninterface ErrorState {\n  title: string;\n  detail: string;\n}\n\nexport const VideoRenderer = forwardRef<RendererHandle, VideoRendererProps>(({ url, fileName }, ref) => {\n  const t = useTranslator();\n  const [error, setError] = useState<ErrorState | null>(null);\n  const [isLoading, setIsLoading] = useState(true);\n  const videoRef = useRef<HTMLDivElement>(null);\n  const playerRef = useRef<VideoJsPlayer | null>(null);\n\n  useEffect(() => {\n    const videoExt = getVideoExt(url, fileName);\n\n    // 已知浏览器不支持的容器：跳过 videojs 初始化，直接展示友好提示\n    if (BROWSER_UNSUPPORTED_EXTS.has(videoExt)) {\n      setError({\n        title: t('video.unsupported_title'),\n        detail: t('video.unsupported_detail', { format: videoExt.toUpperCase() }),\n      });\n      setIsLoading(false);\n      return;\n    }\n\n    // 确保 Video.js 播放器只初始化一次\n    if (!playerRef.current && videoRef.current) {\n      const videoElement = document.createElement('video-js');\n      videoElement.classList.add('vjs-big-play-centered', 'vjs-theme-apple');\n      videoRef.current.appendChild(videoElement);\n\n      const videoType = getVideoType(url);\n\n      // 为特定格式提供多个 MIME 类型作为备用\n      let sources: Array<{ src: string; type: string }>;\n\n      if (videoType === 'video/quicktime') {\n        // MOV 格式 fallback\n        sources = [\n          { src: url, type: 'video/quicktime' },\n          { src: url, type: 'video/mp4' }\n        ];\n      } else {\n        sources = [{ src: url, type: videoType }];\n      }\n\n      const player = videojs(videoElement, {\n        controls: true,\n        fill: true,\n        preload: 'auto',\n        controlBar: {\n          children: [\n            'playToggle',\n            'volumePanel',\n            'currentTimeDisplay',\n            'timeDivider',\n            'durationDisplay',\n            'progressControl',\n            'remainingTimeDisplay',\n            'fullscreenToggle'\n          ],\n          volumePanel: {\n            inline: false\n          }\n        },\n        html5: {\n          vhs: {\n            overrideNative: true\n          },\n          nativeVideoTracks: false,\n          nativeAudioTracks: false,\n          nativeTextTracks: false\n        },\n        sources\n      });\n\n      // 确保视频保持比例\n      const videoEl = player.el().querySelector('video');\n      if (videoEl) {\n        (videoEl as HTMLVideoElement).style.objectFit = 'contain';\n      }\n\n      // 监听加载完成\n      player.on('loadeddata', () => {\n        setIsLoading(false);\n      });\n\n      player.on('error', () => {\n        const err = player.error();\n        // 视频加载错误是正常情况（格式不支持、编解码器缺失等），用 warn 级别记录\n        console.warn('[VideoRenderer] Video playback error:', err?.message || 'Unknown error');\n\n        // MEDIA_ERR_SRC_NOT_SUPPORTED（code=4）：编码或容器层面浏览器解不了\n        if (err?.code === 4) {\n          setError({\n            title: t('video.unsupported_title'),\n            detail: t('video.unsupported_detail', {\n              format: videoExt ? videoExt.toUpperCase() : t('common.unknown_error'),\n            }),\n          });\n        } else {\n          setError({\n            title: t('video.load_failed'),\n            detail: err?.message || t('common.unknown_error'),\n          });\n        }\n        setIsLoading(false);\n      });\n\n      playerRef.current = player;\n    }\n  }, [url, fileName, t]);\n\n  // 清理函数\n  useEffect(() => {\n    const player = playerRef.current;\n\n    return () => {\n      if (player && !player.isDisposed()) {\n        player.dispose();\n        playerRef.current = null;\n      }\n    };\n  }, []);\n\n  // 暴露接口给父组件（必须在所有提前 return 之前）\n  useImperativeHandle(ref, () => ({\n    getToolbarGroups: () => [],\n  }), []);\n\n  if (error) {\n    return <RendererError message={error.title} detail={error.detail} />;\n  }\n\n  return (\n    <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n      <div className=\"rfp-w-full rfp-h-full rfp-relative\">\n        {/* 加载状态 */}\n        {isLoading && (\n          <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-3 rfp-backdrop-blur-sm rfp-z-10\">\n            <div className=\"rfp-text-center\">\n              <div className=\"rfp-w-12 rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-3 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n              <p className=\"rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('video.loading')}</p>\n            </div>\n          </div>\n        )}\n\n        {/* 视频播放器容器 */}\n        <div\n          ref={videoRef}\n          className=\"rfp-overflow-hidden rfp-w-full rfp-h-full [&_video]:rfp-object-contain\"\n          style={{\n            boxShadow: '0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05)'\n          }}\n        />\n      </div>\n    </div>\n  );\n});\n"],"names":["BROWSER_UNSUPPORTED_EXTS","getVideoType","url","ext","_a","getVideoExt","fileName","VideoRenderer","forwardRef","ref","t","useTranslator","error","setError","useState","isLoading","setIsLoading","videoRef","useRef","playerRef","useEffect","videoExt","videoElement","videoType","sources","player","videojs","videoEl","err","useImperativeHandle","RendererError","jsxs","jsx"],"mappings":"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"}