阿里云视频点播资源迁移及回溯校验

news2024/11/15 17:50:26

阿里云视频点播资源迁移及回溯校验

  • 一:准备环境
    • 1.1、搭建springboot
    • 1.2、引入外部包
  • 二:表与配置
    • 2.1、拉取视频点播文件
    • 2.2、建表
    • 2.3、yml配置文件
  • 三:代码
    • 3.1、XML
    • 3.2、entity
    • 3.3、mapper
    • 3.4、Synchronize
    • 3.5、视频迁移Controller
    • 3.6、视频回溯校验Controller
  • 四:资源
    • 4.1、源码链接

一:准备环境

1.1、搭建springboot

建一个Springboot项目。
需要做的就是 配置一个datasource,加一个mybatis-plus即可。
网上教程很多,不多说了。

1.2、引入外部包

如图,将资源包全部添加即可。资源在文末。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二:表与配置

2.1、拉取视频点播文件

登录阿里云视频点播平台
在这里插入图片描述
按照需要的内容导出即可
在这里插入图片描述
注意:有可能出现导出之后下载不了的情况,这个事情就需要在阿里云提工单,从后台导出,会更快一些。

导出的是 .csv文件,和Excel一样的打开方式,类似下面这种文件内容。
在这里插入图片描述

2.2、建表

创建表 t_video

