| /** | |
|  * Copyright(c) ali-sdk and other contributors. | |
|  * MIT Licensed | |
|  * | |
|  * Authors: | |
|  *   rockuw <rockuw@gmail.com> (http://rockuw.com) | |
|  */ | |
| 
 | |
| /** | |
|  * Module dependencies. | |
|  */ | |
| 
 | |
| const jstoxml = require('jstoxml'); | |
| const utility = require('utility'); | |
| const copy = require('copy-to'); | |
| const urlutil = require('url'); | |
| 
 | |
| const proto = exports; | |
| 
 | |
| /** | |
|  * RTMP operations | |
|  */ | |
| 
 | |
| /** | |
|  * Create a live channel | |
|  * @param {String} id the channel id | |
|  * @param {Object} conf the channel configuration | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.putChannel = async function putChannel(id, conf, options) { | |
|   options = options || {}; | |
|   options.subres = 'live'; | |
| 
 | |
|   const params = this._objectRequestParams('PUT', id, options); | |
|   params.xmlResponse = true; | |
|   params.content = jstoxml.toXML({ | |
|     LiveChannelConfiguration: conf | |
|   }); | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   let publishUrls = result.data.PublishUrls.Url; | |
|   if (!Array.isArray(publishUrls)) { | |
|     publishUrls = [publishUrls]; | |
|   } | |
|   let playUrls = result.data.PlayUrls.Url; | |
|   if (!Array.isArray(playUrls)) { | |
|     playUrls = [playUrls]; | |
|   } | |
| 
 | |
|   return { | |
|     publishUrls, | |
|     playUrls, | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Get the channel info | |
|  * @param {String} id the channel id | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.getChannel = async function getChannel(id, options) { | |
|   options = options || {}; | |
|   options.subres = 'live'; | |
| 
 | |
|   const params = this._objectRequestParams('GET', id, options); | |
|   params.xmlResponse = true; | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   return { | |
|     data: result.data, | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Delete the channel | |
|  * @param {String} id the channel id | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.deleteChannel = async function deleteChannel(id, options) { | |
|   options = options || {}; | |
|   options.subres = 'live'; | |
| 
 | |
|   const params = this._objectRequestParams('DELETE', id, options); | |
|   params.successStatuses = [204]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   return { | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Set the channel status | |
|  * @param {String} id the channel id | |
|  * @param {String} status the channel status | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.putChannelStatus = async function putChannelStatus(id, status, options) { | |
|   options = options || {}; | |
|   options.subres = { | |
|     live: null, | |
|     status | |
|   }; | |
| 
 | |
|   const params = this._objectRequestParams('PUT', id, options); | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   return { | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Get the channel status | |
|  * @param {String} id the channel id | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.getChannelStatus = async function getChannelStatus(id, options) { | |
|   options = options || {}; | |
|   options.subres = { | |
|     live: null, | |
|     comp: 'stat' | |
|   }; | |
| 
 | |
|   const params = this._objectRequestParams('GET', id, options); | |
|   params.xmlResponse = true; | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   return { | |
|     data: result.data, | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * List the channels | |
|  * @param {Object} query the query parameters | |
|  *  filter options: | |
|  *   - prefix {String}: the channel id prefix (returns channels with this prefix) | |
|  *   - marker {String}: the channle id marker (returns channels after this id) | |
|  *   - max-keys {Number}: max number of channels to return | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.listChannels = async function listChannels(query, options) { | |
|   // prefix, marker, max-keys | |
|  | |
|   options = options || {}; | |
|   options.subres = 'live'; | |
| 
 | |
|   const params = this._objectRequestParams('GET', '', options); | |
|   params.query = query; | |
|   params.xmlResponse = true; | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   let channels = result.data.LiveChannel || []; | |
|   if (!Array.isArray(channels)) { | |
|     channels = [channels]; | |
|   } | |
| 
 | |
|   channels = channels.map(x => { | |
|     x.PublishUrls = x.PublishUrls.Url; | |
|     if (!Array.isArray(x.PublishUrls)) { | |
|       x.PublishUrls = [x.PublishUrls]; | |
|     } | |
|     x.PlayUrls = x.PlayUrls.Url; | |
|     if (!Array.isArray(x.PlayUrls)) { | |
|       x.PlayUrls = [x.PlayUrls]; | |
|     } | |
| 
 | |
|     return x; | |
|   }); | |
| 
 | |
|   return { | |
|     channels, | |
|     nextMarker: result.data.NextMarker || null, | |
|     isTruncated: result.data.IsTruncated === 'true', | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Get the channel history | |
|  * @param {String} id the channel id | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.getChannelHistory = async function getChannelHistory(id, options) { | |
|   options = options || {}; | |
|   options.subres = { | |
|     live: null, | |
|     comp: 'history' | |
|   }; | |
| 
 | |
|   const params = this._objectRequestParams('GET', id, options); | |
|   params.xmlResponse = true; | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   let records = result.data.LiveRecord || []; | |
|   if (!Array.isArray(records)) { | |
|     records = [records]; | |
|   } | |
|   return { | |
|     records, | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Create vod playlist | |
|  * @param {String} id the channel id | |
|  * @param {String} name the playlist name | |
|  * @param {Object} time the begin and end time | |
|  *  time: | |
|  *   - startTime {Number}: the begin time in epoch seconds | |
|  *   - endTime {Number}: the end time in epoch seconds | |
|  * @param {Object} options | |
|  * @return {Object} | |
|  */ | |
| proto.createVod = async function createVod(id, name, time, options) { | |
|   options = options || {}; | |
|   options.subres = { | |
|     vod: null | |
|   }; | |
|   copy(time).to(options.subres); | |
| 
 | |
|   const params = this._objectRequestParams('POST', `${id}/${name}`, options); | |
|   params.query = time; | |
|   params.successStatuses = [200]; | |
| 
 | |
|   const result = await this.request(params); | |
| 
 | |
|   return { | |
|     res: result.res | |
|   }; | |
| }; | |
| 
 | |
| /** | |
|  * Get RTMP Url | |
|  * @param {String} channelId the channel id | |
|  * @param {Object} options | |
|  *  options: | |
|  *   - expires {Number}: expire time in seconds | |
|  *   - params {Object}: the parameters such as 'playlistName' | |
|  * @return {String} the RTMP url | |
|  */ | |
| proto.getRtmpUrl = function (channelId, options) { | |
|   options = options || {}; | |
|   const expires = utility.timestamp() + (options.expires || 1800); | |
|   const res = { | |
|     bucket: this.options.bucket, | |
|     object: this._objectName(`live/${channelId}`) | |
|   }; | |
|   const resource = `/${res.bucket}/${channelId}`; | |
| 
 | |
|   options.params = options.params || {}; | |
|   const query = Object.keys(options.params) | |
|     .sort() | |
|     .map(x => `${x}:${options.params[x]}\n`) | |
|     .join(''); | |
| 
 | |
|   const stringToSign = `${expires}\n${query}${resource}`; | |
|   const signature = this.signature(stringToSign); | |
| 
 | |
|   const url = urlutil.parse(this._getReqUrl(res)); | |
|   url.protocol = 'rtmp:'; | |
|   url.query = { | |
|     OSSAccessKeyId: this.options.accessKeyId, | |
|     Expires: expires, | |
|     Signature: signature | |
|   }; | |
|   copy(options.params).to(url.query); | |
| 
 | |
|   return url.format(); | |
| };
 |