检查文件是否存在
This commit is contained in:
parent
47b156c68a
commit
44a669b6fc
|
|
@ -81,6 +81,39 @@ pub async fn read_video_as_data_url(file_path: String) -> Result<String, String>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查文件是否存在
|
||||||
|
#[command]
|
||||||
|
pub async fn check_file_exists(file_path: String) -> Result<bool, String> {
|
||||||
|
let path = Path::new(&file_path);
|
||||||
|
Ok(path.exists())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取文件信息
|
||||||
|
#[command]
|
||||||
|
pub async fn get_file_info(file_path: String) -> Result<serde_json::Value, String> {
|
||||||
|
let path = Path::new(&file_path);
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
return Err("File does not exist".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
match fs::metadata(&file_path) {
|
||||||
|
Ok(metadata) => {
|
||||||
|
let info = serde_json::json!({
|
||||||
|
"exists": true,
|
||||||
|
"size": metadata.len(),
|
||||||
|
"is_file": metadata.is_file(),
|
||||||
|
"is_dir": metadata.is_dir(),
|
||||||
|
"modified": metadata.modified()
|
||||||
|
.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs())
|
||||||
|
.unwrap_or(0)
|
||||||
|
});
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
Err(e) => Err(format!("Failed to get file metadata: {}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取视频文件的blob URL(用于大文件)
|
/// 获取视频文件的blob URL(用于大文件)
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn get_video_blob_url(file_path: String) -> Result<String, String> {
|
pub async fn get_video_blob_url(file_path: String) -> Result<String, String> {
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,9 @@ pub fn run() {
|
||||||
commands::media::delete_original_video,
|
commands::media::delete_original_video,
|
||||||
commands::file_utils::read_image_as_data_url,
|
commands::file_utils::read_image_as_data_url,
|
||||||
commands::file_utils::read_video_as_data_url,
|
commands::file_utils::read_video_as_data_url,
|
||||||
commands::file_utils::get_video_blob_url
|
commands::file_utils::get_video_blob_url,
|
||||||
|
commands::file_utils::check_file_exists,
|
||||||
|
commands::file_utils::get_file_info
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost data:; media-src 'self' asset: http://asset.localhost; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
|
"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost data:; media-src 'self' asset: http://asset.localhost; video-src 'self' asset: http://asset.localhost; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
|
||||||
"assetProtocol": {
|
"assetProtocol": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"scope": ["**"]
|
"scope": ["**"]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useRef, useEffect } from 'react'
|
import React, { useState, useRef, useEffect } from 'react'
|
||||||
import { Play, Pause, Volume2, VolumeX, Maximize2, X } from 'lucide-react'
|
import { Play, Pause, Volume2, VolumeX, Maximize2, X, AlertCircle } from 'lucide-react'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc, invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
interface VideoPlayerProps {
|
interface VideoPlayerProps {
|
||||||
videoPath: string
|
videoPath: string
|
||||||
|
|
@ -23,14 +23,43 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||||
const [volume, setVolume] = useState(1)
|
const [volume, setVolume] = useState(1)
|
||||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
const [isFullscreen, setIsFullscreen] = useState(false)
|
||||||
const [videoSrc, setVideoSrc] = useState<string>('')
|
const [videoSrc, setVideoSrc] = useState<string>('')
|
||||||
|
const [fileExists, setFileExists] = useState<boolean>(true)
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string>('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen && videoPath) {
|
const checkFileAndSetSrc = async () => {
|
||||||
// 使用Tauri的convertFileSrc来获取安全的文件URL
|
if (isOpen && videoPath) {
|
||||||
const src = convertFileSrc(videoPath)
|
try {
|
||||||
console.log({ src })
|
// 首先检查文件是否存在
|
||||||
setVideoSrc(src)
|
const exists = await invoke<boolean>('check_file_exists', { filePath: videoPath })
|
||||||
|
console.log('File exists check:', { videoPath, exists })
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
setFileExists(false)
|
||||||
|
setErrorMessage(`文件不存在: ${videoPath}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件存在,转换为安全URL
|
||||||
|
const src = convertFileSrc(videoPath)
|
||||||
|
console.log('Video path conversion:', {
|
||||||
|
originalPath: videoPath,
|
||||||
|
convertedSrc: src,
|
||||||
|
fileExists: exists
|
||||||
|
})
|
||||||
|
|
||||||
|
setFileExists(true)
|
||||||
|
setErrorMessage('')
|
||||||
|
setVideoSrc(src)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking file:', error)
|
||||||
|
setFileExists(false)
|
||||||
|
setErrorMessage(`文件检查失败: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkFileAndSetSrc()
|
||||||
}, [isOpen, videoPath])
|
}, [isOpen, videoPath])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -146,15 +175,44 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||||
|
|
||||||
{/* 视频容器 */}
|
{/* 视频容器 */}
|
||||||
<div className="relative bg-black rounded-lg overflow-hidden">
|
<div className="relative bg-black rounded-lg overflow-hidden">
|
||||||
<video
|
{!fileExists || errorMessage ? (
|
||||||
ref={videoRef}
|
/* 错误显示 */
|
||||||
src={videoSrc}
|
<div className="flex items-center justify-center h-96 text-white">
|
||||||
className="w-full h-auto max-h-[70vh]"
|
<div className="text-center">
|
||||||
onClick={handlePlayPause}
|
<AlertCircle size={64} className="mx-auto mb-4 text-red-400" />
|
||||||
onError={(e) => {
|
<h3 className="text-xl font-medium mb-2">视频加载失败</h3>
|
||||||
console.error('Video error:', e)
|
<p className="text-gray-300 mb-4">{errorMessage}</p>
|
||||||
}}
|
<p className="text-sm text-gray-400">文件路径: {videoPath}</p>
|
||||||
/>
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
src={videoSrc}
|
||||||
|
className="w-full h-auto max-h-[70vh]"
|
||||||
|
onClick={handlePlayPause}
|
||||||
|
onError={(e) => {
|
||||||
|
console.error('Video loading error:', {
|
||||||
|
error: e,
|
||||||
|
videoSrc,
|
||||||
|
originalPath: videoPath,
|
||||||
|
currentTarget: e.currentTarget,
|
||||||
|
networkState: e.currentTarget.networkState,
|
||||||
|
readyState: e.currentTarget.readyState
|
||||||
|
})
|
||||||
|
setErrorMessage('视频加载失败,请检查文件格式和路径')
|
||||||
|
}}
|
||||||
|
onLoadStart={() => {
|
||||||
|
console.log('Video load started:', videoSrc)
|
||||||
|
}}
|
||||||
|
onCanPlay={() => {
|
||||||
|
console.log('Video can play:', videoSrc)
|
||||||
|
}}
|
||||||
|
onLoadedData={() => {
|
||||||
|
console.log('Video data loaded:', videoSrc)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 控制栏 */}
|
{/* 控制栏 */}
|
||||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4">
|
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue