Commit 52c75644 authored by yaoke.yk's avatar yaoke.yk

导出视频合集多fix

parent 8c8e78a2
......@@ -36,4 +36,16 @@ public interface AiTaskMapper extends BaseMapper<AiTask> {
LIMIT #{limit}
""")
List<AiTask> findPollableVideoTasks(int limit);
@Select("""
SELECT * FROM ai_tasks
WHERE storyboard_id = #{storyboardId}
AND tenant_id = #{tenantId}
AND status = 'succeeded'
AND result_video_url IS NOT NULL
AND result_video_url <> ''
ORDER BY created_at DESC
LIMIT 1
""")
AiTask findLatestSucceededVideoByStoryboard(Long storyboardId, Long tenantId);
}
......@@ -19,6 +19,21 @@ public interface ShotAssetMapper extends BaseMapper<ShotAsset> {
List<ShotAsset> findByStoryboard(@Param("storyboardId") Long storyboardId, @Param("tenantId") Long tenantId);
@Select("""
SELECT * FROM shot_assets
WHERE storyboard_id=#{storyboardId}
AND tenant_id=#{tenantId}
AND asset_type='video'
AND is_active=1
AND status='ready'
AND external_url IS NOT NULL
AND external_url <> ''
ORDER BY asset_version DESC, created_at DESC
LIMIT 1
""")
ShotAsset findActiveReadyVideoByStoryboard(@Param("storyboardId") Long storyboardId,
@Param("tenantId") Long tenantId);
@Select("""
SELECT a.* FROM shot_assets a
LEFT JOIN storyboards s ON a.storyboard_id = s.id
LEFT JOIN ai_tasks t ON a.source_task_id = t.id AND a.tenant_id = t.tenant_id
......
......@@ -4,10 +4,14 @@ import cn.dev33.satoken.stp.StpUtil;
import com.yaoai.billing.dto.BillingChargeRequest;
import com.yaoai.billing.service.BillingService;
import com.yaoai.common.context.UserContext;
import com.yaoai.domain.entity.AiTask;
import com.yaoai.domain.entity.AssemblyTask;
import com.yaoai.domain.entity.ShotAsset;
import com.yaoai.domain.entity.Storyboard;
import com.yaoai.domain.mapper.AiTaskMapper;
import com.yaoai.domain.mapper.AssemblyTaskMapper;
import com.yaoai.domain.mapper.ShotAssetMapper;
import com.yaoai.domain.mapper.StoryboardMapper;
import com.yaoai.media.service.FfmpegService;
import com.yaoai.pipeline.service.AssemblyPipelineService;
import com.yaoai.storage.service.TosService;
......@@ -19,6 +23,7 @@ import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
......@@ -28,7 +33,9 @@ import java.util.concurrent.Executor;
public class AssemblyPipelineServiceImpl implements AssemblyPipelineService {
private final AssemblyTaskMapper assemblyTaskMapper;
private final StoryboardMapper storyboardMapper;
private final ShotAssetMapper shotAssetMapper;
private final AiTaskMapper aiTaskMapper;
private final FfmpegService ffmpegService;
private final TosService tosService;
private final BillingService billingService;
......@@ -36,13 +43,17 @@ public class AssemblyPipelineServiceImpl implements AssemblyPipelineService {
public AssemblyPipelineServiceImpl(
AssemblyTaskMapper assemblyTaskMapper,
StoryboardMapper storyboardMapper,
ShotAssetMapper shotAssetMapper,
AiTaskMapper aiTaskMapper,
FfmpegService ffmpegService,
TosService tosService,
BillingService billingService,
@Qualifier("assemblyExecutor") Executor assemblyExecutor) {
this.assemblyTaskMapper = assemblyTaskMapper;
this.storyboardMapper = storyboardMapper;
this.shotAssetMapper = shotAssetMapper;
this.aiTaskMapper = aiTaskMapper;
this.ffmpegService = ffmpegService;
this.tosService = tosService;
this.billingService = billingService;
......@@ -90,19 +101,9 @@ public class AssemblyPipelineServiceImpl implements AssemblyPipelineService {
Path outputFile = null;
try {
List<ShotAsset> shots = shotAssetMapper.findActiveReadyVideosByEpisode(episodeId, task.getTenantId());
if (shots.isEmpty()) {
fail(task, "没有可用的视频片段(需先生成每个镜头的视频)");
return;
}
List<String> urls = shots.stream()
.map(ShotAsset::getExternalUrl)
.filter(u -> u != null && !u.isBlank())
.toList();
List<String> urls = buildAssemblyUrls(episodeId, task.getTenantId());
if (urls.isEmpty()) {
fail(task, "所有镜头的视频 URL 为空");
fail(task, "没有可用的视频片段(需先生成每个镜头的视频)");
return;
}
......@@ -142,6 +143,24 @@ public class AssemblyPipelineServiceImpl implements AssemblyPipelineService {
}
}
private List<String> buildAssemblyUrls(Long episodeId, Long tenantId) {
List<Storyboard> storyboards = storyboardMapper.findByEpisode(episodeId, tenantId);
List<String> urls = new ArrayList<>();
for (Storyboard storyboard : storyboards) {
AiTask latestTask = aiTaskMapper.findLatestSucceededVideoByStoryboard(storyboard.getId(), tenantId);
if (latestTask != null && latestTask.getResultVideoUrl() != null && !latestTask.getResultVideoUrl().isBlank()) {
urls.add(latestTask.getResultVideoUrl());
continue;
}
ShotAsset activeVideo = shotAssetMapper.findActiveReadyVideoByStoryboard(storyboard.getId(), tenantId);
if (activeVideo != null && activeVideo.getExternalUrl() != null && !activeVideo.getExternalUrl().isBlank()) {
urls.add(activeVideo.getExternalUrl());
}
}
return urls;
}
private void fail(AssemblyTask task, String msg) {
task.setStatus("failed");
task.setErrorMessage(msg);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment