forked from 77media/video-flow
196 lines
6.4 KiB
TypeScript
196 lines
6.4 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Input } from '@/components/ui/input';
|
||
import { AlertCircle, Loader2 } from 'lucide-react';
|
||
import { shareApiUrl } from '@/lib/env';
|
||
import { message } from 'antd';
|
||
|
||
interface VideoShareFormProps {
|
||
onCancel: () => void;
|
||
onShare: (videoUrl: string, videoName: string) => void;
|
||
error?: string | null;
|
||
platform?: string;
|
||
videoUrl?: string;
|
||
videoName?: string;
|
||
}
|
||
|
||
// 平台上传接口映射(统一使用小写 key)
|
||
const PLATFORM_UPLOAD_API: Record<string, string> = {
|
||
'youtube': '/api/video-share/youtube/upload',
|
||
'tiktok': '/api/video-share/tiktok/upload',
|
||
'reddit': '/api/video-share/reddit/upload',
|
||
'twitter': '/api/video-share/x/upload',
|
||
// Instagram 开发中,暂不支持
|
||
// 'instagram': '/api/video-share/instagram/upload',
|
||
};
|
||
|
||
export const VideoShareForm: React.FC<VideoShareFormProps> = ({
|
||
onCancel,
|
||
onShare,
|
||
error = null,
|
||
platform = '',
|
||
videoUrl: initialVideoUrl = '',
|
||
videoName: initialVideoName = ''
|
||
}) => {
|
||
const [videoUrl, setVideoUrl] = useState(initialVideoUrl);
|
||
const [videoName, setVideoName] = useState(initialVideoName);
|
||
const [uploading, setUploading] = useState(false);
|
||
|
||
/**
|
||
* 获取用户ID
|
||
*/
|
||
const getUserId = () => {
|
||
const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||
return currentUser.id || currentUser.userId;
|
||
};
|
||
|
||
/**
|
||
* 上传视频到指定平台
|
||
*/
|
||
const uploadVideoToPlatform = async (platformName: string, contentUrl: string, title: string) => {
|
||
const userId = getUserId();
|
||
|
||
if (!userId) {
|
||
message.error('用户ID不存在,请先登录');
|
||
return false;
|
||
}
|
||
|
||
// 统一转换成小写后查找 API 路径
|
||
const normalizedPlatform = platformName.toLowerCase().replace(/\s+/g, '').replace(/[()]/g, '');
|
||
const apiPath = PLATFORM_UPLOAD_API[normalizedPlatform];
|
||
if (!apiPath) {
|
||
message.error(`平台 ${platformName} 暂不支持或正在开发中`);
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`${shareApiUrl}${apiPath}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`,
|
||
'ngrok-skip-browser-warning': 'true'
|
||
},
|
||
body: JSON.stringify({
|
||
user_id: userId,
|
||
content_url: contentUrl,
|
||
title: title
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.successful && data.code === 0) {
|
||
message.success(`视频已成功上传到 ${platformName}!`);
|
||
return true;
|
||
} else {
|
||
message.error(data.message || `上传到 ${platformName} 失败`);
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
console.error(`上传到 ${platformName} 失败:`, error);
|
||
message.error(`上传失败:${error instanceof Error ? error.message : '网络错误'}`);
|
||
return false;
|
||
}
|
||
};
|
||
|
||
const handleShare = async () => {
|
||
if (!videoUrl || !videoName) {
|
||
message.warning('请填写完整的视频信息');
|
||
return;
|
||
}
|
||
|
||
setUploading(true);
|
||
try {
|
||
const success = await uploadVideoToPlatform(platform, videoUrl, videoName);
|
||
if (success) {
|
||
onShare(videoUrl, videoName);
|
||
}
|
||
} finally {
|
||
setUploading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div data-alt="video-share-form-container" className="backdrop-blur-lg bg-white/10 dark:bg-slate-900/30 rounded-lg p-6 shadow-lg">
|
||
<h2 data-alt="form-title" className="text-2xl font-bold mb-4 text-white">
|
||
转发视频{platform && ` - ${platform}`}
|
||
</h2>
|
||
|
||
{/* 错误信息显示 */}
|
||
{error && (
|
||
<div className="mb-4 p-3 bg-red-500/20 border border-red-500/50 rounded-lg flex items-start gap-2">
|
||
<AlertCircle className="w-5 h-5 text-red-400 flex-shrink-0 mt-0.5" />
|
||
<div className="flex-1">
|
||
<p className="text-red-200 text-sm font-medium">授权失败</p>
|
||
<p className="text-red-300 text-xs mt-1">{error}</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div data-alt="form-fields" className="space-y-4">
|
||
<div data-alt="video-url-field" className="flex flex-col gap-2">
|
||
<label htmlFor="videoUrl" className="text-sm font-medium text-white/90">视频链接</label>
|
||
<Input
|
||
id="videoUrl"
|
||
value={videoUrl}
|
||
onChange={(e) => setVideoUrl(e.target.value)}
|
||
placeholder="请输入视频链接"
|
||
className="bg-white/10 border-white/20 text-white placeholder:text-white/50"
|
||
disabled={!!error}
|
||
/>
|
||
</div>
|
||
<div data-alt="video-name-field" className="flex flex-col gap-2">
|
||
<label htmlFor="videoName" className="text-sm font-medium text-white/90">视频名称</label>
|
||
<Input
|
||
id="videoName"
|
||
value={videoName}
|
||
onChange={(e) => setVideoName(e.target.value)}
|
||
placeholder="请输入视频名称"
|
||
className="bg-white/10 border-white/20 text-white placeholder:text-white/50"
|
||
disabled={!!error}
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div data-alt="form-actions" className="mt-6 flex justify-end gap-2">
|
||
<Button
|
||
variant="outline"
|
||
onClick={onCancel}
|
||
data-alt="cancel-button"
|
||
className="border-white/30 text-white hover:bg-white/10"
|
||
disabled={uploading}
|
||
>
|
||
{error ? '关闭' : '取消'}
|
||
</Button>
|
||
{!error && (
|
||
<Button
|
||
onClick={handleShare}
|
||
data-alt="share-button"
|
||
className="
|
||
relative overflow-hidden
|
||
bg-gradient-to-r from-[#6AF4F9] to-[#C039F6]
|
||
hover:from-[#5ee3e8] hover:to-[#b028e5]
|
||
text-white font-medium
|
||
shadow-lg shadow-purple-500/30
|
||
hover:shadow-xl hover:shadow-purple-500/50
|
||
transition-all duration-300
|
||
disabled:opacity-50 disabled:cursor-not-allowed
|
||
disabled:shadow-none
|
||
"
|
||
disabled={!videoUrl || !videoName || uploading}
|
||
>
|
||
{uploading ? (
|
||
<>
|
||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||
上传中...
|
||
</>
|
||
) : (
|
||
'一键转发'
|
||
)}
|
||
</Button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|