CREATE TABLE `t_video` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `MEDIA_ID` varchar(100) DEFAULT NULL COMMENT '媒体ID',
  `MEDIA_NAME` varchar(100) DEFAULT NULL COMMENT '媒体名称',
  `MEDIA_DURATION` varchar(100) DEFAULT NULL COMMENT '媒体时长(s)',
  `MEDIA_SIZE` varchar(100) DEFAULT NULL COMMENT '媒体大小(bytes)',
  `TYPE` varchar(100) DEFAULT NULL COMMENT '分类',
  `TYPE_CODE` varchar(100) DEFAULT NULL COMMENT '分类CODE',
  `MEDIA_CREATE_TIME` varchar(100) DEFAULT NULL COMMENT '创建时间',
  `MEDIA_UPDATE_TIME` varchar(100) DEFAULT NULL COMMENT '最新更新',
  `MEDIA_SOURCE_PATH` varchar(2000) DEFAULT NULL COMMENT '源文件地址',
  `MEDIA_MP4_PATH` varchar(2000) DEFAULT NULL COMMENT '源文件地址MP4',
  `MEDIA_STANDARD_PATH` varchar(2000) DEFAULT NULL COMMENT '标清_M3U8',
  `MEDIA_SOURCE_MP4_PATH` varchar(2000) DEFAULT NULL COMMENT '原画_MP4',
  `MEDIA_STANDARD_MP4_PATH` varchar(2000) DEFAULT NULL COMMENT '标清_MP4',
  `CREATE_TIME` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `UPDATE_TIME` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `STATUS` char(1) DEFAULT '0' COMMENT '状态:0-未同步,1-已同步,2-数据异常,3-视频无法访问,4-无视频',
  `SUCCESS` varchar(50) DEFAULT '0' COMMENT '校验视频 0:未校验;1:无视频;有视频则保存视频状态',
  PRIMARY KEY (`ID`),
  KEY `NK_MEDIA_CREATE_TIME` (`MEDIA_CREATE_TIME`) USING BTREE,
  KEY `NK_STATUS` (`STATUS`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='视频同步表';

重要字段说明:
MEDIA_NAME:就是上传视频的唯一标识,依据该标识可以找到对应的视频信息。
TYPE:迁移之后,新的环境对应的视频分类是不一样的,所以需要做一下映射。
MEDIA_MP4_PATH:视频的访问地址,可以用该地址访问视频内容。
STATUS:用来记录视频迁移状态的字段,0-未同步,1-已同步,2-数据异常,3-视频无法访问,4-无视频
SUCCESS:用来做视频回溯校验的字段,校验视频 0:未校验;1:无视频;有视频则保存视频状态

2.3、yml配置文件

server:
  port: 8008
  servlet:
    context-path: /export

spring:
  mvc:
    view:
      suffix: .html

  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB

# 数据源配置
  datasource:
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/video?characterEncoding=utf8&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
    type: com.alibaba.druid.pool.DruidDataSource
    initialSize: 5
    min: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: config,stat,wall
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

# 使用mybatis-plus
mybatis-plus:
  mapper-locations:
  - classpath*:/mybatis/mapper/*.xml

  type-aliases-package: com.export.entity;
  global-config:
    id-type: 3
    db-column-underline: true
    field-strategy: 2

  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: false
    call-setters-on-nulls: true

logging:
  file: /log/generate.log
  level:
    com.cpic.chjb: INFO
  root: INFO

info:
  app:
    name: test

三:代码

3.1、XML

VideoBaseMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.export.VideoBaseMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.export.VideoEntity">
        <id column="ID" property="id" />
        <result column="MEDIA_ID" property="mediaId" />
        <result column="MEDIA_NAME" property="mediaName" />
        <result column="MEDIA_DURATION" property="mediaDuration" />
        <result column="MEDIA_SIZE" property="mediaSize" />
        <result column="TYPE" property="type" />
        <result column="TYPE_CODE" property="typeCode" />
        <result column="MEDIA_CREATE_TIME" property="mediaCreateTime" />
        <result column="MEDIA_UPDATE_TIME" property="mediaUpdateTime" />
        <result column="MEDIA_SOURCE_PATH" property="mediaSourcePath" />
        <result column="MEDIA_MP4_PATH" property="mediaMp4Path" />
        <result column="MEDIA_STANDARD_PATH" property="mediaStandardPath" />
        <result column="MEDIA_SOURCE_MP4_PATH" property="mediaSourceMp4Path" />
        <result column="MEDIA_STANDARD_MP4_PATH" property="mediaStandardMp4Path" />
        <result column="CREATE_TIME" property="createTime" />
        <result column="UPDATE_TIME" property="updateTime" />
        <result column="STATUS" property="status" />
        <result column="SUCCESS" property="success" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        ID, MEDIA_ID, MEDIA_NAME, MEDIA_DURATION, MEDIA_SIZE, TYPE, TYPE_CODE, MEDIA_CREATE_TIME, MEDIA_UPDATE_TIME, MEDIA_SOURCE_PATH, MEDIA_MP4_PATH, MEDIA_STANDARD_PATH, MEDIA_SOURCE_MP4_PATH, MEDIA_STANDARD_MP4_PATH, CREATE_TIME, UPDATE_TIME, STATUS, SUCCESS
    </sql>

</mapper>

3.2、entity

VideoEntity.java

package com.export;

import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("T_VIDEO")
public class VideoEntity extends Model<VideoEntity> {
    private static final long serialVersionUID = 1L;
    /**
     * 自增列
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    //媒体ID
    private String mediaId;
    //媒体名称
    private String mediaName;
    //媒体时长(s)
    private String mediaDuration;
    //媒体大小(bytes)
    private String mediaSize;
    //分类
    private String type;
    //分类CODE
    private String typeCode;
    //创建时间
    private String mediaCreateTime;
    //最新更新
    private String mediaUpdateTime;
    //源文件地址
    private String mediaSourcePath;
    //源文件地址MP4
    private String mediaMp4Path;
    //标清_M3U8
    private String mediaStandardPath;
    //原画_MP4
    private String mediaSourceMp4Path;
    //标清_MP4
    private String mediaStandardMp4Path;
    //创建时间
    private Date createTime;
    //更新时间
    private Date updateTime;
    //状态:0-未同步,1-已同步,2-数据异常,3-视频无法访问,4-无视频
    private String status;
    //校验视频 0:未校验;1:无视频;有视频则保存视频状态
    private String success;

    @Override
    protected Serializable pkVal() {
        return this.id;
    }
}

3.3、mapper

VideoBaseMapper.java

package com.export;

import com.baomidou.mybatisplus.mapper.BaseMapper;

public interface VideoBaseMapper extends BaseMapper<VideoEntity> {

}

3.4、Synchronize

package com.export;

import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * 使用上传SDK进行视频文件上传
 */
@Slf4j
public class VideoSynchronize {

    public final static String KEY_ID = "**********";
    public final static String KEY_SECRET = "**********";
    //视频类别映射,由原视频类型,映射成新环境视频类型
    public static final Map<String,String> map = new HashMap<>();
    static {
        map.put("11111","22222");
    }

    /**
     * 流式上传接口
     * @param accessKeyId KEY_ID
     * @param accessKeySecret KEY_SECRET
     * @param title 媒体名称
     * @param fileName 文件名称
     * @param inputStream 流
     * @param cateId 文件类别
     */
    private static void uploadStream(String accessKeyId, String accessKeySecret, String title,
                                         String fileName, InputStream inputStream, Long cateId) throws RuntimeException{
        UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, title, fileName, inputStream);
        /* 自定义消息回调设置,参数说明请参见基本数据类型 */
        //request.setUserData(""{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://example.aliyundoc.com\"}}"");
        /* 视频分类ID(可选) */
        request.setCateId(cateId);
        /* 视频标签,多个用逗号分隔(可选) */
        //request.setTags("标签1,标签2");
        /* 视频描述(可选) */
        //request.setDescription("视频描述");
        /* 封面图片(可选) ,如http://****.example.com/image_01.jpg*/
        //request.setCoverURL("<Your CoverURL>");
        /* 模板组ID(可选) */
        //request.setTemplateGroupId("8c4792cbc8694e****fd5330e56a33d");
        /* 工作流ID(可选) */
        //request.setWorkflowId("d4430d07361f****1339577859b0177b");
        /* 存储区域(可选) */
        //request.setStorageLocation("outin-20170323****266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
        /* 点播服务接入点 */
        request.setApiRegionId("cn-shanghai");
        /* ECS部署区域*/
        // request.setEcsRegionId("cn-shanghai");
        UploadVideoImpl uploader = new UploadVideoImpl();
        UploadStreamResponse response = uploader.uploadStream(request);
        log.info("RequestId=" + response.getRequestId() + "\n");  //请求视频点播服务的请求ID
        if (response.isSuccess()) {
            log.info("VideoId=" + response.getVideoId() + "\n");
        } else { //如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
            log.info("VideoId=" + response.getVideoId() + "\n");
            log.info("ErrorCode=" + response.getCode() + "\n");
            log.info("ErrorMessage=" + response.getMessage() + "\n");
            throw new RuntimeException();
        }
    }

    public static void synchronizeData(VideoEntity videoBean) throws IOException {
        /*//用户可自行添加url数据源,并传入视频媒资信息,上传视频资源
        InputStream inputStream = null;
        //您的视频地址。如http://example.aliyundoc.com/video*/
        String url = videoBean.getMediaMp4Path();
        InputStream inputStream = new URL(url).openStream();
        //以下参数重的AccessKey ID, AccessKey Secret提前准备好的AccessKey信息。<Your Video Title>为视频标题。<Your Video with File Extension>为含文件扩展名的视频,如video-1.mp4。
        uploadStream(KEY_ID, KEY_SECRET, videoBean.getMediaName(), videoBean.getMediaId() + ".MP4",
                inputStream, Long.valueOf(map.get(videoBean.getTypeCode())));
    }

}

