康鲁杰
2025-04-08 f75f9d9cc646d83f9d53edd4a82764fd086a3cc1
队列生成视频
已修改2个文件
已添加3个文件
205 ■■■■■ 文件已修改
media_task_queue.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/manager/MediaTaskManager.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/model/MediaTask.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
media_task_queue.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
{"current":null,"queue":[]}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/manager/MediaTaskManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
package cn.iocoder.yudao.module.digitalcourse.manager;
import cn.iocoder.yudao.module.digitalcourse.model.MediaTask;
import cn.iocoder.yudao.module.digitalcourse.service.coursemedia.CourseMediaServiceUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
/**
 * @author kanglujie
 * @date 2025-04-07 14:36:00
 */
@Component
public class MediaTaskManager {
    private final BlockingQueue<MediaTask> queue = new LinkedBlockingQueue<>();
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    private final Path persistPath = Paths.get("media_task_queue.json");
    private final Path persistTempPath = Paths.get("media_task_queue_temp.json");
    private final Object lock = new Object(); // é”å¯¹è±¡
    private volatile MediaTask currentTask = null;
    @Resource
    private CourseMediaServiceUtil courseMediaServiceUtil;
    @PostConstruct
    public void init() throws IOException {
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        loadQueueFromDisk();
        startWorker();
    }
    public int submitTask(MediaTask task) throws IOException {
        synchronized (lock) {
            queue.add(task);
            persistQueue(); // ç«‹å³æŒä¹…化
            return queue.size();
        }
    }
    public int getQueuePosition(Long taskId) {
        int pos = 1;
        if (currentTask != null && currentTask.getId().equals(taskId)) return 0;
        for (MediaTask task : queue) {
            if (task.getId().equals(taskId)) return pos;
            pos++;
        }
        return -1;
    }
    private void startWorker() {
        executor.submit(() -> {
            while (true) {
                try {
                    currentTask = queue.take(); // å¼€å§‹å¤„理
                    persistQueue();
                    System.out.println("开始合成:" + currentTask.getId());
                    courseMediaServiceUtil.remoteMegerMedia(currentTask.getReq());
                    synchronized (lock) {
                        currentTask = null;
                        persistQueue(); // æ‰§è¡Œå®Œå†æŒä¹…化
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    // å¦‚果异常了,重新入队
                    if (currentTask != null) {
                        queue.add(currentTask);
                        currentTask = null;
                    }
                    persistQueue();
                }
            }
        });
    }
    private void persistQueue() {
        synchronized (lock) {
            try {
                QueueState state = new QueueState();
                state.current = currentTask;
                state.queue = new ArrayList<>(queue);
                mapper.writeValue(persistTempPath.toFile(), state);
                Files.move(persistTempPath, persistPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private void loadQueueFromDisk() {
        synchronized (lock) {
            if (Files.exists(persistPath)) {
                try {
                    QueueState state = mapper.readValue(persistPath.toFile(), QueueState.class);
                    if (state.current != null) {
                        queue.add(state.current);
                    }
                    if (state.queue != null) {
                        queue.addAll(state.queue);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @PreDestroy
    public void shutdown() {
        executor.shutdownNow();
        persistQueue();
    }
    // å†…部类用于 JSON æ˜ å°„
    public static class QueueState {
        public MediaTask current;
        public List<MediaTask> queue;
    }
}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/model/MediaTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.digitalcourse.model;
/**
 * @author kanglujie
 * @date 2025-04-07 14:35:03
 */
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaMegerVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MediaTask {
    private Long id;
    private CourseMediaMegerVO req;
    private LocalDateTime submitTime;
}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java
@@ -1,38 +1,28 @@
package cn.iocoder.yudao.module.digitalcourse.service.coursemedia;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaMegerVO;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaPageReqVO;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaSaveReqVO;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.courses.vo.AppCoursesUpdateReqVO;
import cn.iocoder.yudao.module.digitalcourse.dal.dataobject.coursemedia.CourseMediaDO;
import cn.iocoder.yudao.module.digitalcourse.dal.mysql.coursemedia.CourseMediaMapper;
import cn.iocoder.yudao.module.digitalcourse.manager.MediaTaskManager;
import cn.iocoder.yudao.module.digitalcourse.model.MediaTask;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import java.math.BigInteger;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.module.digitalcourse.enums.ErrorCodeConstants.COURSE_MEDIA_NOT_EXISTS;
@@ -115,10 +105,17 @@
        }
        updateReqVO.setCourseMediaId(courseMediaDO.getId());
        //异步调用数字人视频渲染接口,开始合并
        courseMediaServiceUtil.remoteMegerMedia(updateReqVO);
        return CommonResult.success(true);
        MediaTask task = new MediaTask(id, updateReqVO, LocalDateTime.now());
        int pos = 0;
        try {
            pos = mediaTaskManager.submitTask(task);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return CommonResult.success("合成视频提交成功,您排在第 " + (pos+1) + " ä¸ª");
    }
    @Resource
    private MediaTaskManager mediaTaskManager;
    @Override
    public CommonResult reMegerMedia(CourseMediaMegerVO updateReqVO) {
        Long id = updateReqVO.getId();
@@ -141,4 +138,4 @@
}
}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
@@ -73,9 +73,11 @@
     * @param updateReqVO
     * @return
     */
    @Async
    public void remoteMegerMedia(CourseMediaMegerVO updateReqVO) {
        FileUtil.clean("D:/heygem_data/face2face/temp/");
        CourseMediaDO courseMediaDO = courseMediaMapper.selectById(updateReqVO.getCourseMediaId());
        courseMediaDO.setStatus(1);
        courseMediaMapper.updateById(courseMediaDO);
        List<AppCourseScenesMegerReqVO> scenes = updateReqVO.getScenes();
        //获取数字人素材(声音、视频)
        String entityId = null;
@@ -279,7 +281,7 @@
        byte[] bytes = FileUtil.readBytes(FileUtil.file("D:/heygem_data/face2face/temp/"+"111111.mp4"));
        String file = fileApi.createFile(bytes);
        // å¦‚果成功,更新状态为1(成功)
        courseMediaDO.setStatus(1);
        courseMediaDO.setStatus(2);
        courseMediaDO.setPreviewUrl(file);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();