diff --git a/app/api/video-share/x/auth/callback/page.tsx b/app/api/video-share/x/auth/callback/page.tsx deleted file mode 100644 index f74e61d..0000000 --- a/app/api/video-share/x/auth/callback/page.tsx +++ /dev/null @@ -1,71 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import TwitterCallbackModal from '@/components/common/TwitterCallbackModal'; - -export default function TwitterAuthCallbackPage() { - const router = useRouter(); - const [callbackParams, setCallbackParams] = useState<{ state: string; code: string } | null>(null); - const [project, setProject] = useState(null); - - useEffect(() => { - // 解析回调参数 - const searchParams = new URLSearchParams(window.location.search); - const state = searchParams.get('state'); - const code = searchParams.get('code'); - - if (state && code) { - setCallbackParams({ state, code }); - - // 从 localStorage 获取项目信息 - const storedProject = localStorage.getItem('currentShareProject'); - if (storedProject) { - try { - const parsedProject = JSON.parse(storedProject); - setProject(parsedProject); - } catch (error) { - console.error('解析项目信息失败:', error); - setProject({ project_id: '', name: '未知项目', final_video_url: '' }); - } - } else { - setProject({ project_id: '', name: '未知项目', final_video_url: '' }); - } - - // 清理 URL 参数 - const cleanUrl = window.location.pathname; - window.history.replaceState({}, document.title, cleanUrl); - } else { - // 如果没有参数,重定向到主页面 - router.push('/movies'); - } - }, [router]); - - const handleCloseModal = () => { - // 清理 localStorage 中的临时数据 - try { - localStorage.removeItem('twitterAuthCallbackPayload'); - localStorage.removeItem('twitterAuthCallbackFlag'); - } catch (error) { - console.error('清理 localStorage 失败:', error); - } - setCallbackParams(null); - router.push('/movies'); - }; - - return ( -
- {callbackParams && project && ( - - )} - {!callbackParams && ( -

处理 Twitter 授权回调...

- )} -
- ); -} diff --git a/components/common/ShareModal.tsx b/components/common/ShareModal.tsx index e13e006..a866f97 100644 --- a/components/common/ShareModal.tsx +++ b/components/common/ShareModal.tsx @@ -1,17 +1,16 @@ 'use client'; -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { Modal, Button, Spin, QRCode } from 'antd'; import { Youtube, Instagram, - Share2, + Share2, Copy, ExternalLink, Loader2 } from 'lucide-react'; -import { baseUrl } from '@/lib/env'; -import TwitterCallbackModal from './TwitterCallbackModal'; +import { shareApiUrl } from '@/lib/env'; interface ShareModalProps { /** 是否显示弹框 */ @@ -89,70 +88,6 @@ const sharePlatforms: SharePlatform[] = [ export default function ShareModal({ visible, onClose, project }: ShareModalProps) { const [loadingPlatform, setLoadingPlatform] = useState(null); - const [twitterCallbackVisible, setTwitterCallbackVisible] = useState(false); - const [twitterCallbackParams, setTwitterCallbackParams] = useState<{ - state: string; - code: string; - } | null>(null); - - /** - * 检查 URL 参数并处理 Twitter 回调 - */ - useEffect(() => { - if (typeof window !== 'undefined') { - const urlParams = new URLSearchParams(window.location.search); - const state = urlParams.get('state'); - const code = urlParams.get('code'); - - // 当前就在回调路径(同窗口授权) - if (state && code && window.location.pathname.includes('/api/video-share/x/auth/callback')) { - setTwitterCallbackParams({ state, code }); - setTwitterCallbackVisible(true); - const newUrl = window.location.pathname; - window.history.replaceState({}, document.title, newUrl); - return; - } - - // 新窗口授权,回到非回调路径,比如 /movies?twitterCallback=true - const hasFlag = urlParams.get('twitterCallback') === 'true'; - const cached = localStorage.getItem('twitterAuthCallbackPayload'); - if (hasFlag && cached) { - try { - const parsed = JSON.parse(cached); - if (parsed?.state && parsed?.code) { - setTwitterCallbackParams({ state: parsed.state, code: parsed.code }); - setTwitterCallbackVisible(true); - } - } catch { - - } - // 清理 URL 中的 twitterCallback 标记 - const cleanUrl = window.location.pathname; - window.history.replaceState({}, document.title, cleanUrl); - } - - // 监听 storage 事件(跨窗口通知) - const onStorage = (e: StorageEvent) => { - if (e.key === 'twitterAuthCallbackFlag') { - const cachedPayload = localStorage.getItem('twitterAuthCallbackPayload'); - if (cachedPayload) { - try { - const parsed = JSON.parse(cachedPayload); - if (parsed?.state && parsed?.code) { - setTwitterCallbackParams({ state: parsed.state, code: parsed.code }); - setTwitterCallbackVisible(true); - } - } catch { - - } - } - } - }; - window.addEventListener('storage', onStorage); - return () => window.removeEventListener('storage', onStorage); - } - }, []); - /** * 获取用户ID */ @@ -172,12 +107,7 @@ export default function ShareModal({ visible, onClose, project }: ShareModalProp return; } - // Twitter 平台保存项目信息到 localStorage - if (platformName.toLowerCase().includes('twitter') || platformName.toLowerCase().includes('x')) { - localStorage.setItem('currentShareProject', JSON.stringify(project)); - } - - const response = await fetch(`${baseUrl}${apiPath}?user_id=${userId}`, { + const response = await fetch(`${shareApiUrl}${apiPath}?user_id=${userId}`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -336,23 +266,6 @@ export default function ShareModal({ visible, onClose, project }: ShareModalProp - {/* Twitter 授权回调弹框 */} - {twitterCallbackParams && ( - { - setTwitterCallbackVisible(false); - setTwitterCallbackParams(null); - // 关闭时清理临时缓存 - try { - localStorage.removeItem('twitterAuthCallbackPayload'); - localStorage.removeItem('twitterAuthCallbackFlag'); - } catch {} - }} - project={project} - urlParams={twitterCallbackParams} - /> - )} ); } diff --git a/components/common/TwitterCallbackModal.tsx b/components/common/TwitterCallbackModal.tsx deleted file mode 100644 index 57f4121..0000000 --- a/components/common/TwitterCallbackModal.tsx +++ /dev/null @@ -1,218 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { Modal, Button, Spin, message } from 'antd'; -import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'; -import { Loader2 } from 'lucide-react'; -import { baseUrl } from '@/lib/env'; - -interface TwitterCallbackModalProps { - /** 是否显示弹框 */ - visible: boolean; - /** 关闭弹框回调 */ - onClose: () => void; - /** 项目信息 */ - project: { - project_id: string; - name?: string; - final_video_url?: string; - final_simple_video_url?: string; - }; - /** URL 参数 */ - urlParams: { - state: string; - code: string; - }; -} - -enum CallbackStatus { - LOADING = 'loading', - SUCCESS = 'success', - FAILED = 'failed' -} - -export default function TwitterCallbackModal({ - visible, - onClose, - project, - urlParams -}: TwitterCallbackModalProps) { - const [callbackStatus, setCallbackStatus] = useState(CallbackStatus.LOADING); - const [callbackData, setCallbackData] = useState(null); - - /** - * 处理 Twitter 授权回调 - */ - const handleTwitterCallback = async () => { - try { - setCallbackStatus(CallbackStatus.LOADING); - - // 从localStorage获取用户信息 - const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}'); - const userId = currentUser.id || currentUser.userId; - - if (!userId) { - throw new Error('用户ID不存在,请先登录'); - } - - // 调用 Twitter 授权回调接口 - const response = await fetch(`${baseUrl}/api/video-share/x/auth/callback?state=${encodeURIComponent(urlParams.state)}&code=${encodeURIComponent(urlParams.code)}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${localStorage.getItem('token') || ''}` - } - }); - - const data = await response.json(); - - if (data.successful && data.code === 0) { - setCallbackData(data.data); - setCallbackStatus(CallbackStatus.SUCCESS); - message.success('Twitter 授权成功!'); - } else { - throw new Error(data.message || 'Twitter 授权回调失败'); - } - } catch (error) { - console.error('Twitter 授权回调失败:', error); - setCallbackStatus(CallbackStatus.FAILED); - message.error(`Twitter 授权失败: ${error instanceof Error ? error.message : '未知错误'}`); - } - }; - - // 组件挂载时自动处理回调 - useEffect(() => { - if (visible && urlParams.state && urlParams.code) { - handleTwitterCallback(); - } - }, [visible, urlParams.state, urlParams.code]); - - /** - * 重新尝试授权 - */ - const handleRetry = () => { - setCallbackStatus(CallbackStatus.LOADING); - handleTwitterCallback(); - }; - - /** - * 关闭弹框 - */ - const handleClose = () => { - setCallbackStatus(CallbackStatus.LOADING); - setCallbackData(null); - onClose(); - }; - - return ( - -
- X -
- Twitter 授权回调 - - } - open={visible} - onCancel={handleClose} - footer={null} - width={480} - className="twitter-callback-modal" - styles={{ - content: { - backgroundColor: 'rgba(27, 27, 27, 0.8)', - backdropFilter: 'blur(20px)', - border: '1px solid rgba(255, 255, 255, 0.1)', - }, - header: { - backgroundColor: 'rgba(27, 27, 27, 0.6)', - backdropFilter: 'blur(20px)', - borderBottom: '1px solid rgba(255, 255, 255, 0.1)', - }, - body: { - backgroundColor: 'transparent', - } - }} - > -
- {/* 项目信息 */} -
-

- 分享视频: {project.name || 'Unnamed Project'} -

-

- 视频链接: {project.final_video_url || project.final_simple_video_url || 'N/A'} -

-
- - {/* 回调状态显示 */} -
- {callbackStatus === CallbackStatus.LOADING && ( - <> - -
-

正在处理 Twitter 授权...

-

请稍候,正在验证授权信息

-
- - )} - - {callbackStatus === CallbackStatus.SUCCESS && ( - <> - -
-

授权成功!

-

- Twitter 授权已完成,可以开始分享视频 -

- {callbackData && ( -
-

- 授权 Token: {callbackData.access_token ? '已获取' : '未获取'} -

-
- )} - -
- - )} - - {callbackStatus === CallbackStatus.FAILED && ( - <> - -
-

授权失败

-

- Twitter 授权处理失败,请重试 -

-
- - -
-
- - )} -
-
-
- ); -} diff --git a/components/common/VideoShareForm.tsx b/components/common/VideoShareForm.tsx new file mode 100644 index 0000000..d1b7a22 --- /dev/null +++ b/components/common/VideoShareForm.tsx @@ -0,0 +1,57 @@ +import React, { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; + +export const VideoShareForm: React.FC<{ onCancel: () => void; onShare: (videoUrl: string, videoName: string) => void }> = ({ onCancel, onShare }) => { + const [videoUrl, setVideoUrl] = useState(''); + const [videoName, setVideoName] = useState(''); + + const handleShare = () => { + onShare(videoUrl, videoName); + }; + + return ( +
+

转发视频

+
+
+ + setVideoUrl(e.target.value)} + placeholder="请输入视频链接" + className="bg-white/50 dark:bg-slate-800/50 border-slate-300 dark:border-slate-700 text-slate-900 dark:text-slate-100" + /> +
+
+ + setVideoName(e.target.value)} + placeholder="请输入视频名称" + className="bg-white/50 dark:bg-slate-800/50 border-slate-300 dark:border-slate-700 text-slate-900 dark:text-slate-100" + /> +
+
+
+ + +
+
+ ); +}; diff --git a/lib/env.ts b/lib/env.ts index 2064994..da020d3 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -17,6 +17,7 @@ export interface EnvConfig { javaUrl: string; cutUrl: string; cutUrlTo: string; + shareApiUrl: string; // 新增视频转发 URL 配置 // Google OAuth 配置 googleClientId: string; @@ -52,6 +53,7 @@ export const getEnvConfig = (): EnvConfig => { javaUrl: process.env.NEXT_PUBLIC_JAVA_URL || 'https://77.app.java.auth.qikongjian.com', cutUrl: process.env.NEXT_PUBLIC_CUT_URL || 'https://smartcut.api.movieflow.ai', cutUrlTo: process.env.NEXT_PUBLIC_CUT_URL_TO || 'https://smartcut.api.movieflow.ai', + shareApiUrl: process.env.NEXT_PUBLIC_SHARE_API_URL || 'http://39.97.48.225:8000', // Google OAuth 配置 googleClientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com', @@ -90,6 +92,7 @@ export const { javaUrl, cutUrl, cutUrlTo, + shareApiUrl, // 新增视频转发 URL 配置 // Google OAuth 配置 googleClientId,