shikeying
2022-09-27 26f5dd8ef80e5671cda8fc0e6c0d0298c4e678ff
recommend-video/src/main/java/com/iplatform/recvideo/SimilarExecutor.java
@@ -1,12 +1,14 @@
package com.iplatform.recvideo;
import com.iplatform.model.po.Rc_video_t1;
import com.iplatform.recvideo.util.PythonInvokeUtils;
import com.iplatform.recvideo.util.TestUtils;
import com.iplatform.recvideo.util.VideoFileUtils;
import com.walker.infrastructure.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
@@ -25,7 +27,9 @@
    private String batchId = null;
    // 当前批次要处理的原始视频集合
    private List<VideoFolderInfo> videoFolderInfoList = null;
    private List<String> videoIdList = new ArrayList<>();
    // 记录当前执行到(该批次)哪个视频文件对应的第几个图片
    private int currentVideoFolderIndex = -1;
@@ -33,15 +37,24 @@
//    private VideoFolderInfo currentVideoFolderInfo = null;
//    private ImageInfo currentImageInfo = null;
    //
    // 是否已完成视频加载调用,完成后才能检索相似视频
    private boolean pythonLoadVideoDone = false;
    private int topN = 40;
    // 如果测试模式,数据是假的!
    private boolean testMode = false;
    public int getTopN() {
        return topN;
    }
    /**
     * 初始化对象调用一次
     * @param videoDataFolder
     * @param batchId
     */
    public void startup(String videoDataFolder, String batchId){
    public void startup(String videoDataFolder, String batchId, int topN, boolean testMode){
        if(StringUtils.isEmpty(videoDataFolder)){
            throw new IllegalArgumentException("视频文件夹根目录必须设置!");
        }
@@ -50,17 +63,28 @@
        }
        this.videoDataFolder = videoDataFolder;
        this.batchId = batchId;
        this.topN = topN;
        this.testMode = testMode;
//        this.videoFolderInfoList = VideoFileUtils.getBatchVideoFolderInfo(this.videoDataFolder, batchId);
    }
    public void destroy(){
        if(this.videoFolderInfoList != null){
            this.videoFolderInfoList.clear();
        }
        if(this.videoIdList != null){
            this.videoIdList.clear();
        }
    }
    /**
     * 在每次调度时钟周期执行一次。例如: 10秒一次。<p></p>
     * 注意:该方法英确保每次调用不会重复数据。
     */
    public void execute(){
    public int execute() throws Exception{
        if(!this.pythonLoadVideoDone){
            logger.debug("当前 pythonLoadVideoDone = false, 需要查询数据库是否已加载视频");
            this.pythonLoadVideoDone = this.pythonLoadVideoDone(this.batchId, videoDataFolder + File.separator + batchId);
            this.pythonLoadVideoDone = this.pythonLoadVideoDone(this.batchId, VideoFileUtils.combineBatchPath(videoDataFolder, batchId));
        }
        // 1: 如果视频还未加载,则先加载视频
@@ -70,29 +94,42 @@
                if(StringUtils.isNotEmpty(error)){
                    // 终止调用,等待下次调度继续尝试执行
                    logger.error("python调用加载视频返回错误:" + error);
                    return;
                    return -1;
                }
                this.pythonLoadVideoDone = true;
            } catch (Exception ex){
                logger.error("python调用加载视频异常:" + this.batchId, ex);
                return;
                return -1;
            }
        }
        // 2: 加载完视频,需要查询每个图片相似度结果,并存储到数据库
        //    这里注意,程序必须和AI服务器部署在一起,方便检索视频分析文件夹(本地)
        if(this.videoFolderInfoList == null){
            this.videoFolderInfoList = VideoFileUtils.getBatchVideoFolderInfo(this.videoDataFolder, batchId);
            if(testMode){
                this.videoFolderInfoList = TestUtils.getBatchVideoFolderInfo(this.videoDataFolder, batchId);
            } else {
                this.videoFolderInfoList = VideoFileUtils.getBatchVideoFolderInfo(this.videoDataFolder, batchId);
            }
        }
        if(StringUtils.isEmptyList(this.videoFolderInfoList)){
            logger.warn("视频分析文件夹内容为空,无法继续查询相似度结果! videoFolderInfoList = null");
            return;
            return -1;
        }
        for(VideoFolderInfo v : this.videoFolderInfoList){
            this.videoIdList.add(v.getVideoId());
        }
        if(this.isSearchWriteDone()){
            logger.info("已经完成批次相似结果写入数据库,不再往下处理。batch = " + this.batchId);
            return;
            logger.info("已经完成批次相似结果写入数据库,处理最后一步:写入用户推荐表数据。batch = " + this.batchId);
            try {
                this.writeRcVideoUser(this.batchId, this.videoIdList);
                // 返回1表示整个流程执行完毕。
                return 1;
            } catch (Exception ex){
                throw new Exception("writeRcVideoUser():" + ex.getMessage(), ex);
            }
        }
        // 开始检索相似度
