普通剪辑 WebSDK 模版工厂

普通模版工程是基于普通剪辑工程创建的,所以普通模版的编辑功能也是集成在普通剪辑的WebSDK中。

模版模式

开启普通模版编辑模式,在原来初始化的基础之上传入 mode 参数即可。

window.AliyunVideoEditor.init({
  mode: 'template'
  ......
});

视图差异介绍如下:

  1. 左边会有相关可替换素材的模版参数编辑面板,用来设置相关素材的可替换属性。目前可替换的素材类型有:视频、音频、图片、文字。

  2. 模版模式下,右上方的剪辑工程相关按钮将不会出现。点击“保存模版”则会调用 updateTemplate 将模版的时间线等数据传给调用方,调用方应将这些数据发送给服务端进行保存。

新增 Config

参数名

参数说明

是否必选

参数类型

参数类型说明

引入版本

updateTemplate

保存模版的时间线

(data: {coverUrl: string; duration: number; timeline: Timeline; isAuto: boolean}) => Promise<{projectId: string}>

一个函数,传入参数为工程的封面图地址、时长(单位为秒)、Timeline 数据和是否自动保存(当前每分钟自动保存 1 次),返回一个 Promise,调用方保存成功后 resolve,否则 reject

3.7.0

注意

普通剪辑SDK的其余参数仍然在模版模式下被沿用。

但因为普通模版工程与普通剪辑工程绑定素材的方式不一样,您在传入以下参数的时候,调用的接口会有所不同。具体差异见下一节的代码示例。

  • getEditingProject(获取工程的时间线)

  • getEditingProjectMaterials(获取工程关联素材)

  • deleteEditingProjectMaterials(删除工程关联素材)

  • searchMedia(从媒资库导入素材)

示例代码

WebSDK 本身只负责界面和交互,不会发起请求,您需要将请求的逻辑提供给 WebSDK 调用。请求本身应该先发送给您自己的服务端,您自己的服务端再根据自己的 AccessKeyId, AccessKeySecret 转发给相关的阿里云服务端。

此处仅对差异之处进行示例,其余参数的传入可参考init方式示例代码

// 注意,WebSDK 本身并不提供 request 这个方法,这里仅作为示例,您可以使用您喜欢的网络请求库,如 axios 等