3.5、视频迁移Controller

package com.export;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Controller
public class VideoController extends ServiceImpl<VideoBaseMapper, VideoEntity>{

    private final static String VIDEO_STATUS_0 = "0";//未同步
    private final static String VIDEO_STATUS_1 = "1";//已同步
    private final static String VIDEO_STATUS_2 = "2";//数据异常
    private final static String VIDEO_STATUS_3 = "3";//视频无法访问
    private final static String VIDEO_STATUS_4 = "4";//无视频

    // http://localhost:8008/export/video
    @RequestMapping("/video")
    @ResponseBody
    public void queryDictionaryAllList(HttpServletResponse response) throws Exception {
        response.setHeader("Access-Control-Allow-Origin","*");
        response.setHeader("Catch-Control","no-cache");
        //1、查询数据库没有执行数据
        EntityWrapper<VideoEntity> wrapper = new EntityWrapper<>();
        wrapper.in("STATUS", Collections.singletonList(VIDEO_STATUS_0));
        wrapper.orderBy("ID", true);
        wrapper.isNotNull("MEDIA_MP4_PATH");
        wrapper.last("limit 1000");
        List<VideoEntity> videoEntityList = baseMapper.selectList(wrapper);
        if(null == videoEntityList || videoEntityList.size() == 0) return;
        //2、设置一个值放置别的线程拉取到这部分数据
        for (VideoEntity video : videoEntityList) {
            video.setStatus("a");
        }
        updateBatchById(videoEntityList);
        //3、开启一个异步线程跑数据
        Date now = new Date();
        Thread thread = new Thread(() ->{
            for(VideoEntity videoEntity : videoEntityList){
                VideoEntity videoCheck = baseMapper.selectById(videoEntity.getId());
                if(null == videoCheck || VIDEO_STATUS_1.equals(videoCheck.getStatus())){
                    continue;
                }
                //单独校验一次文件流是否存在
                try {
                    InputStream inputStream = new URL(videoEntity.getMediaMp4Path()).openStream();
                } catch (Exception e){
                    videoEntity.setStatus(VIDEO_STATUS_3);
                    videoEntity.setUpdateTime(now);
                    baseMapper.updateById(videoEntity);
                    continue;
                }
                //同步视频
                try {
                    VideoSynchronize.synchronizeData(videoEntity);
                    videoEntity.setStatus(VIDEO_STATUS_1);
                } catch (Exception e){
                    log.error("Current Thread Write Excel Error", e);
                    videoEntity.setStatus(VIDEO_STATUS_2);
                } finally {
                    videoEntity.setUpdateTime(now);
                    baseMapper.updateById(videoEntity);
                }
            }
//        writeDataPool(videoEntityList);
        });
        thread.start();
    }

