智能生產制作提供專業在線的視頻剪輯能力,針對自動化、智能化剪輯以及多人協作視頻制作需求,您可以基于時間線進行云剪輯。通過閱讀本文,您可以了解如何接入視頻剪輯Web SDK。
使用說明
本文中引入的視頻剪輯Web SDK的版本號5.2.2(僅供參考),從5.0.0開始,你需要申請License授權才能使用剪輯Web SDK。獲取最新的版本信息,請參見視頻剪輯工程——幫助信息。
License授權方式有2種:
方式1(推薦):購買企業標準版(售價3萬元,支持進行官網價5萬元的IMS任意功能消費抵扣)及以上訂閱資源包,會贈送webSDK License,購買說明請參見:計費概述;
方式2:其他訂閱方式,您可以單獨購買webSDK license,定價2萬/年,可工單聯系官方獲取購買鏈接;
操作步驟
引入視頻剪輯Web SDK。
在項目前端頁面文件中的
<head>
標簽處引入視頻剪輯Web SDK的CSS文件,如下所示:<head> <link rel="stylesheet" > </head>
在
<body>
標簽處添加一個用以掛載剪輯界面的<div>
節點,并在<body>
標簽末尾添加引入Web SDK的JS文件,同時添加一個用以調用Web SDK的<script>
節點。<body> <div id="aliyun-video-editor" style="height:700px"></div> // 您可以根據需要改變 container 高度 <script src="https://g.alicdn.com/thor-server/video-editing-websdk/5.2.2/index.js"></script> <script> // 調用 SDK 的代碼放在這里 </script> </body>
初始化視頻剪輯Web SDK。
window.AliyunVideoEditor.init(config);
參數
config
為對象,對象中的屬性說明請參見config屬性說明。初始化函數
init()
示例請參見init()示例代碼。
config屬性說明
config參數
參數 | 類型 | 必填 | 描述 | 引入版本 |
locale | string | 否 | 界面語言,取值:
| 3.0.0 |
container | Element | 是 | Web SDK生成界面掛載的DOM節點。 | 3.0.0 |
defaultAspectRatio | 否 | 默認的視頻預覽比例,默認為16∶9。 | 3.4.0 | |
defaultSubtitleText | string | 否 | 默認的字幕內容,不超過20個字符,默認為“阿里云剪輯”。 | 3.6.0 |
useDynamicSrc | boolean | 否 | 是否動態獲取資源信息。 | 3.0.0 |
getDynamicSrc | (mediaId: string, mediaType: 'video' | 'audio' | 'image' | 'font', mediaOrigin?:'private' | 'public', inputUrl?: string) => Promise<string>; | 否 | 動態獲取資源信息,必填與否與參數useDynamicSrc一致。返回的Promise對象需要resolve資源新的信息。 | 3.10.0 |
getEditingProjectMaterials | () => Promise<InputMedia[]>; | 是 | 獲取工程關聯的素材。返回的Promise對象需要resolve所有素材類型的數組。 | 3.0.0 |
searchMedia | (mediaType: 'video' | 'audio' | 'image') => Promise<InputMedia[]>; | 是 | 資源庫導入素材按鈕相應函數。單擊導入素材后會搜索媒資信息,將媒資庫媒資導入到資源庫中。返回的Promise對象需要resolve新增素材的數組。 重要 您需要調用AddEditingProjectMaterials將新增的素材與工程關聯起來。 | 3.0.0 |
deleteEditingProjectMaterials | (mediaId: string, mediaType: 'video' | 'audio' | 'image') => Promise<void>; | 是 | 解綁工程與素材。返回的Promise對象需要resolve。 | 3.0.0 |
submitASRJob | (mediaId: string, startTime: string, duration: string) => Promise<ASRResult[]>; | 否 | 提交智能識別字幕任務。返回的Promise對象需要resolve識別的結果ASRResult數組。 | 3.1.0 推薦使用AsrConfig,使用AsrConfig會覆蓋submitASRJob方法。 |
submitAudioProduceJob | (text: string, voice: string, voiceConfig?: VoiceConfig) => Promise<InputMedia>; | 否 | 提交文字轉語音任務,參數依次為:字幕內容、語音效果voice值和語音配置。返回的Promise對象需要resolve生成語音的數據。 | 4.3.5 推薦使用TTSConfig,使用TTSConfig會覆蓋submitAudioProduceJob方法。 |
licenseConfig | 是 | WebSDK的使用需要進行license配置。只有配置了license,才能在線上環境中使用WebSDK,如果沒有配置license,只能在localhost域名下使用,在沒有配置license的情況下,在localhost使用會出現水印,而在在線上環境中進行預覽時會出現黑屏情況。 | 5.0.1 | |
dynamicSrcQps | number | 否 | 限制dynamicSrc的請求頻率。 | 4.13.0 |
getTimelineMaterials | (params: TimelineMaterial[]) => Promise<InputMedia[]> | 否 | 獲取timeline中的素材媒資信息,以用于獲取在getEditingProjectMaterials中未注冊的媒資,例如:第三方媒資。 | 4.13.4 |
asrConfig | 否 | 提交智能字幕任務的配置。 | 4.13.0 | |
ttsConfig | 否 | 提交智能配音任務的配置。 | 5.0.1 | |
disableAutoJobModal | boolean | 否 | 關閉項目出現AI任務時自動打開的彈窗。 | 5.0.1 |
disableGreenMatting | boolean | 否 | 關閉綠幕摳圖入口。 | 4.13.0 |
disableRealMatting | boolean | 否 | 關閉實景摳圖入口。 | 4.13.0 |
disableDenoise | boolean | 否 | 關閉降噪入口。 | 4.13.0 |
audioWaveRenderDisabled | boolean | 否 | 關閉波形圖渲染。 | 4.13.0 |
publicMaterials | 否 | 公共素材庫配置。 | 4.13.0 | |
subtitleConfig | 否 | 字幕背景漸變等配置。 | 4.13.0 | |
getStickerCategories | () => Promise<StickerCategory[]>; | 否 | 獲取貼紙分類,如果不傳,則不分類。返回的Promise對象需要resolve貼紙的分類數組。 | 3.0.0 |
getStickers | (config: {categoryId?: string; page: number; size: number}) => Promise<StickerResponse>; | 否 | 獲取貼紙,如果貼紙沒有分類,則categoryId 為空。返回的Promise對象需要resolve貼紙的總量和貼紙數組。 | 3.0.0 |
getEditingProject | () => Promise<{timeline?: Timeline; projectId?: string; modifiedTime?: string}>; | 是 | 獲取工程的時間線。返回的Promise對象需要resolve時間線Timeline數據、項目ID和最后修改時間。 | 3.0.0 |
updateEditingProject | (data: {coverUrl: string; duration: number; timeline: Timeline; isAuto: boolean}) => Promise<{projectId: string}>; | 是 | 保存工程的時間線,參數依次為:工程的封面圖地址、時長(單位:秒)、Timeline數據和是否自動保存(當前每分鐘自動保存1次)。返回的Promise對象需要resolve項目ID。 | 3.0.0 |
produceEditingProjectVideo | (data:{ coverUrl: string; duration: number; aspectRatio: PlayerAspectRatio; mediaMarks: MediaMark[]; timeline: Timeline; recommend: IProduceRecommend; }) => Promise<void>; | 是 | 生成視頻,參數依次為:工程的封面圖地址、時長(單位:秒)、視頻比例、媒資標記、Timeline數據和recommend (視頻合成的分辨率、碼率的推薦數據)。返回的Promise對象需要resolve。 | 4.4.0 |
customTexts | {importButton?:string;updateButton?:string;produceButton?:string;backButton?:string;logoUrl?:string;} | 否 | 自定義部分文案,參數依次對應視頻剪輯界面的導入素材、保存、導出視頻、返回按鈕的文案和左上角Logo。 | 3.7.0 |
customFontList | Array<string | CustomFontItem>; | 否 | 自定義字體類型。 | 3.10.0 |
customVoiceGroups | 否 | 自定義語音選項。 | 4.3.5 | |
getPreviewWaterMarks | () => Promise<Array<{ url?: string; mediaId?:string; width?: number; height?: number; x?: number; y?: number; xPlusWidth?: number; yPlusHeight?: number; opacity?: number; }>>; | 否 | 預覽區添加水印,防止截屏(合成時沒有水?。瑓等缦滤荆?/p>
| 4.3.5 |
exportVideoClipsSplit | (data: Array<{ coverUrl: string; duration: number; aspectRatio: PlayerAspectRatio; mediaMarks: MediaMark[]; timeline: Timeline; recommend?: IProduceRecommend; }>) => Promise<void>; | 否 | 將選中Timeline的多個獨立片段拆分為不同Timeline并導出,參數依次為:默認封面圖、導出Timeline的時長、導出比例、媒資標記、導出的Timeline片段、視頻合成的分辨率或碼率的推薦數據。 | 4.4.0 |
exportFromMediaMarks | (data: Array<{coverUrl: string; duration: number; aspectRatio: PlayerAspectRatio; mediaMarks: MediaMark[]; timeline:Timeline; recommend?: IProduceRecommend;}>,) => Promise<void>; | 否 | 將選中Timeline的多個標記片段拆分為不同Timeline并導出,參數依次為:默認封面圖、導出Timeline的時長、導出比例、媒資標記、導出的Timeline片段、視頻合成的分辨率或碼率的推薦數據。 | 4.4.5 |
exportVideoClipsMerge | (data: { coverUrl: string; duration: number; aspectRatio: PlayerAspectRatio; mediaMarks: MediaMark[]; timeline: Timeline; recommend?:IProduceRecommend; }) => Promise<void>; | 否 | 將選中Timeline同一軌道的多個獨立片段合成為一個Timeline并導出,參數依次為:默認封面圖、導出Timeline的時長、導出比例、媒資標記、導出的Timeline片段、視頻合成的分辨率或碼率的推薦數據。 | 4.4.0 |
getAudioByMediaId | (mediaId: string) =>Promise<string>; | 否 | 獲取音頻地址,用于提升視頻的音頻波形圖繪制速度。傳入的參數為視頻素材ID,初始化時傳入該參數,SDK會優先使用其返回的音頻地址解析出視頻的音頻波形。返回的Promise對象需要resolve音頻地址URL。 | 4.3.5 |
hasTranscodedAudio | boolean | 否 | 導入工程中的所有視頻是否有代理音頻(轉碼后的音頻),取值:
| 4.3.6 |
avatarConfig | 否 | 數字人接入配置。 | 4.10.0 | |
disableAutoAspectRatio | boolean | 否 | 是否關閉根據素材分辨率切換比例彈窗。 | 4.12.2 |
videoTranslation | VideoTranslation | 否 | 視頻翻譯相關配置 | 5.1.0 |
數據結構說明
PlayerAspectRatio
enum PlayerAspectRatio { w1h1 = '1:1', w2h1 = '2:1', w4h3 = '4:3', w3h4 = '3:4', w9h16 = '9:16', w16h9 = '16:9', w21h9 = '21:9', }
VoiceConfig
interface VoiceConfig { volume: number; // 音量,取值0~100,默認值50 speech_rate: number; // 語速,取值范圍:-500~500,默認值:0 pitch_rate: number; // 語調,取值范圍:-500~500,默認值:0 format?: string; // 輸出文件格式,支持:PCM/WAV/MP3 }
InputMedia
type InputMedia = (InputVideo | InputAudio | InputImage) interface InputSource { sourceState?: 'ready' | 'loading' | 'fail'; } type MediaIdType = 'mediaId' | 'mediaURL'; interface SpriteConfig { num: string; lines: string; cols: string; cellWidth?: string; cellHeight?: string; } interface MediaMark { startTime: number; endTime: number; content: string; } interface InputVideo extends InputSource { mediaId: string; mediaIdType?: MediaIdType; mediaType: 'video'; video: { title: string; coverUrl?: string; duration: number; format?: string; src?: string; snapshots?: string[]; sprites?: string[]; spriteConfig?: SpriteConfig; width?: number; height?: number; rotate?: number; bitrate?: number; fps?: number; hasTranscodedAudio?: true; agentAudioSrc?: string; marks?: MediaMark[]; codec?: string; }; } interface InputAudio extends InputSource { mediaId: string; mediaIdType?: MediaIdType; mediaType: 'audio'; audio: { title: string; duration: number; coverUrl?: string; src?: string; marks?: MediaMark[]; formatNames?: string[]; }; } interface InputImage extends InputSource { mediaId: string; mediaIdType?: MediaIdType; mediaType: 'image'; image: { title: string; coverUrl?: string; src?: string; width?: number; height?: number; rotate?: number; }; } type TimelineMaterial = { mediaIdType: MediaIdType; mediaId: string; mediaType: MediaType };
MediaMark
interface MediaMark { startTime: number; endTime: number; content: string; }
ASRResult
interface ASRResult { content: string; // 字幕的內容 from: number; // 字幕的開始相對于識別素材的開始的時間偏移量 to: number; // 字幕的結束相對于識別素材的開始的時間偏移量 }
StickerCategory
interface StickerCategory { id: string; // 分類的 id name: string; // 分類的名稱,調用者自行切換語言 }
StickerResponse
interface Sticker { mediaId: string; src: string; } interface StickerResponse { total: number; stickers: Sticker[]; }
IProduceRecommend
interface IProduceRecommend { width?: number; height?: number; bitrate?: number; }
CustomFontItem
interface CustomFontItem { key: string; // 字體唯一標識 name?: string; // 展示的名字,沒有用key url: string; // 字體地址 // 用于前、后端字體渲染保持一致,頁面文字渲染大小是您設置的字體大小乘以這個倍數 fontServerScale?: { // 普通字幕字體倍數 common: number; // 花字字體倍數 decorated: number; }; }
VoiceGroup
export interface VoiceGroup { type: string; // 分類 category:string; // 主分類 voiceList?: Voice[]; emptyContent?: { description: string; linkText: string; link: string; }; getVoiceList?: (page: number, pageSize: number) => Promise<{ items: Voice[]; total: number }>; getVoice?: (voiceId: string) => Promise<Voice | null>; getDemo?: (mediaId: string) => Promise<{ src: string }>; }
Voice
export interface Voice { voiceUrl?: string; // 示例音頻地址 demoMediaId?: string; // 示例音頻的播放地址 voiceType: VoiceType; // 類型 voice: string; // 人聲 key name: string; // “人名” desc: string; // 簡介 tag?: string; // 標簽 remark?: string; // 備注支持的語言等信息 custom?: boolean; // 是否專屬人聲 }
VoiceType
enum VoiceType { Male = 'Male', // 男聲 Female = 'Female', // 女聲 Boy = 'Boy', // 男孩童聲 Girl = 'Girl', // 女孩童聲 }
AvatarConfig
// 數字人配置說明 interface AvatarConfig { // 數字人列表 getAvatarList: () => DigitalHumanList[]; // 提交數字人任務 submitAvatarVideoJob: <T extends keyof DigitalHumanJobParamTypes>( job: DigitalHumanJob<T>, ) => Promise<DigitalHumanJobInfo>; // 獲取數字人任務結果 getAvatarVideoJob: (jobId: string) => Promise<DigitalHumanJobResult>; // 任務輪詢時間 refreshInterval: number; // 數字人輸出視頻配置 outputConfigs: Array<{ width: number; height: number; bitrates: number[]; }>; filterOutputConfig?: ( item: DigitalHuman, config: Array<{ width: number; height: number; bitrates: number[]; }>, ) => Array<{ width: number; height: number; bitrates: number[]; }>; } // 數字人詳細類型說明 // 數字人參數 interface DigitalHuman { avatarId: string; // 數字人ID avatarName: string; // 數字人名稱 coverUrl: string; // 數字人封面 videoUrl?: string; // 數字人視頻demo地址 outputMask?: boolean; // 是否輸出遮罩 transparent?: boolean; // 是否背景透明 } // 數字人列表 interface DigitalHumanList { default: boolean; id: string; name: string; getItems: (pageNo: number, pageSize: number) => Promise<{ total: number; items: DigitalHuman[] }>; } // 數字人提交任務返回的信息 interface DigitalHumanJobInfo { jobId: string; mediaId: string; } // 數字人任務參數類型 type DigitalHumanJobParamTypes = { text: {//文字驅動 text?: string; params?: DigitalHumanTextParams; output?: DigitalHumanOutputParams; }; audio: {//音頻文件驅動 mediaId?: string; params?: DigitalHumanAudioParams; output?: DigitalHumanOutputParams; }; }; // text|audio type DigitalHumanJobType = keyof DigitalHumanJobParamTypes; // 數字人文字驅動任務參數 type DigitalHumanTextParams = { voice: string; volume: number; speechRate: number; pitchRate: number; autoASRJob?: boolean; }; // 數字人音頻文件驅動任務參數 type DigitalHumanAudioParams = { title: string; autoASRJob?: boolean; }; // 數字人輸出視頻其他參數 type DigitalHumanOutputParams = { bitrate: number; width: number; height: number; }; // 生成數字人字幕切片類型 type SubtitleClip = { from: number; to: number; content: string }; // 數字人任務運行輪詢結果 interface DigitalHumanJobResult { jobId: string; mediaId: string; done: boolean; errorMessage?: string; job?: DigitalHumanJob<any>; video?: InputVideo; subtitleClips?: SubtitleClip[]; } // 數字人任務 type DigitalHumanJob<T extends DigitalHumanJobType> = { type: T; title: string; avatar: DigitalHuman; data: DigitalHumanJobParamTypes[T]; }; // 數字人生成視頻 interface InputVideo { mediaId: string; mediaType: 'video'; video: { title: string; coverUrl?: string; duration: number; src?: string; // 當 useDynamicUrl 為 true 時,src 可以不傳 snapshots?: string[]; sprites?: string[]; spriteConfig?: SpriteConfig;//精靈圖 width?: number; // 視頻源的寬度 height?: number; // 視頻源的高度 rotate?: number; // 視頻源的旋轉角度 bitrate?: number; // 視頻源的碼率 fps?: number; // 視頻源的幀率 hasTranscodedAudio?: true; // 是否含有轉碼后的音頻流 agentAudioSrc?: string; // 代理的音頻地址(用于分離音頻軌),當 useDynamicUrl 為 true 時,agentAudioSrc 可以不傳 marks?: MediaMark[];// 視頻標記 }; }
LicenseConfig
// license的配置 type LicenseConfig = { rootDomain?: string; // license使用的根域名,例如使用的域名是 editor.abc.com,這里填的值就是abc.com licenseKey?: string; // 申請的licenseKey,參考頂部使用說明在控制臺中申請 }
AsrConfig
// 智能生成字幕的配置 type AsrConfig = { interval?: number; // 輪詢時長,單位:毫秒 defaultText?: string; // 默認的文案 maxPlaceHolderLength?: number; // 默認的文案最大長度 submitASRJob: (mediaId: string, startTime: string, duration: string) => Promise<ASRJobInfo>; getASRJobResult?: (jobId: string) => Promise<ASRJobInfo>; } interface ASRJobInfo { jobId?: string; jobDone: boolean; jobError?: string; result?: ASRResult[]; }
TTSConfig
// 智能配音任務的配置 type TTSConfig = { interval?: number; // 輪詢時長,單位:毫秒 submitAudioProduceJob: (text: string, voice: string, voiceConfig?: VoiceConfig) => Promise<TTSJobInfo>; getAudioJobResult?: (jobId: string) => Promise<TTSJobInfo>; } interface VoiceConfig { volume: number; speech_rate: number; pitch_rate: number; format?: string; custom?: boolean; } interface TTSJobInfo { jobId?: string; jobDone: boolean; jobError?: string; asr?: AudioASRResult[]; result?: InputAudio | null; } interface AudioASRResult { begin_time?: string; end_time?: string; text?: string; content?: string; from?: number; to?: number; }
PublicMaterialLibrary
// 公共媒資庫的配置 type PublicMaterialLibrary = { getLists: () => Promise<MaterialList[]>; name?: string; pageSize?: number; // 單頁展示數量 }; type MaterialList = { name?: string; key: string; tag?: string; mediaType: 'video' | 'audio' | 'image'; styleType?: 'video' | 'audio' | 'image' | 'background'; getItems: ( pageIndex: number, pageSize: number, ) => Promise<{ items: InputMedia[]; end: boolean; }>; };
SubtitleConfig
type SubtitleConfig = { // 自定義紋理列表 customTextures?: { list: () => Promise< Array<{ key: string; url: string; }> >; // 添加自定義紋理 onAddTexture: () => Promise<{ key: string; url: string; }>; // 刪除自定義紋理 onDeleteTexture: (key: string) => Promise<void>; }; }
AliyunVideoEditor
// AliyunVideoEditor 實例方法 type AliyunVideoEditor = { init: (config: IConfig) => void; // 初始化編輯器 destroy: (keepState?: boolean) => boolean; // 銷毀編輯器 version: string | undefined; // 獲取編輯器版本 setCurrentTime: (currentTime: number) => void; // 設置編輯器預覽時間 getCurrentTime: () => number; // 獲取編輯器預覽時間 getDuration: () => number; // 獲取編輯器時長 addProjectMaterials: (materials: InputMedia[]) => void; // 添加項目素材到編輯器 setProjectMaterials: (materials: InputMedia[]) => void; // 設置項目素材到編輯器 updateProjectMaterials: (update: (materials: InputMedia[]) => InputMedia[]) => void; // 更新編輯器當前項目素材 deleteProjectMaterial: (mediaId: string) => void; // 刪除編輯器項目素材 setProjectTimeline: ({ VideoTracks, AudioTracks, AspectRatio }: CustomTimeline) => Promise<void>; // 設置編輯器的timeline getProjectTimeline: () => any; // 獲取編輯器的timeline getEvents: (eventType?: 'ui' | 'player' | 'error' | 'websdk' | 'timeline') => IObservable<EventData<any>>; // 獲取編輯器的事件 importSubtitles: (type: 'ass' | 'srt' | 'clip' | 'asr', config: string) => void; // 批量導入字幕到編輯器 }
VideoTranslation
type VideoTranslation = { language?: { // 源語言 source: Array<{ value: string; label: string; }>; // 目標語言 target: Array<{ value: string; label: string; }>; }; // 視頻翻譯 translation?: { interval?: number; submitVideoTranslationJob: (params: TranslationJobParams) => Promise<TranslationJobInfo>; getVideoTranslationJob: (jobId: string) => Promise<TranslationJobInfo>; }; // 字幕擦除 detext?: { interval?: number; submitDetextJob: (param: DetextJobParams) => Promise<DetextJobInfo>; getDetextJob: (jobId: string) => Promise<DetextJobInfo>; }; // 字幕提取 captionExtraction?: { interval?: number; submitCaptionExtractionJob: (param: CaptionExtractionJobParams) => Promise<CaptionExtractionJobInfo>; getCaptionExtractionJob: (jobId: string) => Promise<CaptionExtractionJobInfo>; }; }; interface TranslationJobParams { type: 'Video' | 'Text' | 'TextArray'; mediaId?: string; mediaIdType?: MediaIdType; text?: string; textArray?: string[]; editingConfig: { SourceLanguage: string; TargetLanguage: string; DetextArea?: string; SupportEditing?: boolean; SubtitleTranslate?: { TextSource: 'OCR' | 'SubtitleFile'; OcrArea?: string; SubtitleConfig?: string; }; }; } interface TranslationJobInfo { jobId?: string; jobDone: boolean; jobError?: string; result?: { video?: InputVideo; timeline?: string; text?: string; textArray?: Array<{ Target: string; Source: string; }>; }; } interface DetextJobParams { mediaId: string; mediaIdType: MediaIdType; box?: 'auto' | Array<[number, number, number, number]>; } interface DetextJobInfo { jobId?: string; jobDone: boolean; jobError?: string; result?: { video?: InputVideo; }; } interface CaptionExtractionJobParams { mediaId: string; mediaIdType: MediaIdType; box?: 'auto' | Array<[number, number, number, number]>; } interface CaptionExtractionJobInfo { jobId?: string; jobDone: boolean; jobError?: string; result?: { srtContent?: string; }; }
init()示例代碼
Web SDK只負責界面交互,不會發起請求,您需要通過Web SDK調用請求邏輯。請求本身應該先發送給您自己的服務端,您自己的服務端再根據AccessKey信息(AccessKey ID和AccessKey Secret)轉發給相關的阿里云OpenAPI。
// 注意,WebSDK 本身并不提供 request 這個方法,這里僅作為示例,您可以使用您喜歡的網絡請求庫,如 axios 等
window.AliyunVideoEditor.init({
container: document.getElementById('aliyun-video-editor'),
locale: 'zh-CN',
licenseConfig: {
rootDomain: "", // license使用的根域名,例如abc.com
licenseKey: "", // 申請的licenseKey,沒有配置licenseKey,在預覽時會出現水印,沒有配置license的情況下,只能在localhost的域名下預覽
},
useDynamicSrc: true, // 媒資庫默認情況下播放地址會過期,所以需要動態獲取
getDynamicSrc: (mediaId, mediaType) => new Promise((resolve, reject) => {
request('GetMediaInfo', { // http://bestwisewords.com/document_detail/197842.html
MediaId: mediaId
}).then((res) => {
if (res.code === '200') {
// 注意,這里僅作為示例,實際中建議做好錯誤處理,避免如 FileInfoList 為空數組時報錯等異常情況
resolve(res.data.MediaInfo.FileInfoList[0].FileBasicInfo.FileUrl);
} else {
reject();
}
});
}),
getEditingProjectMaterials: () => {
if (projectId) { // 如果沒有 projectId,需要先在智能媒體服務控制臺創建剪輯工程,獲取projectId。
return request('GetEditingProjectMaterials', { // http://bestwisewords.com/document_detail/209068.html
ProjectId: projectId
}).then((res) => {
const data = res.data.MediaInfos;
return transMediaList(data); // 需要做一些數據變換,具體參考后文
});
}
return Promise.resolve([]);
},
searchMedia: (mediaType) => { // mediaType 為用戶當前所在的素材 tab,可能為 video | audio | image,您可以根據這個參數對應地展示同類型的可添加素材
return new Promise((resolve) => {
// 調用方需要自己實現展示媒資、選擇媒資添加的界面,這里的 callDialog 只是一種示例,WebSDK 本身并不提供
// 關于展示媒資,請參考:http://bestwisewords.com/document_detail/197964.html
callDialog({
onSubmit: async (materials) => {
if (!projectId) { // 如果沒有 projectId,需要先創建工程,如果能確保有 projectId,則不需要該步
const addRes = await request('CreateEditingProject', { // http://bestwisewords.com/document_detail/197834.html
Title: 'xxxx',
});
projectId = addRes.data.Project.ProjectId;
}
// 組裝數據
const valueObj = {};
materials.forEach(({ mediaType, mediaId }) => {
if (!valueObj[mediaType]) {
valueObj[mediaType] = mediaId;
} else {
valueObj[mediaType] += mediaId;
}
})
const res = await request('AddEditingProjectMaterials', { // http://bestwisewords.com/document_detail/209069.html
ProjectId: projectId,
MaterialMaps: valueObj,
});
if (res.code === '200') {
return resolve(transMediaList(res.data.MediaInfos));
}
resolve([]);
}
});
});
},
deleteEditingProjectMaterials: async (mediaId, mediaType) => {
const res = await request('DeleteEditingProjectMaterials', { // http://bestwisewords.com/document_detail/209067.html
ProjectId: projectId,
MaterialType: mediaType,
MaterialIds: mediaId
});
if (res.code === '200') return Promise.resolve();
return Promise.reject();
},
getStickerCategories: async () => {
const res = await request('ListAllPublicMediaTags', { // http://bestwisewords.com/document_detail/207796.html
BusinessType: 'sticker',
WebSdkVersion: window.AliyunVideoEditor.version
});
const stickerCategories = res.data.MediaTagList.map(item => ({
id: item.MediaTagId,
name: myLocale === 'zh-CN' ? item.MediaTagNameChinese : item.MediaTagNameEnglish // myLocale 是您期望的語言
}));
return stickerCategories;
},
getStickers: async ({ categoryId, page, size }) => {
const params = {
PageNo: page,
PageSize: size,
IncludeFileBasicInfo: true,
MediaTagId: categoryId
};
const res = await request('ListPublicMediaBasicInfos', params); // http://bestwisewords.com/document_detail/207797.html
const fileList = res.data.MediaInfos.map(item => ({
mediaId: item.MediaId,
src: item.FileInfoList[0].FileBasicInfo.FileUrl
}));
return {
total: res.data.TotalCount,
stickers: fileList
};
},
getEditingProject: async () => {
if (projectId) {
const res = await request('GetEditingProject', { // http://bestwisewords.com/document_detail/197837.html
ProjectId: projectId
});
const timelineString = res.data.Project.Timeline;
return {
projectId,
timeline: timelineString ? JSON.parse(timelineString) : undefined,
modifiedTime: res.data.Project.ModifiedTime,
title:res.data.Project.Title // 項目標題
};
}
return {};
},
updateEditingProject: ({ coverUrl, duration, timeline, isAuto }) => new Promise((resolve, reject) => {
request('UpdateEditingProject', { // http://bestwisewords.com/document_detail/197835.html
ProjectId: projectId,
CoverURL: coverUrl,
Duration: duration,
Timeline: JSON.stringify(timeline)
}).then((res) => {
if (res.code === '200') {
// WebSDK 本身會進行自動保存,isAuto 則是告訴調用方這次保存是否自動保存,調用方可以控制只在手動保存時才展示保存成功的提示
!isAuto && Message.success('保存成功');
resolve();
} else {
reject();
}
});
}),
produceEditingProjectVideo: ({ coverUrl, duration = 0, aspectRatio, timeline, recommend }) => {
return new Promise((resolve) => {
callDialog({ // 調用方需要自己實現提交合成任務的界面,這里的 callDialog 只是一種示例
onSubmit: async ({ fileName, format, bitrate, description }) => { // 假設提交合成任務的界面讓你獲得了這些數據
// 先根據 fileName 和 format 拼接出存儲的 mediaURL
const mediaURL = `http://bucketName.oss-cn-hangzhou.aliyuncs.com/${fileName}.${format}`;
// 根據 WebSDK 傳入的預覽比例來決定合成的寬高
const width = aspectRatio === '16:9' ? 640 : 360;
const height = aspectRatio === '16:9' ? 360 : 640;
// 若視頻、圖片素材傳入的長寬、碼率等數據,那么該函數返回的數據中的 recommend 就會包含了根據所使用的視頻、圖片計算得到的推薦的分辨率和碼率
// recommend 數據結構可以查看 IProduceRecommend
// 你可以在提交界面上展示推薦數據或者直接使用在提交接口的參數里
const res = await request('SubmitMediaProducingJob', { // http://bestwisewords.com/document_detail/197853.html
OutputMediaConfig: JSON.stringify({
mediaURL,
bitrate: recommend.bitrate || bitrate,
width: recommend.width || width,
height: recommend.height || height
}),
OutputMediaTarget: 'oss-object',
ProjectMetadata: JSON.stringify({ Description: description }),
ProjectId: projectId,
Timeline: JSON.stringify(timeline)
});
if (res.code === '200') {
Message.success('生成視頻成功');
}
resolve();
}
});
});
}
});
/**
* 將服務端的素材信息轉換成 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];
}
}