window.AliyunVideoEditor.init({
    container: document.getElementById('aliyun-video-editor'),
    locale: 'zh-CN',
    mode: 'template', // 开启普通模版编辑模式
    getEditingProjectMaterials: () => {
      if (templateId) {
        // templateId 由调用方自己保存

        // 相关接口文档 https://help.aliyun.com/document_detail/277452.html
        return request('GetTemplate', {
          TemplateId: templateId,
          RelatedMediaidFlag: 1 // 加上这个参数,会返回相应的用于绑定媒资的字段
        })
          .then(res => {
            const { RelatedMediaids } = res.data.Template;
            const MediaIds = Object.values(JSON.parse(RelatedMediaids)).reduce(
              (acc, cur) => acc.concat(cur),
              []
            );

            // 批量获取媒资ID对应的资源
            // 用 GetMediaInfo 逐个媒资单个查询 https://help.aliyun.com/document_detail/197842.htm
            return request('BatchGetMediaInfos', {
              MediaIds,
              AdditionType: 'FileInfo'
            });
          })
          .then(res => {
            return transMediaList(res.data.MediaInfos); // 需要做一些数据变换,具体参考后文
          });
      }
      return Promise.resolve([]);
    },
    searchMedia: mediaType => {
      // mediaType 为用户当前所在的素材 tab,可能为 video | audio | image,您可以根据这个参数对应地展示同类型的可添加素材
      return new Promise(resolve => {
        // 调用方需要自己实现展示媒资、选择媒资添加的界面,这里的 callDialog 只是一种示例,WebSDK 本身并不提供
        // 关于展示媒资,请参考:https://help.aliyun.com/document_detail/197964.html
        callDialog({
          onSubmit: async materials => {
            // 这里的 materials 是 ListMediaBasicInfos 接口获取的媒资列表,并经过 transMediaList 转换

            // 相关接口文档 https://help.aliyun.com/document_detail/277452.html
            const res = await request('GetTemplate', {
              TemplateId: templateId, // templateId 由调用方自己保存
              RelatedMediaidFlag: 1 // 加上这个参数,会返回相应的用于绑定媒资的字段
            });

            const { Template } = res.data;
            const MediaIdsMap = JSON.parse(Template.RelatedMediaids); // 解析出当前已绑定的媒资

            // 将选中的媒资进行添加
            materials.forEach(({ mediaType: type, mediaId }) => {
              if (!MediaIdsMap[type]) {
                MediaIdsMap[type] = [];
              }

              if (!MediaIdsMap[type].includes(mediaId)) {
                MediaIdsMap[type].push(mediaId);
              }
            });

            // 保存更新的媒资
            // 相关接口文档 https://help.aliyun.com/document_detail/277454.html
            await request('UpdateTemplate', {
              TemplateId: templateId, // templateId 由调用方自己保存
              RelatedMediaids: JSON.stringify(MediaIdsMap)
            });

            resolve(materials);
          }
        });
      });
    },
    deleteEditingProjectMaterials: async (mediaId, mediaType) => {
      // 相关接口文档 https://help.aliyun.com/document_detail/277452.html
      const res = await request('GetTemplate', {
        TemplateId: templateId, // templateId 由调用方自己保存
        RelatedMediaidFlag: 1 // 加上这个参数,会返回相应的用于绑定媒资的字段
      });

      const { Template } = res.data;
      const MediaIdsMap = JSON.parse(Template.RelatedMediaids); // 解析出当前已绑定的媒资

      // 剔除要删除的媒资
      if (MediaIdsMap[mediaType] && MediaIdsMap[mediaType].includes(mediaId)) {
        MediaIdsMap[mediaType].splice(MediaIdsMap[mediaType].indexOf(mediaId), 1);

        // 保存更新的媒资
        // 相关接口文档 https://help.aliyun.com/document_detail/277454.html
        await request('UpdateTemplate', {
          TemplateId: templateId, // templateId 由调用方自己保存
          RelatedMediaids: JSON.stringify(MediaIdsMap)
        });

        return true;
      }

      return false;
    },
    getEditingProject: async () => {
      // 相关接口文档 https://help.aliyun.com/document_detail/277452.html
      const res = await request('GetTemplate', {
        TemplateId: templateId // templateId 由调用方自己保存
      });

      const config = res.data.Template.Config;
      return {
        timeline: config ? JSON.parse(Config) : undefined
      };
    },
    updateTemplate: async ({ coverUrl, timeline, isAuto }) => {
      const updateParams = {
        TemplateId: templateId, // templateId 由调用方自己保存
        CoverURL: coverUrl,
        Config: JSON.stringify(timeline)
      };

      // 保存更新
      // 相关接口文档 https://help.aliyun.com/document_detail/277454.html
      await request('UpdateTemplate', updateParams);
    }

    // updateEditingProject 模版模式下不会使用到该参数,不需要传入
    // produceEditingProjectVideo 模版模式下不会使用到该参数,不需要传入
  });

  /**
   * 将服务端的素材信息转换成 WebSDK 需要的格式
   */
  function transMediaList(data) {
    if (!data) return [];

    if (Array.isArray(data)) {
      return data.map(item => {
        const basicInfo = item.MediaBasicInfo;
        const fileBasicInfo = item.FileInfoList[0].FileBasicInfo;
        const mediaId = basicInfo.MediaId;
        const result = {
          mediaId
        };
        const mediaType = basicInfo.MediaType;
        result.mediaType = mediaType;

        if (mediaType === 'video') {
          result.video = {
            title: fileBasicInfo.FileName,
            duration: Number(fileBasicInfo.Duration),
            // 源视频的宽高、码率等数据,用于推荐合成数据,不传入或是0时无推荐数据
            width: Number(fileBasicInfo.Width) || 0,
            height: Number(fileBasicInfo.Height) || 0,
            bitrate: Number(fileBasicInfo.Bitrate) || 0,
            coverUrl: basicInfo.CoverURL
          };
          const spriteImages = basicInfo.SpriteImages;
          if (spriteImages) {
            try {
              const spriteArr = JSON.parse(spriteImages);
              const sprite = spriteArr[0];
              const config = JSON.parse(sprite.Config);
              result.video.spriteConfig = {
                num: config.Num,
                lines: config.SpriteSnapshotConfig?.Lines,
                cols: config.SpriteSnapshotConfig?.Columns,
                cellWidth: config.SpriteSnapshotConfig?.CellWidth,
                cellHeight: config.SpriteSnapshotConfig?.CellHeight
              };
              result.video.sprites = sprite.SnapshotUrlList;
            } catch (e) {
              console.log(e);
            }
          }
        } else if (mediaType === 'audio') {
          result.audio = {
            title: fileBasicInfo.FileName,
            duration: Number(fileBasicInfo.Duration),
            coverURL: '' // 您可以给音频文件一个默认的封面图
          };
        } else if (mediaType === 'image') {
          result.image = {
            title: fileBasicInfo.FileName,
            coverUrl: fileBasicInfo.FileUrl,
            // 图片的宽高等数据,用于推荐合成数据,不传入或是0时无推荐数据
            width: Number(fileBasicInfo.Width) || 0,
            height: Number(fileBasicInfo.Height) || 0
          };
        }

        return result;
      });
    } else {
      return [data];
    }
  }