    //线程池(在使用线程池多线程跑视频迁移的过程中,发现存在大量迁移失败的情况,猜测应该是做了限流之类的东西)
    private void writeDataPool(List<VideoEntity> list) {
        Integer poolSize = 1000;
        ExecutorService pool = new ThreadPoolExecutor(poolSize, 2 * poolSize, poolSize, TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue<Runnable>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        for (VideoEntity video : list) {
            pool.execute(new writeDataThread(video));
        }
        try {
            pool.shutdown();
            boolean loop = true;
            do {
                loop = !pool.awaitTermination(200, TimeUnit.MILLISECONDS);
            } while (loop);
        } catch (InterruptedException e) {
            log.error("Current Thread Pool Error", e);
        }
    }

    private class writeDataThread implements Runnable {
        private VideoEntity videoEntity;
        public writeDataThread(VideoEntity videoEntity) {
            this.videoEntity = videoEntity;
        }
        @Override
        public void run() {
            try {
                VideoSynchronize.synchronizeData(videoEntity);
                videoEntity.setStatus(VIDEO_STATUS_1);
            } catch (Exception e){
                log.error("Current Thread Write Excel Error", e);
                videoEntity.setStatus(VIDEO_STATUS_2);
            } finally {
                videoEntity.setUpdateTime(new Date());
                baseMapper.updateById(videoEntity);
            }
        }
    }

}

3.6、视频回溯校验Controller

package com.export;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vod.model.v20170321.SearchMediaRequest;
import com.aliyuncs.vod.model.v20170321.SearchMediaResponse;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Controller
public class CheckController extends ServiceImpl<VideoBaseMapper, VideoEntity> {

