添加视频帧提取功能和阿里云OSS存储支持
- 新增从视频素材提取首帧/尾帧的功能,支持画面连续性编辑 - 添加阿里云OSS存储支持,可配置本地或OSS存储方式 - 导入视频素材时自动探测并更新视频时长信息 - 前端添加从素材提取尾帧的UI界面 - 添加FramePrompt模型的数据库迁移 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import type {
|
||||
Asset,
|
||||
AssetCollection,
|
||||
AssetTag,
|
||||
CreateAssetRequest,
|
||||
ListAssetsParams,
|
||||
UpdateAssetRequest
|
||||
Asset,
|
||||
AssetCollection,
|
||||
AssetTag,
|
||||
CreateAssetRequest,
|
||||
ListAssetsParams,
|
||||
UpdateAssetRequest
|
||||
} from '../types/asset'
|
||||
import request from '../utils/request'
|
||||
|
||||
@@ -43,5 +43,9 @@ export const assetAPI = {
|
||||
|
||||
importFromVideo(videoGenId: number) {
|
||||
return request.post<Asset>(`/assets/import/video/${videoGenId}`)
|
||||
},
|
||||
|
||||
extractFrame(assetId: number, data: { position: string; storyboard_id: number; frame_type?: string }) {
|
||||
return request.post(`/assets/${assetId}/extract-frame`, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,24 @@
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<!-- 从素材提取尾帧 -->
|
||||
<div class="extract-frame-section" v-if="selectedFrameType === 'first'" style="margin: 12px 0; padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||
<div class="section-label" style="margin-bottom: 8px;">从素材提取尾帧(用于画面连续性)</div>
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<el-select v-model="selectedAssetForExtract" placeholder="选择视频素材" style="width: 300px;" size="small">
|
||||
<el-option
|
||||
v-for="asset in videoAssets"
|
||||
:key="asset.id"
|
||||
:label="`镜头 #${asset.storyboard_num || asset.id} - ${asset.name}`"
|
||||
:value="asset.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" size="small" :loading="extractingFrame" :disabled="!selectedAssetForExtract" @click="extractLastFrame">
|
||||
提取尾帧
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词区域 -->
|
||||
<div class="prompt-section">
|
||||
<div class="section-label">
|
||||
@@ -952,6 +970,8 @@ const generatingImage = ref(false)
|
||||
const generatedImages = ref<ImageGeneration[]>([])
|
||||
const isSwitchingFrameType = ref(false) // 标志位:是否正在切换帧类型
|
||||
const loadingImages = ref(false)
|
||||
const selectedAssetForExtract = ref<number | null>(null) // 选中的素材用于提取尾帧
|
||||
const extractingFrame = ref(false) // 是否正在提取尾帧
|
||||
let pollingTimer: any = null
|
||||
let pollingFrameType: FrameType | null = null // 记录正在轮询的帧类型
|
||||
|
||||
@@ -1603,6 +1623,35 @@ const generateFrameImage = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 从素材提取尾帧
|
||||
const extractLastFrame = async () => {
|
||||
if (!selectedAssetForExtract.value || !currentStoryboard.value) {
|
||||
ElMessage.warning('请先选择视频素材')
|
||||
return
|
||||
}
|
||||
|
||||
extractingFrame.value = true
|
||||
try {
|
||||
const result = await assetAPI.extractFrame(selectedAssetForExtract.value, {
|
||||
position: 'last',
|
||||
storyboard_id: currentStoryboard.value.id,
|
||||
frame_type: 'first' // 提取的尾帧用作当前分镜的首帧
|
||||
})
|
||||
|
||||
ElMessage.success('尾帧提取成功,已添加到首帧图片列表')
|
||||
|
||||
// 刷新图片列表
|
||||
await loadStoryboardImages(currentStoryboard.value!.id, 'first')
|
||||
|
||||
// 清空选择
|
||||
selectedAssetForExtract.value = null
|
||||
} catch (error: any) {
|
||||
ElMessage.error('提取失败: ' + (error.message || '未知错误'))
|
||||
} finally {
|
||||
extractingFrame.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusType = (status: string) => {
|
||||
const statusMap: Record<string, any> = {
|
||||
@@ -2154,7 +2203,13 @@ const handleTimelineSelect = (sceneId: number) => {
|
||||
}
|
||||
|
||||
const handleAddStoryboard = async () => {
|
||||
ElMessage.info('添加分镜功能开发中')
|
||||
if (!currentStoryboard.value) {
|
||||
ElMessage.warning('请先选择分镜')
|
||||
return
|
||||
}
|
||||
|
||||
// 调用现有的 generateVideo 函数为当前分镜生成视频
|
||||
await generateVideo()
|
||||
}
|
||||
|
||||
const togglePlay = () => {
|
||||
|
||||
Reference in New Issue
Block a user