智能生產製作提供專業線上的視訊剪輯能力,針對自動化、智能化剪輯以及多人協作視頻製作需求,您可以基於時間軸進行雲剪輯。通過閱讀本文,您可以瞭解如何接入視訊剪輯Web SDK。
使用說明
本文中引入的視訊剪輯Web SDK的版本號碼5.2.2(僅供參考),從5.0.0開始,您需要申請License授權後使用剪輯Web SDK。擷取最新的版本資訊,請參見視訊剪輯工程——協助資訊。
請提交工單申請License。
操作步驟
引入視訊剪輯Web SDK。
在專案前端分頁檔中的
<head>標籤處引入視訊剪輯Web SDK的CSS檔案,如下所示:<head> <link rel="stylesheet" href="https://g.alicdn.com/thor-server/video-editing-websdk/5.2.2/index.css"> </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; }>>; | 否 | 預覽區添加浮水印,防止截屏(合成時沒有浮水印),參數如下所示:
| 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', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-getmediainfo
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', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-geteditingprojectmaterials
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 本身並不提供
// 關於展示媒資,請參考:https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-listmediabasicinfos
callDialog({
onSubmit: async (materials) => {
if (!projectId) { // 如果沒有 projectId,需要先建立工程,如果能確保有 projectId,則不需要該步
const addRes = await request('CreateEditingProject', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-createeditingproject
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', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-addeditingprojectmaterials
ProjectId: projectId,
MaterialMaps: valueObj,
});
if (res.code === '200') {
return resolve(transMediaList(res.data.MediaInfos));
}
resolve([]);
}
});
});
},
deleteEditingProjectMaterials: async (mediaId, mediaType) => {
const res = await request('DeleteEditingProjectMaterials', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-deleteeditingprojectmaterials
ProjectId: projectId,
MaterialType: mediaType,
MaterialIds: mediaId
});
if (res.code === '200') return Promise.resolve();
return Promise.reject();
},
getStickerCategories: async () => {
const res = await request('ListAllPublicMediaTags', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-listallpublicmediatags
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); // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-listpublicmediabasicinfos
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', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-geteditingproject
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', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-updateeditingproject
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', { // https://www.alibabacloud.com/help/zh/ims/developer-reference/api-ice-2020-11-09-submitmediaproducingjob
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];
}
}