    private final static String VIDEO_SUCCESS_0 = "0";//未校验
    private final static String VIDEO_SUCCESS_1 = "1";//无视频
    private final static String VIDEO_SUCCESS_EXCEPTION = "Exception";//异常

    /**
     * 根据分类和视频名称查询视频
     */
    @RequestMapping("/check")
    @ResponseBody
    public void queryVideoByCate() throws Exception {
        //1、查询数据库未校验的视频,以及校验异常的视频
        EntityWrapper<VideoEntity> wrapper = new EntityWrapper<>();
        wrapper.eq("STATUS", "1");
        wrapper.eq("SUCCESS", VIDEO_SUCCESS_0);
//        wrapper.eq("SUCCESS", VIDEO_SUCCESS_EXCEPTION);
        List<VideoEntity> videoEntityList = baseMapper.selectList(wrapper);
        if (null == videoEntityList || videoEntityList.size() == 0) return;
        Date now = new Date();
        Thread thread = new Thread(() ->{
            for (VideoEntity videoEntity : videoEntityList) {
                try {
                    List<VideoResponseDTO> list = checkVideo(videoEntity);
                    if (null != list) {
                        VideoResponseDTO videoResponseDTO = list.get(0);
                        videoEntity.setSuccess(videoResponseDTO.getStatus());
                    } else {
                        videoEntity.setSuccess(VIDEO_SUCCESS_1);//无视频
                    }
                } catch (Exception e) {
                    videoEntity.setSuccess(VIDEO_SUCCESS_EXCEPTION);//有异常
                } finally {
                    videoEntity.setUpdateTime(now);
                }
                baseMapper.updateById(videoEntity);
            }
        });
        thread.start();
    }

    private List<VideoResponseDTO> checkVideo(VideoEntity videoEntity) throws Exception {
        List<VideoResponseDTO> list = new ArrayList<>();
        // 拼接match条件(cateId)
        String matchCondition = "Status in ('UploadSucc','Transcoding','Checking','Blocked','Normal')";
        //查询cateId
        String cateId = VideoSynchronize.map.get(videoEntity.getTypeCode());
        matchCondition = matchCondition + " And CateId = " + cateId;
        VideoResponseDTO videoResponseDTO;
        if (null != videoEntity.getMediaName()) {
            matchCondition = matchCondition + " And Title = '" + videoEntity.getMediaName() + "'";
            // 查询对应分类下所有视频
            SearchMediaResponse videos = getVideos(matchCondition);
            for (SearchMediaResponse.Media media : videos.getMediaList()) {
                videoResponseDTO = new VideoResponseDTO();
                SearchMediaResponse.Media.Video videoInfo = media.getVideo();
                if (null != videoInfo.getVideoId()) {
                    videoResponseDTO.setVideoId(videoInfo.getVideoId());
                    videoResponseDTO.setCoverURL(videoInfo.getCoverURL());
                    videoResponseDTO.setStatus(videoInfo.getStatus());
                    // 时间转化
                    if (null != videoInfo.getDuration()) {
                        String s = String.valueOf(videoInfo.getDuration());
                        videoResponseDTO.setDuration(getDate(Integer.parseInt(s.substring(0, s.indexOf(".")))));
                    }
                }
                SearchMediaResponse.Media.Audio audio = media.getAudio();
                if (null != audio.getAudioId()) {
                    videoResponseDTO.setVideoId(audio.getAudioId());
                    videoResponseDTO.setCoverURL(audio.getCoverURL());
                    videoResponseDTO.setStatus(audio.getStatus());
                    // 时间转化
                    if (null != audio.getDuration()) {
                        String s = String.valueOf(audio.getDuration());
                        videoResponseDTO.setDuration(getDate(Integer.parseInt(s.substring(0, s.indexOf(".")))));
                    }
                }
                list.add(videoResponseDTO);
            }
        }
        return list;
    }