@@ -100,9 +137,15 @@
            this.currentVideoFolderIndex ++;
        }
        try {
            this.processOneSearchAndWrite(this.batchId);
            return 0;
        } catch (Exception e) {
            throw new Exception("processOneSearchAndWrite(): " + e.getMessage(), e);
        }
    }
    private void processOneSearchAndWrite() throws Exception{
    private void processOneSearchAndWrite(String batchId) throws Exception{
        if(this.currentVideoFolderIndex >= this.videoFolderInfoList.size()){
            throw new IllegalArgumentException("currentVideoFolderIndex 越界: " + this.currentVideoFolderIndex);
        }
@@ -113,20 +156,34 @@
        }
        ImageInfo imageInfo = currentVideoFolderInfo.getImageInfoList().get(this.currentImageIndex);
        this.acquirePythonSearchSimilarOnce(imageInfo.getImagePath(), "30");
        List<Rc_video_t1> videoT1_list = this.acquirePythonSearchSimilarOnce(imageInfo.getVideoId()
                , imageInfo.getImagePath(), String.valueOf(this.topN));
        if(!StringUtils.isEmptyList(videoT1_list)){
            try {
                this.writeRcVideoT1(videoT1_list, PythonInvokeUtils.getFileNameWithoutSuffix(imageInfo.getImagePath(), Constants.IMAGE_SUFFIX));
            } catch (Exception ex){
                throw new Exception("writeRcVideoT1()执行错误:" + ex.getMessage(), ex);
            }
        }
        // 每个视频的最后一张图片
        if((this.currentImageIndex + 1) >= currentVideoFolderInfo.getImageInfoSize()){
            try {
                this.writeRcVideoT2(currentVideoFolderInfo, batchId);
            } catch (Exception ex){
                throw new Exception("writeRcVideoT2()执行错误:" + ex.getMessage(), ex);
            }
            if((this.currentVideoFolderIndex + 1) < this.videoFolderInfoList.size()){
                logger.debug("一个视频图像集合检索处理完毕,切换到下一个,currentImageIndex = " + this.currentImageIndex);
                this.currentVideoFolderIndex ++;
                this.currentImageIndex = -1;
            } else {
                //
                // 让最后一个图像集合索引值超过界限,表示最后一个视频已处理完毕。
                this.currentImageIndex ++;
                logger.debug("所有视频包含的所有图像处理完毕,currentVideoFolderIndex = " + this.currentVideoFolderIndex);
            }
            return;
        }
        this.currentImageIndex ++;
    }
@@ -167,5 +224,26 @@
     * 请求AI服务,检索给定图片的相似度结果集合。
     * @return
     */
    protected abstract List<Rc_video_t1> acquirePythonSearchSimilarOnce(String imagePath, String topN);
    protected abstract List<Rc_video_t1> acquirePythonSearchSimilarOnce(String videoId
            , String imagePath, String topN) throws Exception;
    /**
     * 第一个临时表'rc_video_t1',每张图像包含多个相似视频记录。
     * @param list
     * @param srcImageId 原始视频ID
     */
    protected abstract void writeRcVideoT1(List<Rc_video_t1> list, String srcImageId);
    /**
     * 分析表'rc_video_t1',并把给定视频相似记录写入第二个临时表'rc_video_t2'
     * @param videoFolderInfo
     */
    protected abstract void writeRcVideoT2(VideoFolderInfo videoFolderInfo, String batchId);
    /**
     * 分析给定批次所有视频用户推荐的视频信息,并写入表:'rc_video_user'
     * @param batchId
     * @param recVideoIdList 本批次处理原始视频id集合
     */
    protected abstract void writeRcVideoUser(String batchId, List<String> recVideoIdList);
}