|
@@ -0,0 +1,357 @@
|
|
|
+package com.backendsys.modules.ai.volcengine.service.impl;
|
|
|
+
|
|
|
+import com.backendsys.exception.CustException;
|
|
|
+import com.backendsys.modules.ai.volcengine.dao.VolcengineVideoTaskDao;
|
|
|
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask;
|
|
|
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDTO;
|
|
|
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
|
|
|
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskNotify;
|
|
|
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoTaskDetailService;
|
|
|
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoTaskService;
|
|
|
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
|
|
|
+import com.backendsys.modules.upload.entity.SysFileResult;
|
|
|
+import com.backendsys.modules.upload.service.SysFileService;
|
|
|
+import com.backendsys.utils.response.PageEntity;
|
|
|
+import com.backendsys.utils.response.PageInfoResult;
|
|
|
+import com.backendsys.utils.v2.PageUtils;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.volcengine.ark.runtime.model.content.generation.CreateContentGenerationTaskRequest;
|
|
|
+import com.volcengine.ark.runtime.model.content.generation.CreateContentGenerationTaskResult;
|
|
|
+import com.volcengine.ark.runtime.model.content.generation.GetContentGenerationTaskRequest;
|
|
|
+import com.volcengine.ark.runtime.model.content.generation.GetContentGenerationTaskResponse;
|
|
|
+import com.volcengine.ark.runtime.service.ArkService;
|
|
|
+import okhttp3.ConnectionPool;
|
|
|
+import okhttp3.Dispatcher;
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class VolcengineVideoTaskServiceImpl extends ServiceImpl<VolcengineVideoTaskDao, VolcengineVideoTask> implements VolcengineVideoTaskService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private VolcengineVideoTaskDetailService volcengineVideoTaskDetailService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysFileService sysFileService;
|
|
|
+
|
|
|
+ @Value("${volcengine.api-key}")
|
|
|
+ private String apiKey;
|
|
|
+
|
|
|
+ @Value("${domain}")
|
|
|
+ private String domain;
|
|
|
+
|
|
|
+ private final ConnectionPool connectionPool = new ConnectionPool(20, 5, TimeUnit.MINUTES);
|
|
|
+ private final Dispatcher dispatcher = new Dispatcher();
|
|
|
+ private ArkService service;
|
|
|
+
|
|
|
+ @PostConstruct
|
|
|
+ public void init() {
|
|
|
+ this.service = ArkService.builder()
|
|
|
+ .dispatcher(dispatcher)
|
|
|
+ .connectionPool(connectionPool)
|
|
|
+ .apiKey(apiKey)
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public VolcengineVideoTask generateVideo(VolcengineVideoTask volcengineVideoTask) {
|
|
|
+ // 生成任务主表记录
|
|
|
+ this.baseMapper.insert(volcengineVideoTask);
|
|
|
+
|
|
|
+ List<CreateContentGenerationTaskRequest.Content> contents = new ArrayList<>();
|
|
|
+
|
|
|
+ // 图生视频功能 构建请求参数
|
|
|
+ // 文本提示词与参数组合
|
|
|
+ if (StringUtils.isBlank(volcengineVideoTask.getText())) {
|
|
|
+ volcengineVideoTask.setText("");
|
|
|
+ }
|
|
|
+ StringBuilder text = new StringBuilder(volcengineVideoTask.getText());
|
|
|
+ if(StringUtils.isNotBlank(volcengineVideoTask.getResolution())) {
|
|
|
+ text.append(" --rs ").append(volcengineVideoTask.getResolution());
|
|
|
+ }
|
|
|
+ if(volcengineVideoTask.getDuration() != null) {
|
|
|
+ text.append(" --dur ").append(volcengineVideoTask.getDuration());
|
|
|
+ }
|
|
|
+ if(volcengineVideoTask.getCamerafixed() != null) {
|
|
|
+ if (volcengineVideoTask.getCamerafixed() == 1) {
|
|
|
+ text.append(" --cf true");
|
|
|
+ } else {
|
|
|
+ text.append(" --cf false");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ contents.add(CreateContentGenerationTaskRequest.Content.builder()
|
|
|
+ .type("text")
|
|
|
+ .text(text.toString())
|
|
|
+ .build());
|
|
|
+
|
|
|
+ // 首帧图片 (若仅需使用文本生成视频功能,可将此部分内容进行注释处理。)
|
|
|
+ contents.add(CreateContentGenerationTaskRequest.Content.builder()
|
|
|
+ .type("image_url")
|
|
|
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
|
|
|
+ .url(volcengineVideoTask.getImg_url())
|
|
|
+ .build())
|
|
|
+ .build());
|
|
|
+ String notifyUrl = domain+ "/api/ai/volcengine/video/notify";
|
|
|
+ // 创建视频生成任务
|
|
|
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
|
|
|
+ .model(volcengineVideoTask.getModel())
|
|
|
+ .content(contents)
|
|
|
+ // 回调地址
|
|
|
+ .callbackUrl(notifyUrl)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ //根据数量生成任务,记录明细
|
|
|
+ List<VolcengineVideoTaskDetail> detailList = new ArrayList<>();
|
|
|
+ int num = 0;
|
|
|
+ while (volcengineVideoTask.getQuantity() > num) {
|
|
|
+
|
|
|
+ VolcengineVideoTaskDetail volcengineVideoTaskDetail = new VolcengineVideoTaskDetail();
|
|
|
+ volcengineVideoTaskDetail.setTask_id(volcengineVideoTask.getId());
|
|
|
+ volcengineVideoTaskDetail.setUser_id(volcengineVideoTask.getUser_id());
|
|
|
+ volcengineVideoTaskDetail.setCover_img_url(volcengineVideoTask.getImg_url());
|
|
|
+ volcengineVideoTaskDetail.setStatus("queued");
|
|
|
+ // 创建图生视频任务
|
|
|
+ CreateContentGenerationTaskResult taskResult = service.createContentGenerationTask(createRequest);
|
|
|
+
|
|
|
+ volcengineVideoTaskDetail.setVolcengine_task_id(taskResult.getId());
|
|
|
+ detailList.add(volcengineVideoTaskDetail);
|
|
|
+ num++;
|
|
|
+ }
|
|
|
+ if(!detailList.isEmpty()){
|
|
|
+ volcengineVideoTaskDetailService.saveBatch(detailList);
|
|
|
+ volcengineVideoTask.setDetail_list(detailList);
|
|
|
+ }
|
|
|
+
|
|
|
+ return volcengineVideoTask;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public VolcengineVideoTaskDetail selectTaskStatus(String volcengineTaskId) {
|
|
|
+
|
|
|
+ VolcengineVideoTaskDetail volcengineVideoTaskDetail = volcengineVideoTaskDetailService.getOne(Wrappers.<VolcengineVideoTaskDetail>lambdaQuery()
|
|
|
+ .eq(VolcengineVideoTaskDetail::getVolcengine_task_id, volcengineTaskId));
|
|
|
+ if(volcengineVideoTaskDetail == null) {
|
|
|
+ throw new CustException("找不到该图生视频信息,请检查参数是否有误");
|
|
|
+ }
|
|
|
+ // 成功或失败的直接返回
|
|
|
+ if("succeeded".equals(volcengineVideoTaskDetail.getStatus()) || "failed".equals(volcengineVideoTaskDetail.getStatus())) {
|
|
|
+ return volcengineVideoTaskDetail;
|
|
|
+ }
|
|
|
+
|
|
|
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
|
|
|
+ .taskId(volcengineTaskId)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ GetContentGenerationTaskResponse response = service.getContentGenerationTask(getRequest);
|
|
|
+ // 更新任务明细
|
|
|
+ volcengineVideoTaskDetail.setStatus(response.getStatus());
|
|
|
+ if(response.getSeed() != null) {
|
|
|
+ volcengineVideoTaskDetail.setSeed(response.getSeed().intValue());
|
|
|
+ }
|
|
|
+ if("succeeded".equals(response.getStatus())) {
|
|
|
+ // 成功,保存视频url
|
|
|
+ String videoOriginUrl = response.getContent().getVideoUrl();
|
|
|
+ volcengineVideoTaskDetail.setVideo_origin_url(videoOriginUrl);
|
|
|
+
|
|
|
+ // URL转存文件
|
|
|
+ SysFileResult sysFileResult = sysFileService.urlToUploadFile(videoOriginUrl, SecurityUtil.getUserId());
|
|
|
+ String objectKey = sysFileResult.getKey();
|
|
|
+ String videoUrl = sysFileResult.getDomain() + "/" + sysFileResult.getKey();
|
|
|
+ volcengineVideoTaskDetail.setVideo_url(videoUrl);
|
|
|
+ volcengineVideoTaskDetail.setObject_key(objectKey);
|
|
|
+ } else {
|
|
|
+ if(response.getError() !=null) {
|
|
|
+ volcengineVideoTaskDetail.setError_msg(response.getError().toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ volcengineVideoTaskDetailService.updateById(volcengineVideoTaskDetail);
|
|
|
+
|
|
|
+ return volcengineVideoTaskDetail;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PageEntity selectTaskList(VolcengineVideoTaskDTO dto) {
|
|
|
+ PageUtils.startPage();
|
|
|
+ List<VolcengineVideoTask> list = new ArrayList<>();
|
|
|
+ // 时间范围
|
|
|
+ if(dto.getDate_range() != null) {
|
|
|
+ String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
|
+ switch (dto.getDate_range()) {
|
|
|
+ case 0:
|
|
|
+ // 今天
|
|
|
+ dto.setCreate_begin_date(today);
|
|
|
+ dto.setCreate_end_date(today);
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ // 昨天
|
|
|
+ String yesterday = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
|
+ dto.setCreate_begin_date(yesterday);
|
|
|
+ dto.setCreate_end_date(yesterday);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ // 近3天
|
|
|
+ dto.setCreate_begin_date(LocalDate.now().minusDays(2).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
|
|
+ dto.setCreate_end_date(today);
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ // 近5天
|
|
|
+ dto.setCreate_begin_date(LocalDate.now().minusDays(4).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
|
|
+ dto.setCreate_end_date(today);
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ // 近7天
|
|
|
+ dto.setCreate_begin_date(LocalDate.now().minusDays(6).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
|
|
+ dto.setCreate_end_date(today);
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ // 近14天
|
|
|
+ dto.setCreate_begin_date(LocalDate.now().minusDays(13).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
|
|
+ dto.setCreate_end_date(today);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(dto.getOnly_collections() != null && dto.getOnly_collections() == 1){
|
|
|
+ // 只看收藏
|
|
|
+ list = baseMapper.selectTaskCollectList(dto);
|
|
|
+ } else {
|
|
|
+ list = baseMapper.selectTaskList(dto);
|
|
|
+ }
|
|
|
+ return new PageInfoResult(list).toEntity();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public VolcengineVideoTaskDetail regenerate(VolcengineVideoTaskDetail volcengineVideoTaskDetail) {
|
|
|
+
|
|
|
+ VolcengineVideoTaskDetail dataBean = volcengineVideoTaskDetailService.getById(volcengineVideoTaskDetail.getId());
|
|
|
+ if(dataBean == null) {
|
|
|
+ throw new CustException("任务不存在");
|
|
|
+ }
|
|
|
+ VolcengineVideoTask volcengineVideoTask = this.getById(dataBean.getTask_id());
|
|
|
+ if (volcengineVideoTask == null) {
|
|
|
+ throw new CustException("任务不存在");
|
|
|
+ }
|
|
|
+ if(!"failed".equals(dataBean.getStatus())) {
|
|
|
+ throw new CustException("只有失败状态可进行重试");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<CreateContentGenerationTaskRequest.Content> contents = new ArrayList<>();
|
|
|
+
|
|
|
+ // 图生视频功能 构建请求参数
|
|
|
+ // 文本提示词与参数组合
|
|
|
+ if (StringUtils.isBlank(volcengineVideoTask.getText())) {
|
|
|
+ volcengineVideoTask.setText("");
|
|
|
+ }
|
|
|
+ StringBuilder text = new StringBuilder(volcengineVideoTask.getText());
|
|
|
+ if(StringUtils.isNotBlank(volcengineVideoTask.getResolution())) {
|
|
|
+ text.append(" --rs ").append(volcengineVideoTask.getResolution());
|
|
|
+ }
|
|
|
+ if(volcengineVideoTask.getDuration() != null) {
|
|
|
+ text.append(" --dur ").append(volcengineVideoTask.getDuration());
|
|
|
+ }
|
|
|
+ if(volcengineVideoTask.getCamerafixed() != null) {
|
|
|
+ if (volcengineVideoTask.getCamerafixed() == 1) {
|
|
|
+ text.append(" --cf true");
|
|
|
+ } else {
|
|
|
+ text.append(" --cf false");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ contents.add(CreateContentGenerationTaskRequest.Content.builder()
|
|
|
+ .type("text")
|
|
|
+ .text(text.toString())
|
|
|
+ .build());
|
|
|
+
|
|
|
+
|
|
|
+ // 首帧图片 (若仅需使用文本生成视频功能,可将此部分内容进行注释处理。)
|
|
|
+ contents.add(CreateContentGenerationTaskRequest.Content.builder()
|
|
|
+ .type("image_url")
|
|
|
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
|
|
|
+ .url(volcengineVideoTask.getImg_url())
|
|
|
+ .build())
|
|
|
+ .build());
|
|
|
+
|
|
|
+ String notifyUrl = domain+ "/api/ai/volcengine/video/notify";
|
|
|
+ // 创建视频生成任务
|
|
|
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
|
|
|
+ .model(volcengineVideoTask.getModel())
|
|
|
+ .content(contents)
|
|
|
+ // 回调地址
|
|
|
+ .callbackUrl(notifyUrl)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ dataBean.setUser_id(volcengineVideoTaskDetail.getUser_id());
|
|
|
+ dataBean.setStatus("queued");
|
|
|
+ // 创建图生视频任务
|
|
|
+ CreateContentGenerationTaskResult taskResult = service.createContentGenerationTask(createRequest);
|
|
|
+
|
|
|
+ dataBean.setVolcengine_task_id(taskResult.getId());
|
|
|
+
|
|
|
+ volcengineVideoTaskDetailService.updateById(dataBean);
|
|
|
+
|
|
|
+ return dataBean;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void notify(VolcengineVideoTaskNotify notify) {
|
|
|
+
|
|
|
+ VolcengineVideoTaskDetail dataBean = volcengineVideoTaskDetailService.getOne(Wrappers.<VolcengineVideoTaskDetail>lambdaQuery()
|
|
|
+ .eq(VolcengineVideoTaskDetail::getVolcengine_task_id, notify.getId()));
|
|
|
+ if (dataBean == null) {
|
|
|
+ // 任务状态排队/运行中,可能速度快于平台数据库保存,可忽略,其他状态视为异常
|
|
|
+ if(!Arrays.asList("queued","running").contains(notify.getStatus())){
|
|
|
+ log.error("图生视频通知异常,找不到任务明细,通知内容:" + notify);
|
|
|
+ throw new CustException("图生视频通知异常,找不到任务明细");
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if("succeeded".equals(dataBean.getStatus()) || "failed".equals(dataBean.getStatus())) {
|
|
|
+ // 任务已经完成,不再处理
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 任务状态
|
|
|
+ dataBean.setStatus(notify.getStatus());
|
|
|
+
|
|
|
+ if(notify.getSeed() != null) {
|
|
|
+ dataBean.setSeed(notify.getSeed());
|
|
|
+ }
|
|
|
+
|
|
|
+ if("succeeded".equals(notify.getStatus())) {
|
|
|
+ // 成功
|
|
|
+ String videoOriginUrl = notify.getContent().getVideoUrl();
|
|
|
+ dataBean.setVideo_origin_url(videoOriginUrl);
|
|
|
+
|
|
|
+ // URL转存文件
|
|
|
+ SysFileResult sysFileResult = sysFileService.urlToUploadFile(videoOriginUrl, dataBean.getUser_id());
|
|
|
+ String objectKey = sysFileResult.getKey();
|
|
|
+ String videoUrl = sysFileResult.getDomain() + "/" + sysFileResult.getKey();
|
|
|
+ dataBean.setVideo_url(videoUrl);
|
|
|
+ dataBean.setObject_key(objectKey);
|
|
|
+ } else {
|
|
|
+ if(notify.getError() !=null) {
|
|
|
+ dataBean.setError_msg(notify.getError().toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 更新任务信息
|
|
|
+ volcengineVideoTaskDetailService.updateById(dataBean);
|
|
|
+ }
|
|
|
+}
|