    /**
     * 获取视频信息
     */
    public SearchMediaResponse getVideos(String matchCondition) throws Exception {
        DefaultAcsClient client = initVodClient();
        SearchMediaRequest request = new SearchMediaRequest();
        request.setFields("Title,CoverURL,Status,CreationTime,ModificationTime,VideoId,Duration");
        request.setMatch(matchCondition);
        request.setSearchType("video");
        request.setSortBy("CreationTime:Asc");
        return searchMedia(client, request);
    }

    public static DefaultAcsClient initVodClient() throws IOException {
        String regionId = "cn-shanghai";  // 点播服务接入区域
        DefaultProfile profile;
        profile = DefaultProfile.getProfile(regionId, VideoSynchronize.KEY_ID, VideoSynchronize.KEY_SECRET);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    /*查找视频*/
    public static SearchMediaResponse searchMedia(DefaultAcsClient client, SearchMediaRequest request) throws Exception {
        return client.getAcsResponse(request);
    }

    /**
     * 秒转化为时分秒
     */
    public static String getDate(Integer date) {
        if (date < 60) {
            if (date < 10){
                return "00:0" + date;
            } else {
                return "00:" + date;
            }
        } else if (date > 60 && date < 3600) {
            String mm;
            String ss;
            int m = date/60;
            int s = date%60;
            if (m < 10){
                mm = "0" + m;
            } else {
                mm = String.valueOf(m);
            }
            if (s < 10){
                ss = "0" + s;
            } else {
                ss = String.valueOf(s);
            }
            return mm + ":" + ss;
        } else {
            String hh;
            String mm;
            String ss;
            int h = date/3600;
            int m = (date%3600)/60;
            int s = (date%3600)%60;
            if (m < 10){
                mm = "0" + m;
            } else {
                mm = String.valueOf(m);
            }
            if(s < 10){
                ss = "0" + s;
            } else {
                ss = String.valueOf(s);
            }
            if(h < 10){
                hh = "0" + h;
            } else {
                hh = String.valueOf(h);
            }
            return hh + ":" + mm + ":" + ss;
        }
    }

    public class VideoResponseDTO{
        //视频封面URL。
        private String coverURL;
        //视频创建时间。使用UTC时间。
        private String creationTime;
        //视频状态
        private String status;
        //视频分类名称
        private String cateName;
        //视频ID
        private String videoId;
        //视频标题
        private String title;
        //持续时间
        private String duration;
        public String getCoverURL() {
            return coverURL;
        }
        public void setCoverURL(String coverURL) {
            this.coverURL = coverURL;
        }
        public String getCreationTime() {
            return creationTime;
        }
        public void setCreationTime(String creationTime) {
            this.creationTime = creationTime;
        }
        public String getStatus() {
            return status;
        }
        public void setStatus(String status) {
            this.status = status;
        }
        public String getCateName() {
            return cateName;
        }
        public void setCateName(String cateName) {
            this.cateName = cateName;
        }
        public String getVideoId() {
            return videoId;
        }
        public void setVideoId(String videoId) {
            this.videoId = videoId;
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public String getDuration() {
            return duration;
        }
        public void setDuration(String duration) {
            this.duration = duration;
        }
    }

}

四:资源

4.1、源码链接

CSDN资源链接:https://download.csdn.net/download/qq_38254635/87353068
百度网盘资源链接:https://pan.baidu.com/s/1fcPqK449h6NYzC-ADvMZMQ?pwd=54rp
提取码: 54rp

有什么不对的还望指正,书写不易,觉得有帮助就点个赞吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/126307.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fedora初始化 | python在Fedora中的初始化搭建

本来是想玩一下ArticlePairMatching-master&#xff0c;结果需要graph-tool工具&#xff0c;这哥们需要c等环境&#xff0c;无法在window中直接安装。 望着不堪重负的win系统&#xff0c;还是安了一个全新的Fedora虚拟机作为实验环境吧。 1. 使用VMWare安装Fedora Fedora的图形…

户外徒步戴什么耳机比较好、最适合户外运动的五款耳机推荐

现在人们对自己的身体状态越来越重视了&#xff0c;无论是怎样的生活&#xff0c;一个健康的身体非常重要&#xff0c;近几年的运动健身热潮的兴起&#xff0c;能看出来人们会花更多的时间去锻炼自己的身体了&#xff0c;与之而来的就是各种健身、运动的蓝牙耳机不断的推陈出新…

2022餐馆设计最新十大排行榜(附餐馆设计案例图片)

随着生活水平的提升&#xff0c;人们对于用餐的环境要求越来越高&#xff0c;对于餐馆设计的要求随之提高&#xff0c;因此在装修时&#xff0c;很多餐饮老板想找一家实力强的餐厅设计公司&#xff0c;那么国内的餐饮设计公司数量庞大&#xff0c;有哪些擅长餐饮设计呢&#xf…

android生成javadoc

生成步骤 菜单 Tools -> Generate JavaDoc! 问题: 1.JavaDoc导出时出现乱码报错 错误: 编码GBK的不可映射字符 菜单 Tools -> Generate JavaDoc&#xff0c;弹出配置面板 在Other command line arguments 栏输入&#xff1a;-encoding utf-8 -charset utf-8 2.提示 程序…

C/S快速开发框架源码 winform快速开发框架源码

C/S系统开发框架源码 C#快速开发框架源码&#xff08;CS版&#xff09;00683 源码分享&#xff0c;需要源码学习可私信我。 系统功能&#xff1a; 该框架采用逻辑上的三层模式开发业务处理和数据处理完全分开&#xff0c;采用C#语言和MSSQL进行开发&#xff0c;主要实现了菜…

微信小程序 会议OA项目-其它页面04

目录 1.会议管理 1.1 自定义tabs组件 1.2 会议管理 2.投票管理 3.个人中心 1.会议管理 1.1 自定义tabs组件 文档参考:自定义组件 | 微信开放文档微信开发者平台文档https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/ 新建一个文件夹&#xff1…

尚医通-数据字典-EasyExcel-导入 导出(十四)

目录&#xff1a; &#xff08;1&#xff09;EasyExcel-写操作 &#xff08;2&#xff09;EasyExcel-读操作 &#xff08;3&#xff09;后台系统-数据字典-导出 &#xff08;4&#xff09; 后台系统-数据字典-导入 &#xff08;1&#xff09;EasyExcel-写操作 往数据字典里…

家用摄像头选择(户内外)

不知道摄像头怎么选&#xff1f;看下面几篇就够了。 1、户外监控摄像头&#xff0c;哪个品牌的比较好&#xff1f; - 知乎 2、【室外篇】家用监控摄像头选购要素及不同场景下高性价比监控摄像头推荐 - 知乎 3、家用摄像头选什么牌子的好? - 知乎 优先选焦距 IP防水防尘&…

超宽带(UWB)开发板BU0/DW1000性能测试

简介 本实验目的是测试NodeMCU-BU01开发板测距的性能。 BU01是基于Decawave的DW1000设计的超宽带&#xff08;UWB&#xff09;收发器模组。BU01可以用于双向测距或TWR定位系统中&#xff0c;定位精度可达到10厘米&#xff0c;并支持高达6.8 Mbps的数据速率。 UWB技术是一种使…

“ 1天投200次简历,依旧石沉大海 ” 这届年轻人的“求职焦虑”,如何缓解?

最近在新闻上看到这样一则热议事件&#xff1a;“1天投200次简历”&#xff1f;多方发力&#xff0c;缓解应届生“求职焦虑”。 教育部公布的数据显示&#xff0c;我国2022届全国普通高校毕业生数量为1076万人&#xff0c;而2023届预计达1158万人&#xff0c;同比将增加82万人…

通过webpack配置【程序打包时间】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、webpack打包定义全局常量二、将全局常量设置为当前时间三、在console中输出BUILDTIME前言 大家都是做项目研发的&#xff0c;“打包”、“提测”、“发版”…

低代码-零代码基础教育结合场景

在此次大会上&#xff0c;杭州市学军小学教育集团总校长张军林受邀出席。作为将低代码与校园教学管理成功结合运用的一所数字化小学&#xff0c;校长张军林在会上分享了他的数智化管理思想&#xff0c;并发布了学军小学智慧校园系统—学军大脑2.0。 “学军大脑2.0是基于钉钉宜…

ES6-ES11笔记(2)

笔记 (https://www.bilibili.com/video/BV1uK411H7on?p30&vd_source3cf72bb393b8cc11b96c6d4bfbcbd890) 1.ES6 1.14集合介绍与API 1.14.1 set常用的api //声明一个set //set 里面的元素是唯一的,会自动去重 let s1 new Set(); //创建 //可以传入可以迭代的数据 let s…

Windows服务器调整TLS1.2加密套件

由于部分服务器默认不支持&#xff1a;TLS1.2加密条件&#xff0c;这会导致谷歌、火狐等浏览器无法访问网站&#xff0c;这种情况下需要调整加密条件。 支持TLS1.2协议的服务器操作系统需要&#xff1a;windows 2008R2 IIS7以上。 注意&#xff1a;微软说明windows 2003、win…

软件测试期末复习(二)试题及答案

文章目录试卷答案试卷 一、选择题(每题 2 分&#xff0c;共 20 分) 1、下面关于软件测试的说法&#xff0c;_______是错误的。 A 软件测试就是程序测试 B 软件测试贯穿于软件定义和开发的整个期间 C 需求规格说明、设计规格说明都是软件测试的对象 D 程序是软件测试的对象 2…

github工具之OA综合利用python

文章目录0x01 下载地址0x02 主要功能0x03 使用方法0x04 参数使用OA v11.6 report_bi.func.php SQL注入漏洞OA v11.6 print.php 任意文件删除漏洞&#xff0c;删除auth.inc.php中OA v11.8 api.ali.php 任意文件上传漏洞OA v11.8 logincheck_code.php登陆绕过漏洞免责声明0x01 下…

adb工具包环境变量配置

官方下载 Android SDK Platform-Tools 一、关于 adb Android SDK Platform-Tools 是 Android SDK 的一个组件。它包含与 Android 平台进行交互的工具&#xff0c;主要是 adb 和 fastboot。   adb是什么? 他的全称叫安卓调试桥&#xff0c;也就是Android debug bridge &…

AAAI2023 | 针对联邦推荐场景的非定向攻击与防御

嘿&#xff0c;记得给“机器学习与推荐算法”添加星标TLDR&#xff1a;今天跟大家分享一篇通过利用聚类算法来操纵物品嵌入特征以此针对联邦推荐场景进行非定向攻击的工作&#xff0c;随后作者针对这一攻击又提出了一种基于一致性的对应防御机制&#xff0c;该论文已被AAAI2023…

如何在Power BI中使用Python导入数据

在这篇文章中&#xff0c;我们将学习如何在Power BI中使用Python编程语言进行数据源化。 简介 Python可以说是业界最流行、最普遍的编程语言之一。一方面&#xff0c;Python可用于网络和应用程序开发&#xff0c;另一方面&#xff0c;它在数据世界中也非常流行&#xff0c;特…

生产制造业ERP管理系统能解决哪些仓库管理难题?

仓库管理够不够好&#xff0c;安不安全&#xff0c;直接关系到生产制造企业的发展。要知道制造型企业的大部分“身家”&#xff0c;都在仓库里了。来料是否及时&#xff0c;物料是否齐套&#xff0c;库存是否安全&#xff0c;配件品质是否合格&#xff0c;库容是否足够&#xf…