404 lines
18 KiB
Java
404 lines
18 KiB
Java
package com.rj.controller;
|
||
|
||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||
import com.rj.dto.VideoSynthesisRequestDto;
|
||
import com.rj.entity.VideoSynthesisLog;
|
||
import com.rj.mapper.VideoSynthesisLogMapper;
|
||
import com.rj.service.IVideoSynthesisService;
|
||
import com.rj.service.MinIOService;
|
||
import io.swagger.v3.oas.annotations.Operation;
|
||
import io.swagger.v3.oas.annotations.Parameter;
|
||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.http.ResponseEntity;
|
||
import org.springframework.web.bind.annotation.*;
|
||
|
||
import jakarta.validation.Valid;
|
||
import java.util.HashMap;
|
||
import java.util.Map;
|
||
|
||
/**
|
||
* 视频合成控制器
|
||
*
|
||
* @author rj
|
||
* @date 2025-01-02
|
||
*/
|
||
@Slf4j
|
||
@RestController
|
||
@RequestMapping("/api/video-synthesis")
|
||
@Tag(name = "视频合成,根据头像和音频生成视频", description = "视频合成相关接口,根据头像和音频生成视频")
|
||
public class VideoSynthesisFromController {
|
||
|
||
@Autowired
|
||
private IVideoSynthesisService videoSynthesisService;
|
||
|
||
@Autowired
|
||
private VideoSynthesisLogMapper videoSynthesisLogMapper;
|
||
|
||
@Autowired
|
||
private MinIOService minIOService;
|
||
|
||
/**
|
||
* 视频合成接口
|
||
*/
|
||
@PostMapping("/synthesize")
|
||
@Operation(summary = "视频合成", description = "根据图片和音频生成视频")
|
||
public ResponseEntity<Map<String, Object>> synthesizeVideo(@Valid @RequestBody VideoSynthesisRequestDto requestDto) {
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
log.info("开始视频合成,图片URL: {}, 音频URL: {}", requestDto.getImageUrl(), requestDto.getAudioUrl());
|
||
|
||
// 执行视频合成
|
||
VideoSynthesisLog synthesisLog = videoSynthesisService.synthesizeVideoAndSave(requestDto);
|
||
|
||
// 构建响应结果
|
||
result.put("success", synthesisLog.getSuccess());
|
||
result.put("message", synthesisLog.getTaskStatus());
|
||
result.put("requestId", synthesisLog.getRequestId());
|
||
result.put("taskId", synthesisLog.getTaskId());
|
||
result.put("taskStatus", synthesisLog.getTaskStatus());
|
||
result.put("videoUrl", synthesisLog.getVideoUrl());
|
||
result.put("videoDuration", synthesisLog.getVideoDuration());
|
||
result.put("videoRatio", synthesisLog.getVideoRatio());
|
||
result.put("processingTimeMs", synthesisLog.getProcessingTimeMs());
|
||
result.put("imageId", synthesisLog.getImageId());
|
||
result.put("audioId", synthesisLog.getAudioId());
|
||
result.put("ownerName", synthesisLog.getOwnerName());
|
||
result.put("ownerPhone", synthesisLog.getOwnerPhone());
|
||
result.put("modelProvider", synthesisLog.getModelProvider());
|
||
|
||
if (!synthesisLog.getSuccess()) {
|
||
result.put("errorMessage", synthesisLog.getErrorMessage());
|
||
return ResponseEntity.badRequest().body(result);
|
||
}
|
||
|
||
return ResponseEntity.ok(result);
|
||
|
||
} catch (Exception e) {
|
||
log.error("视频合成失败: {}", e.getMessage(), e);
|
||
result.put("success", false);
|
||
result.put("message", "视频合成失败: " + e.getMessage());
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据请求ID查询视频合成结果
|
||
*/
|
||
@GetMapping("/result/{requestId}")
|
||
@Operation(summary = "查询合成结果", description = "根据请求ID查询视频合成结果")
|
||
public ResponseEntity<Map<String, Object>> getSynthesisResult(
|
||
@Parameter(description = "请求ID", required = true)
|
||
@PathVariable String requestId) {
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
VideoSynthesisLog synthesisLog = videoSynthesisService.getVideoSynthesisLogByRequestId(requestId);
|
||
|
||
if (synthesisLog == null) {
|
||
result.put("success", false);
|
||
result.put("message", "未找到对应的视频合成记录");
|
||
return ResponseEntity.notFound().build();
|
||
}
|
||
|
||
result.put("success", true);
|
||
result.put("message", "查询成功");
|
||
result.put("requestId", synthesisLog.getRequestId());
|
||
result.put("taskId", synthesisLog.getTaskId());
|
||
result.put("taskStatus", synthesisLog.getTaskStatus());
|
||
result.put("videoUrl", synthesisLog.getVideoUrl());
|
||
result.put("videoDuration", synthesisLog.getVideoDuration());
|
||
result.put("videoRatio", synthesisLog.getVideoRatio());
|
||
result.put("processingTimeMs", synthesisLog.getProcessingTimeMs());
|
||
result.put("requestTime", synthesisLog.getRequestTime());
|
||
result.put("responseTime", synthesisLog.getResponseTime());
|
||
result.put("imageId", synthesisLog.getImageId());
|
||
result.put("audioId", synthesisLog.getAudioId());
|
||
result.put("ownerName", synthesisLog.getOwnerName());
|
||
result.put("ownerPhone", synthesisLog.getOwnerPhone());
|
||
result.put("modelProvider", synthesisLog.getModelProvider());
|
||
|
||
if (!synthesisLog.getSuccess()) {
|
||
result.put("errorMessage", synthesisLog.getErrorMessage());
|
||
}
|
||
|
||
return ResponseEntity.ok(result);
|
||
|
||
} catch (Exception e) {
|
||
log.error("查询视频合成结果失败: {}", e.getMessage(), e);
|
||
result.put("success", false);
|
||
result.put("message", "查询失败: " + e.getMessage());
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据任务ID查询视频合成结果
|
||
*/
|
||
@GetMapping("/result/task/{taskId}")
|
||
@Operation(summary = "根据任务ID查询结果", description = "根据任务ID查询视频合成结果")
|
||
public ResponseEntity<Map<String, Object>> getSynthesisResultByTaskId(
|
||
@Parameter(description = "任务ID", required = true)
|
||
@PathVariable String taskId) {
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
VideoSynthesisLog synthesisLog = videoSynthesisService.getVideoSynthesisLogByTaskId(taskId);
|
||
|
||
if (synthesisLog == null) {
|
||
result.put("success", false);
|
||
result.put("message", "未找到对应的视频合成记录");
|
||
return ResponseEntity.notFound().build();
|
||
}
|
||
|
||
result.put("success", true);
|
||
result.put("message", "查询成功");
|
||
result.put("requestId", synthesisLog.getRequestId());
|
||
result.put("taskId", synthesisLog.getTaskId());
|
||
result.put("taskStatus", synthesisLog.getTaskStatus());
|
||
result.put("videoUrl", synthesisLog.getVideoUrl());
|
||
result.put("videoDuration", synthesisLog.getVideoDuration());
|
||
result.put("videoRatio", synthesisLog.getVideoRatio());
|
||
result.put("processingTimeMs", synthesisLog.getProcessingTimeMs());
|
||
result.put("requestTime", synthesisLog.getRequestTime());
|
||
result.put("responseTime", synthesisLog.getResponseTime());
|
||
result.put("imageId", synthesisLog.getImageId());
|
||
result.put("audioId", synthesisLog.getAudioId());
|
||
result.put("ownerName", synthesisLog.getOwnerName());
|
||
result.put("ownerPhone", synthesisLog.getOwnerPhone());
|
||
result.put("modelProvider", synthesisLog.getModelProvider());
|
||
|
||
if (!synthesisLog.getSuccess()) {
|
||
result.put("errorMessage", synthesisLog.getErrorMessage());
|
||
}
|
||
|
||
return ResponseEntity.ok(result);
|
||
|
||
} catch (Exception e) {
|
||
log.error("根据任务ID查询视频合成结果失败: {}", e.getMessage(), e);
|
||
result.put("success", false);
|
||
result.put("message", "查询失败: " + e.getMessage());
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 分页查询视频合成日志列表
|
||
*/
|
||
@GetMapping("/list")
|
||
@Operation(summary = "查询视频合成日志列表", description = "分页查询视频合成日志")
|
||
public ResponseEntity<Map<String, Object>> getVideoSynthesisLogs(
|
||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNum,
|
||
@Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer pageSize,
|
||
@Parameter(description = "任务状态") @RequestParam(required = false) String taskStatus,
|
||
@Parameter(description = "模型") @RequestParam(required = false) String model,
|
||
@Parameter(description = "模型供应商") @RequestParam(required = false) String modelProvider,
|
||
@Parameter(description = "归属人姓名") @RequestParam(required = false) String ownerName,
|
||
@Parameter(description = "归属人电话") @RequestParam(required = false) String ownerPhone,
|
||
@Parameter(description = "是否成功") @RequestParam(required = false) Boolean success,
|
||
@Parameter(description = "开始时间") @RequestParam(required = false) String startTime,
|
||
@Parameter(description = "结束时间") @RequestParam(required = false) String endTime) {
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
Page<VideoSynthesisLog> page = new Page<>(pageNum, pageSize);
|
||
QueryWrapper<VideoSynthesisLog> queryWrapper = new QueryWrapper<>();
|
||
|
||
// 添加查询条件
|
||
if (taskStatus != null && !taskStatus.trim().isEmpty()) {
|
||
queryWrapper.eq("task_status", taskStatus);
|
||
}
|
||
if (model != null && !model.trim().isEmpty()) {
|
||
queryWrapper.like("model", model);
|
||
}
|
||
if (modelProvider != null && !modelProvider.trim().isEmpty()) {
|
||
queryWrapper.like("model_provider", modelProvider);
|
||
}
|
||
if (ownerName != null && !ownerName.trim().isEmpty()) {
|
||
queryWrapper.like("owner_name", ownerName);
|
||
}
|
||
if (ownerPhone != null && !ownerPhone.trim().isEmpty()) {
|
||
queryWrapper.like("owner_phone", ownerPhone);
|
||
}
|
||
if (success != null) {
|
||
queryWrapper.eq("success", success);
|
||
}
|
||
if (startTime != null && !startTime.trim().isEmpty()) {
|
||
queryWrapper.ge("request_time", startTime);
|
||
}
|
||
if (endTime != null && !endTime.trim().isEmpty()) {
|
||
queryWrapper.le("request_time", endTime);
|
||
}
|
||
|
||
// 按请求时间倒序排列
|
||
queryWrapper.orderByDesc("request_time");
|
||
|
||
// 执行查询
|
||
Page<VideoSynthesisLog> pageResult = videoSynthesisLogMapper.selectPage(page, queryWrapper);
|
||
|
||
result.put("success", true);
|
||
result.put("message", "查询成功");
|
||
result.put("data", pageResult.getRecords());
|
||
result.put("total", pageResult.getTotal());
|
||
result.put("pageNum", pageResult.getCurrent());
|
||
result.put("pageSize", pageResult.getSize());
|
||
result.put("pages", pageResult.getPages());
|
||
|
||
return ResponseEntity.ok(result);
|
||
|
||
} catch (Exception e) {
|
||
log.error("查询视频合成日志失败: {}", e.getMessage(), e);
|
||
result.put("success", false);
|
||
result.put("message", "查询失败: " + e.getMessage());
|
||
return ResponseEntity.status(500).body(result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据MinIO路径播放视频
|
||
*/
|
||
@GetMapping("/play/{minioPath}")
|
||
@Operation(summary = "播放视频", description = "根据MinIO路径生成预签名URL播放视频")
|
||
public ResponseEntity<Map<String, Object>> playVideo(
|
||
@Parameter(description = "MinIO文件路径", required = true)
|
||
@PathVariable String minioPath,
|
||
@Parameter(description = "过期时间(秒),默认7天", required = false)
|
||
@RequestParam(value = "expires", defaultValue = "900") int expires) {
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
log.info("开始生成视频播放URL,MinIO路径: {}, 过期时间: {}秒", minioPath, expires);
|
||
|
||
// 验证路径格式
|
||
if (minioPath == null || minioPath.trim().isEmpty()) {
|
||
result.put("success", false);
|
||
result.put("message", "MinIO路径不能为空");
|
||
return ResponseEntity.badRequest().body(result);
|
||
}
|
||
|
||
// 清理路径,移除开头的斜杠
|
||
String cleanPath = minioPath.startsWith("/") ? minioPath.substring(1) : minioPath;
|
||
|
||
// 生成预签名URL
|
||
String presignedUrl = minIOService.getPresignedUrl(cleanPath, expires);
|
||
|
||
if (presignedUrl == null || presignedUrl.trim().isEmpty()) {
|
||
result.put("success", false);
|
||
result.put("message", "生成预签名URL失败");
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
|
||
// 构建响应结果
|
||
result.put("success", true);
|
||
result.put("message", "视频播放URL生成成功");
|
||
result.put("minioPath", minioPath);
|
||
result.put("presignedUrl", presignedUrl);
|
||
result.put("expires", expires);
|
||
result.put("expiresInDays", expires / 86400); // 转换为天数
|
||
|
||
log.info("视频播放URL生成成功: {}", presignedUrl);
|
||
return ResponseEntity.ok(result);
|
||
|
||
} catch (Exception e) {
|
||
log.error("生成视频播放URL失败: {}", e.getMessage(), e);
|
||
result.put("success", false);
|
||
result.put("message", "生成视频播放URL失败: " + e.getMessage());
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据任务ID播放视频
|
||
*/
|
||
@GetMapping("/play/task/{taskId}")
|
||
@Operation(summary = "根据任务ID播放视频", description = "根据任务ID查找视频并生成播放URL")
|
||
public ResponseEntity<Map<String, Object>> playVideoByTaskId(
|
||
@Parameter(description = "任务ID", required = true)
|
||
@PathVariable String taskId,
|
||
@Parameter(description = "过期时间(秒),默认7天", required = false)
|
||
@RequestParam(value = "expires", defaultValue = "604800") int expires) {
|
||
|
||
Map<String, Object> result = new HashMap<>();
|
||
|
||
try {
|
||
log.info("开始根据任务ID播放视频,任务ID: {}, 过期时间: {}秒", taskId, expires);
|
||
|
||
// 根据任务ID查询视频合成记录
|
||
QueryWrapper<VideoSynthesisLog> queryWrapper = new QueryWrapper<>();
|
||
queryWrapper.eq("task_id", taskId);
|
||
VideoSynthesisLog synthesisLog = videoSynthesisLogMapper.selectOne(queryWrapper);
|
||
|
||
if (synthesisLog == null) {
|
||
result.put("success", false);
|
||
result.put("message", "未找到对应的视频合成记录");
|
||
return ResponseEntity.notFound().build();
|
||
}
|
||
|
||
if (synthesisLog.getVideoUrl() == null || synthesisLog.getVideoUrl().trim().isEmpty()) {
|
||
result.put("success", false);
|
||
result.put("message", "视频URL为空,可能视频还未生成完成");
|
||
return ResponseEntity.badRequest().body(result);
|
||
}
|
||
|
||
// 从videoUrl中提取MinIO路径
|
||
String videoUrl = synthesisLog.getVideoUrl();
|
||
String minioPath;
|
||
|
||
// 判断是否为MinIO URL格式
|
||
if (videoUrl.contains("/minio/")) {
|
||
// 从MinIO URL中提取路径
|
||
String[] parts = videoUrl.split("/minio/");
|
||
if (parts.length > 1) {
|
||
minioPath = parts[1];
|
||
} else {
|
||
result.put("success", false);
|
||
result.put("message", "无法从视频URL中提取MinIO路径");
|
||
return ResponseEntity.badRequest().body(result);
|
||
}
|
||
} else {
|
||
// 假设直接是MinIO路径
|
||
minioPath = videoUrl;
|
||
}
|
||
|
||
// 生成预签名URL
|
||
String presignedUrl = minIOService.getPresignedUrl(minioPath, expires);
|
||
|
||
if (presignedUrl == null || presignedUrl.trim().isEmpty()) {
|
||
result.put("success", false);
|
||
result.put("message", "生成预签名URL失败");
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
|
||
// 构建响应结果
|
||
result.put("success", true);
|
||
result.put("message", "视频播放URL生成成功");
|
||
result.put("taskId", taskId);
|
||
result.put("requestId", synthesisLog.getRequestId());
|
||
result.put("videoName", synthesisLog.getVideoName());
|
||
result.put("minioPath", minioPath);
|
||
result.put("presignedUrl", presignedUrl);
|
||
result.put("expires", expires);
|
||
result.put("expiresInDays", expires / 86400);
|
||
result.put("taskStatus", synthesisLog.getTaskStatus());
|
||
result.put("success", synthesisLog.getSuccess());
|
||
|
||
log.info("根据任务ID生成视频播放URL成功: {}", presignedUrl);
|
||
return ResponseEntity.ok(result);
|
||
|
||
} catch (Exception e) {
|
||
log.error("根据任务ID生成视频播放URL失败: {}", e.getMessage(), e);
|
||
result.put("success", false);
|
||
result.put("message", "生成视频播放URL失败: " + e.getMessage());
|
||
return ResponseEntity.internalServerError().body(result);
|
||
}
|
||
}
|
||
}
|