230 lines
9.5 KiB
Java
230 lines
9.5 KiB
Java
package com.rj.service.impl;
|
||
|
||
import com.alibaba.fastjson.JSON;
|
||
import com.alibaba.fastjson.JSONObject;
|
||
import com.rj.dto.VideoSynthesisRequestDto;
|
||
import com.rj.entity.VideoSynthesisLog;
|
||
import com.rj.mapper.VideoSynthesisLogMapper;
|
||
import com.rj.service.IVideoSynthesisService;
|
||
import com.rj.utils.MinIOUrlGenerator;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.BeanUtils;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.beans.factory.annotation.Value;
|
||
import org.springframework.http.*;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.web.client.RestTemplate;
|
||
|
||
import java.time.LocalDateTime;
|
||
import java.util.UUID;
|
||
import java.util.concurrent.TimeUnit;
|
||
|
||
/**
|
||
* 视频合成服务实现类
|
||
*
|
||
* @author rj
|
||
* @date 2025-01-02
|
||
*/
|
||
@Slf4j
|
||
@Service
|
||
public class VideoSynthesisServiceImpl implements IVideoSynthesisService {
|
||
|
||
@Autowired
|
||
private VideoSynthesisLogMapper videoSynthesisLogMapper;
|
||
|
||
@Autowired
|
||
private RestTemplate restTemplate;
|
||
|
||
@Autowired
|
||
MinIOUrlGenerator minIOUrlGenerator ;
|
||
|
||
@Value("${dashscope.api.key}")
|
||
private String apiKey;
|
||
|
||
private static final String API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis/";
|
||
|
||
@Override
|
||
public VideoSynthesisLog synthesizeVideoAndSave(VideoSynthesisRequestDto request) {
|
||
LocalDateTime startTime = LocalDateTime.now();
|
||
String requestId = UUID.randomUUID().toString();
|
||
|
||
VideoSynthesisLog synthesisLog = new VideoSynthesisLog();
|
||
synthesisLog.setRequestId(requestId);
|
||
synthesisLog.setRequestTime(startTime);
|
||
synthesisLog.setSuccess(false);
|
||
|
||
// 使用BeanUtils复制相同名称的属性
|
||
BeanUtils.copyProperties(request, synthesisLog);
|
||
|
||
// 设置视频名称(如果请求中有提供)
|
||
if (request.getVideoName() != null && !request.getVideoName().trim().isEmpty()) {
|
||
synthesisLog.setVideoName(request.getVideoName());
|
||
log.info("设置视频名称: {}", request.getVideoName());
|
||
} else {
|
||
// 如果没有提供视频名称,使用默认名称
|
||
synthesisLog.setVideoName("视频合成_" + requestId.substring(0, 8));
|
||
log.info("使用默认视频名称: {}", synthesisLog.getVideoName());
|
||
}
|
||
|
||
// 设置图像区域坐标(如果请求中有提供)
|
||
if (request.getFaceBbox() != null && !request.getFaceBbox().trim().isEmpty()) {
|
||
synthesisLog.setFaceBbox(request.getFaceBbox());
|
||
log.info("设置图像区域坐标: {}", request.getFaceBbox());
|
||
}
|
||
|
||
// 设置动态区域坐标(如果请求中有提供)
|
||
if (request.getExtBbox() != null && !request.getExtBbox().trim().isEmpty()) {
|
||
synthesisLog.setExtBbox(request.getExtBbox());
|
||
log.info("设置动态区域坐标: {}", request.getExtBbox());
|
||
|
||
}
|
||
|
||
|
||
try {
|
||
log.info("开始视频合成,请求ID: {}, 图片URL: {}, 音频URL: {}",
|
||
requestId, request.getImageUrl(), request.getAudioUrl());
|
||
|
||
// 构建请求头
|
||
HttpHeaders headers = new HttpHeaders();
|
||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||
headers.set("X-DashScope-Async", "enable");
|
||
headers.set("Authorization", "Bearer " + apiKey);
|
||
request.buildParam();
|
||
// 构建请求体
|
||
HttpEntity<VideoSynthesisRequestDto> entity = new HttpEntity<>(request, headers);
|
||
log.info("开始视频合成,entity : {} ",entity);
|
||
// 发送请求
|
||
ResponseEntity<String> response = restTemplate.postForEntity(API_URL, entity, String.class);
|
||
|
||
LocalDateTime endTime = LocalDateTime.now();
|
||
synthesisLog.setResponseTime(endTime);
|
||
synthesisLog.setProcessingTimeMs(java.time.Duration.between(startTime, endTime).toMillis());
|
||
|
||
// 解析响应
|
||
if (response.getStatusCode() == HttpStatus.OK) {
|
||
// 直接解析JSON响应
|
||
String responseBody = response.getBody();
|
||
log.info("API响应: {}", responseBody);
|
||
|
||
// 解析JSON响应
|
||
if (responseBody != null && !responseBody.trim().isEmpty()) {
|
||
try {
|
||
// 使用fastjson解析JSON响应
|
||
JSONObject responseJson = JSON.parseObject(responseBody);
|
||
|
||
|
||
// 提取task_id
|
||
if (responseJson.containsKey("output")) {
|
||
JSONObject output = responseJson.getJSONObject("output");
|
||
if (output != null && output.containsKey("task_id")) {
|
||
String taskId = output.getString("task_id");
|
||
synthesisLog.setTaskId(taskId);
|
||
log.info("提取到任务ID: {}", taskId);
|
||
}
|
||
}
|
||
// 设置基本状态
|
||
synthesisLog.setTaskStatus("PENDING");
|
||
synthesisLog.setSuccess(true);
|
||
|
||
// 生成临时访问URL(7天有效期)
|
||
try {
|
||
log.info("即将用音频名称: {}, 生成临时访问URL", synthesisLog.getVideoName());
|
||
String tempUrl = generateTempVideoUrl(synthesisLog.getVideoName(), requestId);
|
||
synthesisLog.setVideoTempUrl(tempUrl);
|
||
log.info("生成临时访问URL: {}", tempUrl);
|
||
} catch (Exception e) {
|
||
log.warn("生成临时访问URL失败: {}", e.getMessage());
|
||
// 临时URL生成失败不影响主流程
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
log.error("解析API响应JSON失败: {}", e.getMessage(), e);
|
||
synthesisLog.setErrorMessage("解析API响应失败: " + e.getMessage());
|
||
synthesisLog.setSuccess(false);
|
||
}
|
||
} else {
|
||
synthesisLog.setErrorMessage("API响应为空");
|
||
synthesisLog.setSuccess(false);
|
||
}
|
||
|
||
log.info("视频合成请求成功,任务ID: {}, 状态: {}",
|
||
synthesisLog.getTaskId(), synthesisLog.getTaskStatus());
|
||
} else {
|
||
synthesisLog.setErrorMessage("HTTP请求失败,状态码: " + response.getStatusCode());
|
||
log.error("视频合成请求失败,状态码: {}, 响应: {}", response.getStatusCode(), response.getBody());
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
LocalDateTime endTime = LocalDateTime.now();
|
||
synthesisLog.setResponseTime(endTime);
|
||
synthesisLog.setProcessingTimeMs(java.time.Duration.between(startTime, endTime).toMillis());
|
||
synthesisLog.setErrorMessage("视频合成异常: " + e.getMessage());
|
||
log.error("视频合成失败: {}", e.getMessage(), e);
|
||
}
|
||
|
||
// 保存日志
|
||
saveVideoSynthesisLog(synthesisLog);
|
||
return synthesisLog;
|
||
}
|
||
|
||
@Override
|
||
public boolean saveVideoSynthesisLog(VideoSynthesisLog synthesisLog) {
|
||
try {
|
||
synthesisLog.setCreatedAt(LocalDateTime.now());
|
||
synthesisLog.setUpdatedAt(LocalDateTime.now());
|
||
|
||
int result = videoSynthesisLogMapper.insert(synthesisLog);
|
||
log.info("视频合成日志保存{}: 请求ID={}, 任务ID={}, 成功={}",
|
||
result > 0 ? "成功" : "失败", synthesisLog.getRequestId(), synthesisLog.getTaskId(), synthesisLog.getSuccess());
|
||
return result > 0;
|
||
} catch (Exception e) {
|
||
log.error("保存视频合成日志失败: {}", e.getMessage(), e);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public VideoSynthesisLog getVideoSynthesisLogByRequestId(String requestId) {
|
||
try {
|
||
return videoSynthesisLogMapper.selectOne(
|
||
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<VideoSynthesisLog>()
|
||
.eq("request_id", requestId)
|
||
);
|
||
} catch (Exception e) {
|
||
log.error("根据请求ID查询视频合成日志失败: {}", e.getMessage(), e);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public VideoSynthesisLog getVideoSynthesisLogByTaskId(String taskId) {
|
||
try {
|
||
return videoSynthesisLogMapper.selectOne(
|
||
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<VideoSynthesisLog>()
|
||
.eq("task_id", taskId)
|
||
);
|
||
} catch (Exception e) {
|
||
log.error("根据任务ID查询视频合成日志失败: {}", e.getMessage(), e);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成临时视频访问URL
|
||
*
|
||
* @param videoName 视频名称
|
||
* @param requestId 请求ID
|
||
* @return 临时访问URL
|
||
*/
|
||
private String generateTempVideoUrl(String videoName, String requestId) {
|
||
try {
|
||
|
||
return minIOUrlGenerator.generateTempUrl(videoName, 7, TimeUnit.DAYS).getUrl();
|
||
|
||
} catch (Exception e) {
|
||
log.error("生成临时视频URL失败: {}", e.getMessage(), e);
|
||
return null;
|
||
}
|
||
}
|
||
}
|