forked from 77media/video-flow
Merge remote-tracking branch 'origin/dev' into prod
This commit is contained in:
commit
852de5e52f
@ -525,6 +525,8 @@ export default function SignupPage() {
|
||||
variant="outline"
|
||||
size="md"
|
||||
className="w-full"
|
||||
label="Sign up with Google"
|
||||
loadingLabel="Signing up..."
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@ -578,7 +580,7 @@ export default function SignupPage() {
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white text-center mb-2" data-alt="activation-title">Please verify your email to activate your account</h3>
|
||||
<p className="text-gray-300 text-center mb-4" data-alt="activation-desc">
|
||||
We have sent an activation email to {email || "your email"}; if you don't receive it, please check your spam folder or try again later
|
||||
We have sent an activation email to {email || "your email"}; if you don't receive it, please check your spam folder or try again later
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-center mb-4 gap-3">
|
||||
|
||||
@ -5,9 +5,10 @@ import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { CheckCircle, XCircle, Loader2, AlertTriangle } from "lucide-react";
|
||||
import type { OAuthCallbackParams } from "@/app/types/google-oauth";
|
||||
|
||||
// 根据接口文档定义响应类型
|
||||
// 根据后端实际返回格式定义响应类型
|
||||
interface GoogleOAuthResponse {
|
||||
success: boolean;
|
||||
code: number;
|
||||
message: string;
|
||||
data: {
|
||||
token: string;
|
||||
user: {
|
||||
@ -21,7 +22,7 @@ interface GoogleOAuthResponse {
|
||||
};
|
||||
message: string;
|
||||
};
|
||||
message?: string;
|
||||
successful: boolean;
|
||||
}
|
||||
|
||||
export default function OAuthCallback() {
|
||||
@ -117,7 +118,13 @@ export default function OAuthCallback() {
|
||||
console.log('Python OAuth接口响应状态:', response.status);
|
||||
const result: GoogleOAuthResponse = await response.json();
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
console.log('🔍 后端返回的完整数据:', result);
|
||||
|
||||
// 根据后端实际返回格式判断成功状态
|
||||
// 后端返回: { code: 0, successful: true } 表示成功
|
||||
const isSuccess = response.ok && result.successful && result.code === 0;
|
||||
|
||||
if (!isSuccess) {
|
||||
console.error('Python OAuth接口处理失败:', result);
|
||||
|
||||
// 处理常见错误码
|
||||
@ -129,21 +136,25 @@ export default function OAuthCallback() {
|
||||
throw new Error('Authentication service error. Please try again later.');
|
||||
}
|
||||
|
||||
throw new Error(result.message || 'Google OAuth failed');
|
||||
// 根据后端返回格式处理错误
|
||||
const errorMessage = result.message || `OAuth failed (code: ${result.code})`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
console.log('Google OAuth成功:', {
|
||||
console.log('🎉 Google OAuth成功:', {
|
||||
userId: result.data?.user?.userId,
|
||||
email: result.data?.user?.email,
|
||||
isNewUser: result.data?.user?.isNewUser
|
||||
isNewUser: result.data?.user?.isNewUser,
|
||||
code: result.code,
|
||||
successful: result.successful
|
||||
});
|
||||
|
||||
// 处理成功结果
|
||||
console.log('Google登录成功:', result);
|
||||
console.log('✅ Google登录成功,完整响应:', result);
|
||||
setStatus("success");
|
||||
setMessage(result.data.message || "Login successful! Redirecting to dashboard...");
|
||||
|
||||
// 根据接口文档的响应格式保存用户信息
|
||||
// 根据后端实际响应格式保存用户信息
|
||||
const { token, user } = result.data;
|
||||
|
||||
// 保存用户信息到localStorage
|
||||
@ -157,14 +168,20 @@ export default function OAuthCallback() {
|
||||
isNewUser: user.isNewUser
|
||||
};
|
||||
|
||||
console.log('💾 保存用户数据到 localStorage:', userData);
|
||||
localStorage.setItem('currentUser', JSON.stringify(userData));
|
||||
|
||||
if (token) {
|
||||
console.log('🔑 保存 token 到 localStorage');
|
||||
localStorage.setItem('token', token);
|
||||
}
|
||||
|
||||
console.log('⏰ 准备在2秒后跳转到 /movies');
|
||||
|
||||
// 2秒后跳转到主页
|
||||
setTimeout(() => {
|
||||
const returnUrl = '/movies';
|
||||
console.log('🚀 开始跳转到:', returnUrl);
|
||||
window.location.href = returnUrl;
|
||||
}, 2000);
|
||||
|
||||
|
||||
@ -261,8 +261,17 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
|
||||
left: (pathname === "/" || !isDesktop) ? "0" : (collapsed ? "2.5rem" : "16rem")
|
||||
}}
|
||||
>
|
||||
<div className="h-full flex items-center justify-between px-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="h-full flex items-center justify-between pr-4 md:px-4">
|
||||
<div className="flex items-center md:space-x-4">
|
||||
{pathname === "/" && (
|
||||
<button
|
||||
data-alt="mobile-menu-toggle"
|
||||
className="md:hidden text-white/90 p-[0.8rem]"
|
||||
onClick={() => window.dispatchEvent(new CustomEvent('home-menu-toggle'))}
|
||||
>
|
||||
☰
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
className={`flex items-center cursor-pointer space-x-1 link-logo roll event-on`}
|
||||
onClick={() => router.push("/")}
|
||||
|
||||
@ -217,6 +217,11 @@ export function HomePage2() {
|
||||
|
||||
const NavBar = () => {
|
||||
if (homeTabs.length === 0) return null;
|
||||
useEffect(() => {
|
||||
const handler = () => setMenuOpen((v) => !v);
|
||||
window.addEventListener('home-menu-toggle' as any, handler as any);
|
||||
return () => window.removeEventListener('home-menu-toggle' as any, handler as any);
|
||||
}, []);
|
||||
return (
|
||||
<div data-alt="home-navbar" className="fixed h-16 top-0 left-0 right-0 z-50">
|
||||
<div className="mx-auto h-full">
|
||||
@ -234,14 +239,8 @@ export function HomePage2() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{/* 移动端开关 */}
|
||||
<button
|
||||
data-alt="mobile-menu-toggle"
|
||||
className="md:hidden text-white/90 px-3 py-1 border border-white/20 rounded"
|
||||
onClick={() => setMenuOpen((v) => !v)}
|
||||
>
|
||||
☰
|
||||
</button>
|
||||
{/* 移动端开关移至 TopBar,保留占位对齐 */}
|
||||
<span className="md:hidden" data-alt="mobile-menu-toggle-placeholder"></span>
|
||||
</div>
|
||||
{/* 移动端下拉(仅三个项) */}
|
||||
{menuOpen && (
|
||||
@ -1396,9 +1395,9 @@ function HomeModule5() {
|
||||
|
||||
{/* 主要价格卡片 */}
|
||||
<div
|
||||
className="w-full max-w-[70%] mx-auto px-4
|
||||
className="w-full max-w-[88%] mx-auto px-4
|
||||
/* 移动端 - 单列布局 */
|
||||
grid grid-cols-1 gap-4
|
||||
grid grid-cols-1 gap-2
|
||||
/* 平板 - 双列布局 */
|
||||
sm:grid-cols-2 sm:gap-6 sm:px-6
|
||||
/* 桌面端 - 三列布局 */
|
||||
|
||||
@ -297,6 +297,8 @@ export default function Login() {
|
||||
variant="outline"
|
||||
size="md"
|
||||
className="w-full"
|
||||
label="Sign in with Google"
|
||||
loadingLabel="Signing in..."
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -164,7 +164,9 @@ export function H5MediaViewer({
|
||||
|
||||
// 渲染视频 slide
|
||||
const renderVideoSlides = () => (
|
||||
<div data-alt="carousel-wrapper" className="relative w-full aspect-auto min-h-[200px] overflow-hidden rounded-lg">
|
||||
<div data-alt="carousel-wrapper" className="relative w-full aspect-auto min-h-[200px] overflow-hidden rounded-lg" style={{
|
||||
height: 'calc(100vh - 20rem)',
|
||||
}}>
|
||||
<Carousel
|
||||
ref={carouselRef}
|
||||
key={`h5-carousel-video-${stage}-${videoUrls.length}`}
|
||||
@ -223,7 +225,7 @@ export function H5MediaViewer({
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full aspect-auto min-h-[200px] flex items-center justify-center bg-black/10 relative" data-alt="video-status" style={{
|
||||
maxHeight: 'calc(100vh - 20rem)',
|
||||
height: 'calc(100vh - 20rem)',
|
||||
}}>
|
||||
{status === 0 && (
|
||||
<span className="text-blue-500 text-base">Generating...</span>
|
||||
@ -248,7 +250,9 @@ export function H5MediaViewer({
|
||||
|
||||
// 渲染图片 slide
|
||||
const renderImageSlides = () => (
|
||||
<div data-alt="carousel-wrapper" className="relative w-full aspect-auto min-h-[200px] overflow-hidden rounded-lg">
|
||||
<div data-alt="carousel-wrapper" className="relative w-full aspect-auto min-h-[200px] overflow-hidden rounded-lg" style={{
|
||||
height: 'calc(100vh - 20rem)',
|
||||
}}>
|
||||
<Carousel
|
||||
ref={carouselRef}
|
||||
key={`h5-carousel-image-${stage}-${imageUrls.length}`}
|
||||
|
||||
@ -11,6 +11,10 @@ interface GoogleLoginButtonProps {
|
||||
className?: string;
|
||||
size?: "sm" | "md" | "lg";
|
||||
variant?: "default" | "outline";
|
||||
/** Button label when not loading */
|
||||
label?: string;
|
||||
/** Button label when loading */
|
||||
loadingLabel?: string;
|
||||
}
|
||||
|
||||
// Google logo SVG component matching the exact reference
|
||||
@ -83,6 +87,8 @@ export const GoogleLoginButton = React.forwardRef<
|
||||
className,
|
||||
size = "md",
|
||||
variant = "default",
|
||||
label = "Google Login",
|
||||
loadingLabel = "Signing in...",
|
||||
...props
|
||||
},
|
||||
ref
|
||||
@ -147,7 +153,7 @@ export const GoogleLoginButton = React.forwardRef<
|
||||
// Custom className
|
||||
className
|
||||
)}
|
||||
aria-label={loading ? "Signing in with Google" : "Google Login"}
|
||||
aria-label={loading ? loadingLabel : label}
|
||||
aria-disabled={isDisabled}
|
||||
role="button"
|
||||
tabIndex={isDisabled ? -1 : 0}
|
||||
@ -158,9 +164,7 @@ export const GoogleLoginButton = React.forwardRef<
|
||||
) : (
|
||||
<GoogleLogo className={sizeConfig.icon} />
|
||||
)}
|
||||
<span className="font-medium">
|
||||
{loading ? "Signing in..." : "Google Login"}
|
||||
</span>
|
||||
<span className="font-medium">{loading ? loadingLabel : label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
153
docs/oauth-callback-fix-summary.md
Normal file
153
docs/oauth-callback-fix-summary.md
Normal file
@ -0,0 +1,153 @@
|
||||
# OAuth 回调页面跳转问题修复总结
|
||||
|
||||
## 🚨 问题描述
|
||||
|
||||
用户反馈:调用后端 `api/oauth/google` 接口返回成功,但前端成功后没有跳转到 `/movies` 页面。
|
||||
|
||||
## 🔍 问题根因分析
|
||||
|
||||
### 后端实际返回格式:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
||||
"user": {
|
||||
"userId": "0a005fe7181e40baafdd80ce3c2ce98d",
|
||||
"userName": "Zixin Zhou",
|
||||
"name": "Zixin Zhou",
|
||||
"email": "codecaesar2020@gmail.com",
|
||||
"authType": "GOOGLE",
|
||||
"avatar": "https://lh3.googleusercontent.com/a/ACg8ocLS9E_7EIntgaxYFuczaU3laxooRltg2JIbnzc6osnANS8beL8=s96-c",
|
||||
"isNewUser": false
|
||||
},
|
||||
"message": "Google OAuth successful"
|
||||
},
|
||||
"successful": true
|
||||
}
|
||||
```
|
||||
|
||||
### 前端原来期望的格式:
|
||||
```typescript
|
||||
interface GoogleOAuthResponse {
|
||||
success: boolean; // ❌ 后端用的是 successful
|
||||
data: { ... };
|
||||
message?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 关键问题:
|
||||
1. **字段名不匹配**:后端用 `successful: true`,前端检查 `success`
|
||||
2. **状态码处理**:后端用 `code: 0` 表示成功,前端没有处理
|
||||
3. **成功判断逻辑错误**:导致成功的响应被当作失败处理
|
||||
|
||||
## 🔧 修复方案
|
||||
|
||||
### 1. 更新响应类型定义
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
interface GoogleOAuthResponse {
|
||||
success: boolean;
|
||||
data: { ... };
|
||||
message?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
interface GoogleOAuthResponse {
|
||||
code: number;
|
||||
message: string;
|
||||
data: {
|
||||
token: string;
|
||||
user: { ... };
|
||||
message: string;
|
||||
};
|
||||
successful: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修复成功判断逻辑
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
if (!response.ok || !result.success) {
|
||||
// 错误处理
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
// 根据后端实际返回格式判断成功状态
|
||||
// 后端返回: { code: 0, successful: true } 表示成功
|
||||
const isSuccess = response.ok && result.successful && result.code === 0;
|
||||
|
||||
if (!isSuccess) {
|
||||
// 错误处理
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 增强调试日志
|
||||
|
||||
添加了详细的调试日志来跟踪整个流程:
|
||||
- 后端返回的完整数据
|
||||
- 成功状态判断
|
||||
- localStorage 数据保存
|
||||
- 跳转准备和执行
|
||||
|
||||
## 📋 修改的文件
|
||||
|
||||
- **`app/users/oauth/callback/page.tsx`**
|
||||
- 更新了 `GoogleOAuthResponse` 接口定义
|
||||
- 修复了成功状态判断逻辑
|
||||
- 增加了详细的调试日志
|
||||
- 保持了2秒延迟跳转到 `/movies` 的逻辑
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
修复后的流程应该是:
|
||||
|
||||
1. **接收后端响应**:
|
||||
```
|
||||
🔍 后端返回的完整数据: { code: 0, successful: true, ... }
|
||||
```
|
||||
|
||||
2. **成功状态判断**:
|
||||
```
|
||||
🎉 Google OAuth成功: { userId: "...", email: "...", code: 0, successful: true }
|
||||
```
|
||||
|
||||
3. **数据保存**:
|
||||
```
|
||||
💾 保存用户数据到 localStorage: { userId: "...", userName: "...", ... }
|
||||
🔑 保存 token 到 localStorage
|
||||
```
|
||||
|
||||
4. **跳转准备**:
|
||||
```
|
||||
⏰ 准备在2秒后跳转到 /movies
|
||||
🚀 开始跳转到: /movies
|
||||
```
|
||||
|
||||
## 🎯 预期结果
|
||||
|
||||
修复后,当后端返回成功响应时:
|
||||
1. 前端能正确识别成功状态
|
||||
2. 用户数据和 token 正确保存到 localStorage
|
||||
3. 2秒后自动跳转到 `/movies` 页面
|
||||
4. 控制台显示详细的调试信息
|
||||
|
||||
## 🔄 回滚方案
|
||||
|
||||
如果修复后出现问题,可以:
|
||||
1. 恢复原来的接口定义
|
||||
2. 联系后端开发者统一响应格式
|
||||
3. 或者在前端做兼容性处理
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
- 这次修复是基于后端实际返回的数据格式
|
||||
- 如果后端响应格式发生变化,需要相应更新前端代码
|
||||
- 建议前后端团队统一 API 响应格式规范
|
||||
123
docs/workflow-auto-download-disabled.md
Normal file
123
docs/workflow-auto-download-disabled.md
Normal file
@ -0,0 +1,123 @@
|
||||
# Video-Flow 自动下载功能禁用说明
|
||||
|
||||
## 📋 修改概述
|
||||
|
||||
根据需求,已将 Video-Flow 工作流中的自动下载视频功能暂时禁用,用户需要手动下载生成的视频。
|
||||
|
||||
## 🔍 工作流程梳理
|
||||
|
||||
### 完整的 Work-Flow 自动调用剪辑接口流程
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[用户在 work-flow 页面] --> B{视频片段生成完成?}
|
||||
B -->|是| C[显示 AI 剪辑按钮]
|
||||
B -->|否| D[等待视频生成]
|
||||
|
||||
C --> E[用户点击 AI 剪辑按钮]
|
||||
E --> F[ai-editing-adapter.ts 处理]
|
||||
F --> G[调用 /api/export/ai-clips 接口]
|
||||
G --> H[执行 AI 剪辑处理]
|
||||
H --> I[生成最终视频]
|
||||
I --> J[返回视频 URL]
|
||||
J --> K[显示手动下载按钮]
|
||||
|
||||
style K fill:#90EE90
|
||||
style I fill:#FFE4B5
|
||||
```
|
||||
|
||||
### 涉及的核心文件
|
||||
|
||||
1. **`components/pages/work-flow.tsx`** - 主工作流页面
|
||||
2. **`components/pages/work-flow/ai-editing-button.tsx`** - AI 剪辑按钮组件
|
||||
3. **`components/pages/work-flow/ai-editing-adapter.ts`** - AI 剪辑适配器
|
||||
4. **`utils/export-service.ts`** - 导出服务(已禁用自动下载)
|
||||
5. **`app/api/export/ai-clips/route.ts`** - 剪辑接口
|
||||
6. **`app/api/export/download/[exportId]/route.ts`** - 下载接口
|
||||
|
||||
## 🚫 已禁用的自动下载功能
|
||||
|
||||
### 修改位置 1: `utils/export-service.ts` (第685-701行)
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
// 如果SSE中直接有完整结果,直接处理
|
||||
if (result?.download_url || result?.video_url) {
|
||||
const downloadUrl = result.download_url || result.video_url;
|
||||
console.log('📥 直接从SSE结果下载视频:', downloadUrl);
|
||||
await downloadVideo(downloadUrl); // 🚫 自动下载
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
// 如果SSE中直接有完整结果,直接处理
|
||||
if (result?.download_url || result?.video_url) {
|
||||
const downloadUrl = result.download_url || result.video_url;
|
||||
console.log('📥 直接从SSE结果获取视频URL:', downloadUrl);
|
||||
|
||||
// 🚫 暂时不支持自动下载 - 已注释
|
||||
// await downloadVideo(downloadUrl);
|
||||
}
|
||||
```
|
||||
|
||||
### 修改位置 2: `utils/export-service.ts` (第726-736行)
|
||||
|
||||
**修改前**:
|
||||
```typescript
|
||||
// 自动下载视频
|
||||
if (finalExportResult.video_url) {
|
||||
console.log('📥 开始下载视频:', finalExportResult.video_url);
|
||||
await downloadVideo(finalExportResult.video_url); // 🚫 自动下载
|
||||
console.log('✅ 视频下载完成');
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```typescript
|
||||
// 🚫 暂时不支持自动下载 - 已注释
|
||||
if (finalExportResult.video_url) {
|
||||
console.log('📥 视频导出完成,URL:', finalExportResult.video_url);
|
||||
// await downloadVideo(finalExportResult.video_url);
|
||||
console.log('✅ 视频导出完成,用户可手动下载');
|
||||
}
|
||||
```
|
||||
|
||||
## ✅ 保留的手动下载功能
|
||||
|
||||
以下手动下载功能仍然保留:
|
||||
|
||||
### 1. AI 剪辑完成后的下载按钮
|
||||
- **文件**: `components/pages/work-flow/ai-editing-button.tsx`
|
||||
- **位置**: 第284-298行
|
||||
- **功能**: AI 剪辑完成后显示下载按钮,用户可手动点击下载
|
||||
|
||||
### 2. 媒体查看器中的下载按钮
|
||||
- **文件**: `components/pages/work-flow/media-viewer.tsx`
|
||||
- **功能**: 用户可手动下载单个视频或所有视频
|
||||
|
||||
### 3. 项目列表中的下载按钮
|
||||
- **文件**: `components/pages/create-to-video2.tsx`
|
||||
- **功能**: 用户可在项目列表中手动下载最终视频
|
||||
|
||||
## 🔄 如何重新启用自动下载
|
||||
|
||||
如果将来需要重新启用自动下载功能,只需:
|
||||
|
||||
1. 在 `utils/export-service.ts` 中取消注释 `await downloadVideo(downloadUrl)` 行
|
||||
2. 将相关的 console.log 消息改回原来的描述
|
||||
3. 测试确保自动下载功能正常工作
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
- 自动下载功能的禁用不影响视频的生成和处理
|
||||
- 用户仍然可以通过手动点击下载按钮来下载视频
|
||||
- 所有的下载接口和相关功能都保持完整,只是不会自动触发
|
||||
- 这个修改是临时的,可以随时恢复
|
||||
|
||||
## 🧪 测试建议
|
||||
|
||||
1. 测试 AI 剪辑流程是否正常工作
|
||||
2. 确认剪辑完成后不会自动下载
|
||||
3. 验证手动下载按钮是否正常显示和工作
|
||||
4. 检查控制台日志确认修改生效
|
||||
@ -685,13 +685,15 @@ export class VideoExportService {
|
||||
// 如果SSE中直接有完整结果,直接处理
|
||||
if (result?.download_url || result?.video_url) {
|
||||
const downloadUrl = result.download_url || result.video_url;
|
||||
console.log('📥 直接从SSE结果下载视频:', downloadUrl);
|
||||
await downloadVideo(downloadUrl);
|
||||
|
||||
console.log('📥 直接从SSE结果获取视频URL:', downloadUrl);
|
||||
|
||||
// 🚫 暂时不支持自动下载 - 已注释
|
||||
// await downloadVideo(downloadUrl);
|
||||
|
||||
// notification.success({
|
||||
// message: '视频下载完成!',
|
||||
// description: result?.file_size
|
||||
// ? `文件大小: ${(result.file_size / 1024 / 1024).toFixed(2)}MB`
|
||||
// description: result?.file_size
|
||||
// ? `文件大小: ${(result.file_size / 1024 / 1024).toFixed(2)}MB`
|
||||
// : '视频已成功下载到本地',
|
||||
// placement: 'topRight',
|
||||
// duration: 8
|
||||
@ -721,16 +723,16 @@ export class VideoExportService {
|
||||
});
|
||||
*/
|
||||
|
||||
// 自动下载视频
|
||||
// 🚫 暂时不支持自动下载 - 已注释
|
||||
if (finalExportResult.video_url) {
|
||||
console.log('📥 开始下载视频:', finalExportResult.video_url);
|
||||
console.log('📥 视频导出完成,URL:', finalExportResult.video_url);
|
||||
console.log('📋 视频文件信息:', {
|
||||
url: finalExportResult.video_url,
|
||||
file_size: finalExportResult.file_size,
|
||||
quality_mode: finalExportResult.quality_mode
|
||||
});
|
||||
await downloadVideo(finalExportResult.video_url);
|
||||
console.log('✅ 视频下载完成');
|
||||
// await downloadVideo(finalExportResult.video_url);
|
||||
console.log('✅ 视频导出完成,用户可手动下载');
|
||||
}
|
||||
|
||||
// 清除缓存的请求数据
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user