2023-10-03 11:14:36 +08:00
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
};
|