├── index.js ├── example ├── package.json ├── rtcV2.js ├── rtc.js └── app.js ├── .travis.yml ├── lib ├── config.js ├── index.js ├── util.js ├── hub.js ├── rtcV2.js ├── rtc.js ├── credentials.js ├── stream.js ├── request.js └── api.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require( "./lib/" ); 2 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "pili nodejs exmaple", 5 | "main": "app.js", 6 | "author": "0day.zh@gmail.com", 7 | "license": "MIT" 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '5' 4 | deploy: 5 | provider: npm 6 | email: sdk@qiniu.com 7 | api_key: 8 | secure: RoIRqmuGIVqMf6ZeE+HSo1WtjuSMUFwg+fm7DP8UL2S7JOylxKBD4xmyP+b16YGVpdpvyNuefeCPqZmSRu/Tx5XwJOi5WSF16585PqrUtuaWRTVDWGJUAfRejJRGNmHXq3yRiK79jbHMiRlEkEEg4I/T3xfXFl8K0FTRdlbuUH4= 9 | on: 10 | tags: true 11 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var config = module.exports = {}; 4 | 5 | config.const = { 6 | ORIGIN: 'ORIGIN', 7 | SDK_VERSION: '1.5.0', 8 | SDK_USER_AGENT: 'pili-sdk-nodejs', 9 | DEFAULT_API_HOST: 'pili.qiniuapi.com', 10 | DEFAULT_API_VERSION: 'v2', 11 | DEFAULT_RTC_HOST: 'rtc.qiniuapi.com' 12 | } 13 | 14 | config.API_HOST = config.const.DEFAULT_API_HOST; 15 | config.USE_HTTPS = false; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | exports.Hub = require('./hub'); 2 | exports.Stream = require('./stream'); 3 | exports.Credentials = require('./credentials'); 4 | exports.config = require('./config'); 5 | exports.publishURL = require('./api').publishURL; 6 | exports.rtmpPlayURL = require('./api').rtmpPlayURL; 7 | exports.hdlPlayURL = require('./api').hdlPlayURL; 8 | exports.hlsPlayURL = require('./api').hlsPlayURL; 9 | exports.snapshotPlayURL = require('./api').snapshotPlayURL; 10 | exports.RTC = require('./rtc.js'); 11 | exports.RTCV2 = require('./rtcV2.js'); -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto'); 4 | 5 | exports.base64ToUrlSafe = function(v) { 6 | return v.replace(/\//g, '_').replace(/\+/g, '-'); 7 | } 8 | 9 | exports.urlsafeBase64Encode = function(jsonFlags) { 10 | var encoded = new Buffer(jsonFlags).toString('base64'); 11 | return exports.base64ToUrlSafe(encoded); 12 | } 13 | 14 | exports.hmacSha1 = function(encodedFlags, secretKey) { 15 | var hmac = crypto.createHmac('sha1', secretKey); 16 | hmac.update(encodedFlags); 17 | return hmac.digest('base64'); 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | 4 | # Local test 5 | example/local-test.js 6 | 7 | # Logs 8 | logs 9 | *.log 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # Commenting this out is preferred by some people, see 30 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 31 | node_modules 32 | 33 | # Users Environment Variables 34 | .lock-wscript 35 | -------------------------------------------------------------------------------- /lib/hub.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Credentials = require('./credentials') 4 | const API = require('./api'); 5 | const Stream = require('./stream'); 6 | 7 | function Hub(credentials, hub) { 8 | this.credentials = credentials; 9 | this.hub = hub; 10 | 11 | API.init(); 12 | } 13 | 14 | Hub.prototype.newStream = function(key) { 15 | return new Stream(this.credentials, this.hub, key); 16 | } 17 | 18 | Hub.prototype.createStream = function(key, fn) { 19 | API.createStream(this.credentials, this.hub, key, fn); 20 | } 21 | 22 | Hub.prototype.listStreams = function(options, fn) { 23 | API.listStreams(this.credentials, this.hub, options, fn); 24 | } 25 | 26 | Hub.prototype.batchLiveStreamsInfo = function (items, fn) { 27 | API.batchLiveStreamsInfo(this.credentials, this.hub, items, fn); 28 | } 29 | 30 | module.exports = exports = Hub; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "piliv2", 3 | "version": "2.1.1", 4 | "description": "Pili Streaming Cloud Server-Side Library For NodeJS", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:pili-engineering/pili-sdk-nodejs.git" 12 | }, 13 | "keywords": [ 14 | "web-service", 15 | "rtmp", 16 | "hls", 17 | "hdl", 18 | "hds", 19 | "dash", 20 | "pili", 21 | "streaming", 22 | "qiniu", 23 | "cloud" 24 | ], 25 | "author": "Pili Engineering, Qiniu Inc.", 26 | "contributors": [ 27 | { 28 | "name": "sdk", 29 | "email": "sdk@qiniu.com" 30 | }, 31 | { 32 | "name": "pili", 33 | "email": "pili@qiniu.com" 34 | } 35 | ], 36 | "license": "MIT", 37 | "bugs": { 38 | "url": "https://github.com/pili-engineering/pili-sdk-nodejs/issues" 39 | }, 40 | "homepage": "https://github.com/pili-engineering/pili-sdk-nodejs" 41 | } 42 | -------------------------------------------------------------------------------- /lib/rtcV2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const API = require('./api'); 4 | 5 | function RTCV2(credentials) { 6 | this.credentials = credentials; 7 | 8 | API.init(); 9 | } 10 | 11 | RTCV2.prototype.createRoom = function(ownerID, roomName, options, fn) { 12 | 13 | return API.createRtcRoomV2(this.credentials, ownerID, roomName, options, fn); 14 | } 15 | 16 | RTCV2.prototype.getRoom = function(roomName, fn) { 17 | 18 | return API.getRtcRoomV2(this.credentials, roomName, fn); 19 | } 20 | 21 | RTCV2.prototype.deleteRoom = function(roomName, fn) { 22 | 23 | return API.deleteRtcRoomV2(this.credentials, roomName, fn); 24 | } 25 | 26 | RTCV2.prototype.roomToken = function(roomAccess) { 27 | 28 | /* 29 | roomAccess = { 30 | "version": "2.0" 31 | "room_name": "", 32 | "user_id": "", 33 | "perm": "", 34 | "expire_at": 35 | } 36 | */ 37 | 38 | if (!roomAccess.expire_at) { 39 | roomAccess.expire_at = Math.floor(Date.now() / 1000) + 1800; 40 | } 41 | 42 | return this.credentials.signJson(roomAccess); 43 | } 44 | 45 | 46 | 47 | module.exports = exports = RTCV2; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Pili Engineering, Qiniu Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /lib/rtc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const API = require('./api'); 4 | 5 | function RTC(credentials) { 6 | this.credentials = credentials; 7 | 8 | API.init(); 9 | } 10 | 11 | RTC.prototype.createRoom = function(ownerID, roomName, options, fn) { 12 | 13 | return API.createRtcRoom(this.credentials, ownerID, roomName, options, fn); 14 | } 15 | 16 | RTC.prototype.getRoom = function(roomName, fn) { 17 | 18 | return API.getRtcRoom(this.credentials, roomName, fn); 19 | } 20 | 21 | RTC.prototype.deleteRoom = function(roomName, fn) { 22 | 23 | return API.deleteRtcRoom(this.credentials, roomName, fn); 24 | } 25 | 26 | RTC.prototype.getRoomMember = function (roomName, fn) { 27 | 28 | return API.getRoomMember(this.credentials, roomName, fn); 29 | } 30 | 31 | RTC.prototype.deleteRoomMember = function (roomName, userId, fn) { 32 | return API.deleteRoomMember(this.credentials, roomName, userId, fn); 33 | } 34 | 35 | RTC.prototype.roomToken = function(roomAccess) { 36 | 37 | /* 38 | roomAccess = { 39 | "room_name": "", 40 | "user_id": "", 41 | "perm": "", 42 | "expire_at": 43 | } 44 | */ 45 | 46 | if (!roomAccess.expire_at) { 47 | roomAccess.expire_at = Math.floor(Date.now() / 1000) + 1800; 48 | } 49 | 50 | return this.credentials.signJson(roomAccess); 51 | } 52 | 53 | module.exports = exports = RTC; -------------------------------------------------------------------------------- /example/rtcV2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guhao on 2017/7/24. 3 | */ 4 | 'use strict'; 5 | 6 | var Pili = require('../index.js'); 7 | 8 | // ======================== Configurations ========================= 9 | // Replace with your keys here 10 | var ACCESS_KEY = 'FMVCRs2-LO1ivRNi4l7mEZE6ZDvPv-519D12kZO'; 11 | var SECRET_KEY = 'InOXBls8alaPiRcFn002XsoXKFw1iFJZxcoOveY'; 12 | 13 | // Replace with your hub name 14 | var HUB = 'qiniu-pili-test'; // The Hub must be exists before use 15 | 16 | 17 | /** 18 | * Instantiate a Pili rtc object 19 | */ 20 | var credentials = new Pili.Credentials(ACCESS_KEY, SECRET_KEY); 21 | var rtc = new Pili.RTCV2(credentials); 22 | 23 | var roomName = "room1"; 24 | var ownerID = 'ownerID'; 25 | rtc.createRoom(ownerID, roomName, {}, function(err, roomInfo) { 26 | if (!err) { 27 | console.log("success") 28 | console.log(roomInfo) 29 | } else { 30 | console.log("oops") 31 | console.log(err) 32 | } 33 | }) 34 | 35 | rtc.getRoom(roomName, function(err, roomInfo) { 36 | if (!err) { 37 | console.log("success") 38 | console.log(roomInfo) 39 | } else { 40 | console.log("oops") 41 | console.log(err) 42 | } 43 | }) 44 | 45 | rtc.deleteRoom(roomName, function(err) { 46 | if (!err) { 47 | console.log("success") 48 | console.log("delete OK") 49 | } else { 50 | console.log("oops") 51 | console.log(err) 52 | } 53 | }) 54 | 55 | 56 | var roomToken = rtc.roomToken({ 57 | "version": "2.0", 58 | "room_name": roomName, 59 | "user_id": ownerID, 60 | "perm": "user" 61 | }) 62 | console.log(roomToken); -------------------------------------------------------------------------------- /example/rtc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guhao on 2017/7/24. 3 | */ 4 | 'use strict'; 5 | 6 | var Pili = require('../index.js'); 7 | 8 | // ======================== Configurations ========================= 9 | // Replace with your keys here 10 | var ACCESS_KEY = 'FMVCRs2-LO1ivRNi4l7mEZE6ZDvPv-519D12kZCO'; 11 | var SECRET_KEY = 'InOXBls8alaPiRcFn002XsoXKFw1iFJZxcoOvAeY'; 12 | 13 | // Replace with your hub name 14 | var HUB = 'qiniu-pili-test'; // The Hub must be exists before use 15 | 16 | 17 | /** 18 | * Instantiate a Pili rtc object 19 | */ 20 | var credentials = new Pili.Credentials(ACCESS_KEY, SECRET_KEY); 21 | var rtc = new Pili.RTC(credentials); 22 | 23 | var roomName = 'roomName2'; 24 | var ownerID = 'ownerID'; 25 | rtc.createRoom(ownerID, roomName, {}, function(err, roomInfo) { 26 | if (!err) { 27 | console.log(roomInfo); 28 | rtc.getRoom(roomInfo.room_name, function(err, info) { 29 | if (!err) { 30 | console.log(info); 31 | rtc.deleteRoom(roomInfo.room_name, function(err) { 32 | if (!err) { 33 | console.log("delete ok"); 34 | } else { 35 | console.log(err); 36 | } 37 | }) 38 | } else { 39 | console.log(err); 40 | } 41 | }) 42 | } else { 43 | console.log(err); 44 | rtc.deleteRoom(roomName, function(err) {}); 45 | } 46 | }) 47 | 48 | var roomToken = rtc.roomToken({ 49 | "room_name": roomName, 50 | "user_id": ownerID, 51 | "perm": "user" 52 | }) 53 | console.log(roomToken); 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /lib/credentials.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('./util'); 4 | 5 | function Credentials(accessKey, secretKey) { 6 | this.accessKey = accessKey; 7 | this.secretKey = secretKey; 8 | } 9 | 10 | Credentials.prototype.generateAccessToken = function(options, data) { 11 | var sign = this._signRequest(options, data); 12 | var token = 'Qiniu' + ' ' + this.accessKey + ':' + sign; 13 | 14 | return token; 15 | } 16 | 17 | Credentials.prototype._signRequest = function(options, body) { 18 | var contentType = options.headers['Content-Type']; 19 | 20 | var host = options.host; 21 | if (options.port && options.port != 80) { 22 | host = host + ':' + options.port; 23 | } 24 | 25 | var data = options.method + ' ' + options.path; 26 | data += '\nHost: ' + host; 27 | if (contentType) { 28 | data += '\nContent-Type: ' + contentType; 29 | } 30 | data += '\n\n'; 31 | 32 | if (body && contentType && contentType != 'application/octet-stream') { 33 | data += body; 34 | } 35 | 36 | var digest = util.hmacSha1(data, this.secretKey); 37 | var sageDigest = util.base64ToUrlSafe(digest); 38 | 39 | return sageDigest; 40 | } 41 | 42 | Credentials.prototype.sign = function(data) { 43 | var digest = util.hmacSha1(data, this.secretKey); 44 | var sageDigest = util.base64ToUrlSafe(digest); 45 | return this.accessKey + ":" + sageDigest; 46 | 47 | } 48 | 49 | Credentials.prototype.signJson = function(opt) { 50 | 51 | var str = JSON.stringify(opt); 52 | var encodedStr = util.urlsafeBase64Encode(str); 53 | var sign = util.hmacSha1(encodedStr, this.secretKey); 54 | var encodedSign = util.base64ToUrlSafe(sign); 55 | 56 | var token = this.accessKey + ':' + encodedSign + ':' + encodedStr; 57 | return token; 58 | } 59 | 60 | module.exports = exports = Credentials; 61 | -------------------------------------------------------------------------------- /lib/stream.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const API = require('./api'); 4 | 5 | function Stream(credentials, hub, key) { 6 | 7 | this.credentials = credentials; 8 | 9 | this.key = key; 10 | this.hub = hub; 11 | } 12 | 13 | /** 14 | * Stream operations 15 | */ 16 | Stream.prototype.loadInfo = function(fn) { 17 | 18 | var self = this; 19 | API.getStreamInfo(this.credentials, self.hub, self.key, function(err, rst) { 20 | if (err) { 21 | fn(err); 22 | } else { 23 | // self = this ? 24 | self.createdAt = rst.createdAt; 25 | self.updatedAt = rst.updatedAt; 26 | self.expireAt = rst.expireAt; 27 | self.disabledTill = rst.disabledTill; 28 | self.converts = rst.converts; 29 | fn(null); 30 | } 31 | }); 32 | } 33 | 34 | Stream.prototype.liveInfo = function(fn) { 35 | 36 | API.streamLiveInfo(this.credentials, this.hub, this.key, function(err, jsonData) { 37 | fn(err, jsonData); 38 | }); 39 | } 40 | 41 | Stream.prototype.disable = function(disabledTill, fn) { 42 | 43 | var self = this; 44 | API.disableStream(this.credentials, this.hub, this.key, disabledTill, function(err, jsonData) { 45 | if (!err) { 46 | self.disabledTill = disabledTill; 47 | fn(null); 48 | } else { 49 | fn(err) 50 | } 51 | }); 52 | } 53 | 54 | Stream.prototype.enable = function(fn) { 55 | 56 | var self = this; 57 | API.disableStream(this.credentials, this.hub, this.key, 0, function(err, jsonData) { 58 | if (!err) { 59 | self.disabledTill = 0; 60 | fn(null); 61 | } else { 62 | fn(err) 63 | } 64 | }); 65 | } 66 | 67 | /** 68 | 将指定时间段的直播保存到存储空间 69 | options 可以带如下参数: 70 | fname: 保存到存储空间的文件名,可选,不指定系统会随机生成 71 | start: 整数,可选,Unix 时间戳,要保存的直播的起始时间,不指定或 0 值表示从第一次直播开始 72 | end: 整数,可选,Unix 时间戳,要保存的直播的结束时间,不指定或 0 值表示当前时间 73 | format: 保存的文件格式,可选,默认为m3u8,如果指定其他格式,则保存动作为异步模式 74 | pipeline: 异步模式时,dora的私有队列,可选,不指定则使用公共队列 75 | notify: 异步模式时,保存成功回调通知地址,可选,不指定则不通知 76 | expireDays: 更改ts文件的过期时间,可选,默认为永久保存 -1表示不更改ts文件的生命周期,正值表示修改ts文件的生命周期为expireDays 77 | */ 78 | Stream.prototype.savePlayback = function(options, fn) { 79 | API.savePlayback(this.credentials, this.hub, this.key, options, function(err, jsonData) { 80 | if (err) { 81 | fn(err, null); 82 | } else { 83 | fn(null, jsonData['fname']); 84 | } 85 | }); 86 | } 87 | 88 | /** 89 | 把指定时间点的直播截图保存到 bucket 里 90 | options 可以带如下参数: 91 | fname: 保存到存储空间的文件名,可选,不指定系统会随机生成 92 | time: 整数,可选,Unix 时间戳,要保存截图的时间点,不指定则为当前时间 93 | format: 保存的文件格式,可选,默认为jpg 94 | */ 95 | Stream.prototype.saveSnapshot = function(options, fn) { 96 | API.saveSnapshot(this.credentials, this.hub, this.key, options, function(err, jsonData) { 97 | if (err) { 98 | fn(err, null); 99 | } else { 100 | fn(null, jsonData['fname']); 101 | } 102 | }); 103 | } 104 | 105 | /** 106 | 修改转码配置 107 | 推流开始时如果有转码配置会触发转码,推流过程中修改转码配置不会立即生效,需要重新推流。 108 | 推流过程中,在直播地址后面添加 @ 即可播放转码流。 109 | 例: 一路流的直播地址是 rtmp://live-rtmp.test.com/PiliSDKTest/streamtitle, 是 720p,则对应转码流的直播地址是 rtmp://live-rtmp.test.com/PiliSDKTest/streamtitle@720p 110 | 111 | profiles 以数组的形式指定相应的配置即可,如:['480p', '720p'] 112 | */ 113 | Stream.prototype.updateConverts = function(profiles, fn) { 114 | API.updateConverts(this.credentials, this.hub, this.key, profiles, function(err, jsonData) { 115 | if (err) { 116 | fn(err); 117 | } else { 118 | fn(null); 119 | } 120 | }); 121 | } 122 | 123 | /** 124 | 查询直播的历史时间记录 125 | options 可以带如下参数: 126 | start: 整数,可选,Unix 时间戳,起始时间,不指定或 0 表示不限制起始时间。 127 | end: 整数,可选,Unix 时间戳,结束时间,不指定或 0 值表示当前时间。 128 | */ 129 | Stream.prototype.publishHistory = function(options, fn) { 130 | API.publishHistory(this.credentials, this.hub, this.key, options, function(err, rst) { 131 | if (err) { 132 | fn(err, null); 133 | } else { 134 | fn(err, rst.items); 135 | } 136 | }); 137 | } 138 | 139 | module.exports = exports = Stream; -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var http = require('http'), 4 | queryString = require('querystring'), 5 | config = require('./config'); 6 | 7 | function Request(opt) { 8 | 9 | if (!opt) { 10 | opt = {}; 11 | } 12 | if (opt.port) { 13 | this.port = opt.port; 14 | } else { 15 | this.port = config.API_PORT; 16 | } 17 | 18 | if (opt.host) { 19 | this.host = opt.host; 20 | } else { 21 | this.host = config.API_HOST; 22 | } 23 | 24 | if (opt.apiVersion) { 25 | this.apiVersion = opt.apiVersion; 26 | } else { 27 | this.apiVersion = config.const.DEFAULT_API_VERSION; 28 | } 29 | } 30 | 31 | Request.prototype.get = function(credentials, path, data, fn) { 32 | var reqPath = '/' + this.apiVersion + path; 33 | 34 | if (data && Object.keys(data).length > 0) { 35 | reqPath += '?' + queryString.stringify(data); 36 | } 37 | 38 | var headers = {}; 39 | var options = { 40 | host: this.host, 41 | port: this.port, 42 | path: reqPath, 43 | method: 'GET', 44 | headers: headers 45 | }; 46 | options.headers['Authorization'] = credentials.generateAccessToken(options, null); 47 | 48 | var req = http.request(options, function(res) { 49 | res.setEncoding('utf-8'); 50 | 51 | var responseString = ''; 52 | 53 | res.on('data', function(data) { 54 | responseString += data; 55 | }); 56 | 57 | res.on('end', function() { 58 | var resultObject = JSON.parse(responseString); 59 | 60 | if (res.statusCode >= 400) { 61 | var err = new Error(resultObject['message']); 62 | err.errorCode = resultObject['error']; 63 | err.httpCode = res.statusCode; 64 | 65 | fn(err, null); 66 | } else { 67 | 68 | fn(null, resultObject); 69 | } 70 | }); 71 | }); 72 | 73 | req.on('error', function(e) { 74 | fn(e, null); 75 | }); 76 | 77 | req.end(); 78 | }; 79 | 80 | Request.prototype.post = function(credentials, path, data, fn) { 81 | var dataString = JSON.stringify(data); 82 | var headers = { 83 | 'Content-Type': 'application/json', 84 | 'Content-Length': dataString.length 85 | }; 86 | 87 | var options = { 88 | host: this.host, 89 | port: this.port, 90 | path: '/' + this.apiVersion + path, 91 | method: 'POST', 92 | headers: headers 93 | }; 94 | options.headers['Authorization'] = credentials.generateAccessToken(options, dataString); 95 | 96 | var req = http.request(options, function(res) { 97 | res.setEncoding('utf-8'); 98 | 99 | var responseString = ''; 100 | 101 | res.on('data', function(data) { 102 | responseString += data; 103 | }); 104 | 105 | res.on('end', function() { 106 | var resultObject = JSON.parse(responseString); 107 | 108 | if (res.statusCode >= 400) { 109 | var err = new Error(resultObject['message']); 110 | err.errorCode = resultObject['error']; 111 | err.httpCode = (resultObject['error'] / 1000).toFixed(0); 112 | fn(err, null); 113 | } else { 114 | fn(null, resultObject); 115 | } 116 | }); 117 | }); 118 | 119 | req.on('error', function(e) { 120 | fn(e, null); 121 | }); 122 | 123 | req.write(dataString); 124 | 125 | req.end(); 126 | }; 127 | 128 | Request.prototype.delete = function(credentials, path, fn) { 129 | var headers = {}; 130 | var options = { 131 | host: this.host, 132 | port: this.port, 133 | path: '/' + this.apiVersion + path, 134 | method: 'DELETE', 135 | headers: headers 136 | }; 137 | options.headers['Authorization'] = credentials.generateAccessToken(options, null); 138 | 139 | var req = http.request(options, function(res) { 140 | res.setEncoding('utf-8'); 141 | 142 | var responseString = ''; 143 | 144 | res.on('data', function(data) { 145 | responseString += data; 146 | }); 147 | 148 | res.on('end', function() { 149 | var resultObject = responseString.length > 0 ? JSON.parse(responseString) : null; 150 | 151 | if (res.statusCode >= 400) { 152 | var err = new Error(resultObject['message']); 153 | err.errorCode = resultObject['error']; 154 | err.httpCode = (resultObject['error'] / 1000).toFixed(0); 155 | fn(err, null); 156 | } else { 157 | fn(null, resultObject); 158 | } 159 | }); 160 | }); 161 | 162 | req.on('error', function(e) { 163 | fn(e, null); 164 | }); 165 | 166 | req.end(); 167 | }; 168 | 169 | module.exports = exports = Request; -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Pili = require('../index.js'); 4 | 5 | // ======================== Configurations ========================= 6 | // Replace with your keys here 7 | var ACCESS_KEY = 'QiniuAccessKey'; 8 | var SECRET_KEY = 'QiniuSecretKey'; 9 | var credentials = new Pili.Credentials(ACCESS_KEY, SECRET_KEY); 10 | 11 | 12 | // Replace with your hub name 13 | var HUB = 'PiliHubName'; // The Hub must be exists before use 14 | 15 | // Change API host as necessary 16 | // 17 | // pili.qiniuapi.com as default 18 | // pili-lte.qiniuapi.com is the latest RC version 19 | // 20 | Pili.config.API_HOST = 'pili.qiniuapi.com'; // default 21 | 22 | // ========================== Hub ============================ 23 | 24 | 25 | var streamKey = 'streamkey'; 26 | 27 | /** 28 | * URLs 29 | */ 30 | 31 | var pu = Pili.publishURL(credentials,'publish-rtmp.test.com', 'PiliSDKTest', 'streamkey', 60); 32 | var rtmpURL = Pili.rtmpPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 33 | var hdlURL = Pili.hdlPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 34 | var hlsURL = Pili.hlsPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 35 | var snapURL = Pili.snapshotPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 36 | console.log(pu, rtmpURL, hdlURL, hlsURL, snapURL); 37 | 38 | /** 39 | * Instantiate a Pili hub object 40 | */ 41 | 42 | var hub = new Pili.Hub(credentials, HUB); 43 | 44 | /** 45 | * Create a new Stream 46 | */ 47 | 48 | hub.createStream(streamKey, function(err, stream) { 49 | 50 | if (!err) { 51 | console.log(stream); 52 | } else { 53 | // Log error 54 | console.log(err, err.errorCode, err.httpCode); 55 | } 56 | }); 57 | 58 | /** 59 | * List Streams 60 | */ 61 | 62 | var listOptions = { 63 | 'liveonly': false, 64 | 'prefix': 'lin', 65 | 'limit': 2, 66 | }; 67 | 68 | var listCallBack = function(err, marker, streams) { 69 | if (!err) { 70 | streams.forEach(function(stream) { 71 | console.log(stream); 72 | }); 73 | if (marker) { 74 | listOptions.marker = marker; 75 | hub.listStreams(listOptions, listCallBack); 76 | } 77 | } else { 78 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 79 | } 80 | } 81 | 82 | hub.listStreams(listOptions, listCallBack); 83 | 84 | 85 | /** 86 | * Get Streams Live Info 87 | */ 88 | hub.batchLiveStreamsInfo([streamKey], function (err, items) { 89 | if (!err) { 90 | console.log('live streams: '); 91 | items.forEach(function(item) { 92 | console.log(item); 93 | }); 94 | } 95 | else { 96 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 97 | } 98 | }); 99 | 100 | /** 101 | * Get Stream Info 102 | */ 103 | var stream = hub.newStream(streamKey); 104 | 105 | stream.loadInfo(function(err) { 106 | if (!err) { 107 | console.log(stream); 108 | } else { 109 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 110 | } 111 | }) 112 | 113 | // * Disable a Stream 114 | // */ 115 | stream.disable(-1, function(err) { 116 | console.log(stream.disabledTill); 117 | }); 118 | 119 | 120 | /** 121 | * Enable a Stream 122 | */ 123 | stream.enable(function(err) { 124 | console.log(stream.disabledTill); 125 | }); 126 | 127 | 128 | /** 129 | * Get Stream Live Info 130 | */ 131 | stream.liveInfo(function(err, status) { 132 | if (!err) { 133 | console.log(status); 134 | } else { 135 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 136 | } 137 | }); 138 | 139 | /** 140 | * Get Stream segments 141 | */ 142 | var savePlaybackOptions = { 143 | fname: streamKey, // optional, file name 144 | start : null, // optional, in second, unix timestamp 145 | end : null // optional, in second, unix timestamp 146 | }; 147 | 148 | stream.savePlayback(savePlaybackOptions, function(err, m3u8Name) { 149 | 150 | if (!err) { 151 | console.log(m3u8Name); 152 | } else { 153 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 154 | } 155 | }); 156 | 157 | /** 158 | * Get Stream snapshot 159 | */ 160 | var saveSnapshotOptions = { 161 | fname: streamKey // optional, file name 162 | }; 163 | 164 | stream.saveSnapshot(saveSnapshotOptions, function(err, snapshotName) { 165 | if (!err) { 166 | console.log(snapshotName); 167 | } else { 168 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 169 | } 170 | }); 171 | 172 | /** 173 | * Update Stream Converts 174 | */ 175 | stream.updateConverts(['480p', '720p'], function (err) { 176 | if (!err) { 177 | console.log('update converts success'); 178 | } 179 | else { 180 | console.log('update converts error: ' + err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 181 | } 182 | }) 183 | 184 | /** 185 | * Get Stream history activity 186 | */ 187 | 188 | var publishHistoryOptions = { 189 | start : null, // optional, in second, unix timestamp 190 | end : null, // optional, in second, unix timestamp 191 | }; 192 | stream.publishHistory(publishHistoryOptions, function(err, history) { 193 | if (!err) { 194 | console.log(history); 195 | } else { 196 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 197 | } 198 | }) 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)* 4 | 5 | - [Pili Streaming Cloud server-side library for NodeJS](#pili-streaming-cloud-server-side-library-for-nodejs) 6 | - [Features](#features) 7 | - [Installaion](#installaion) 8 | - [Usage](#usage) 9 | - [Configuration](#configuration) 10 | - [URL](#url) 11 | - [Generate RTMP publish URL](#generate-rtmp-publish-url) 12 | - [Generate RTMP play URL](#generate-rtmp-play-url) 13 | - [Generate HLS play URL](#generate-hls-play-url) 14 | - [Generate HDL play URL](#generate-hdl-play-url) 15 | - [Generate Snapshot play URL](#generate-snapshot-play-url) 16 | - [Hub](#hub) 17 | - [Instantiate a Pili Hub object](#instantiate-a-pili-hub-object) 18 | - [Create a new Stream](#create-a-new-stream) 19 | - [Get a Stream](#get-a-stream) 20 | - [LiveInfo](#liveinfo) 21 | - [List Stream](#list-stream) 22 | - [Get Streams live status](#get-streams-live-status) 23 | - [Stream](#stream) 24 | - [Disable a Stream](#disable-a-stream) 25 | - [Enable a Stream](#enable-a-stream) 26 | - [Get Stream live status](#get-stream-live-status) 27 | - [Get Stream history activity](#get-stream-history-activity) 28 | - [Save Stream live playback](#save-stream-live-playback) 29 | - [Save Stream snapshot](#save-stream-snapshot) 30 | - [Update Stream converts](#update-stream-converts) 31 | 32 | 33 | 34 | # Pili Streaming Cloud server-side library for NodeJS 35 | 36 | ## Features 37 | 38 | - URL 39 | - [x] RTMP推流地址: RTMPPublishURL(domain, hub, streamKey, mac, expireAfterSeconds) 40 | - [x] RTMP直播地址: RTMPPlayURL(domain, hub, streamKey) 41 | - [x] HLS直播地址: HLSPlayURL(domain, hub, streamKey) 42 | - [x] HDL直播地址: HDLPlayURL(domain, hub, streamKey) 43 | - [x] 截图直播地址: SnapshotPlayURL(domain, hub, streamKey) 44 | - Hub 45 | - [x] 创建流: hub.Create(streamKey) 46 | - [x] 获得流: hub.Stream(streamKey) 47 | - [x] 列出流: hub.List(prefix, limit, marker) 48 | - [x] 列出正在直播的流: hub.ListLive(prefix, limit, marker) 49 | - [x] 批量查询直播实时信息: hub.batchLiveStreamsInfo(items) 50 | - Stream 51 | - [x] 流信息: stream.Info() 52 | - [x] 禁用流: stream.Disable() 53 | - [x] 启用流: stream.Enable() 54 | - [x] 查询直播状态: stream.LiveStatus() 55 | - [x] 保存直播回放: stream.Save(start, end) 56 | - [x] 查询直播历史: stream.HistoryActivity(start, end) 57 | - [x] 保存直播截图: stream.saveSnapshot() 58 | - [x] 修改流转码配置: stream.updateConverts(profiles) 59 | 60 | 61 | ## Installaion 62 | 63 | ```shell 64 | // install latest version 65 | npm install piliv2 --save 66 | ``` 67 | 68 | ## Usage 69 | 70 | ### Configuration 71 | 72 | ```javascript 73 | var Pili = require('piliv2'); 74 | 75 | var ACCESS_KEY = 'QiniuAccessKey'; 76 | var SECRET_KEY = 'QiniuSecretKey'; 77 | 78 | var HUB = 'PiliHubName'; // The Hub must be exists before use 79 | 80 | var credentials = new Pili.Credentials(ACCESS_KEY, SECRET_KEY); 81 | ``` 82 | 83 | ### URL 84 | 85 | #### Generate RTMP publish URL 86 | 87 | ```javascript 88 | var url = Pili.publishURL(credentials,'publish-rtmp.test.com', 'PiliSDKTest', 'streamkey', 60); 89 | console.log(url); 90 | // rtmp://publish-rtmp.test.com/PiliSDKTest/streamkey?e=1466652726&token=9roGzeeJkZh4y5gHpzT7Uv1CIw0KiVV8K4dfXRY0:bYZGIdK-tjjAfyIwbShQ5Bb1kBY= 91 | ``` 92 | 93 | #### Generate RTMP play URL 94 | 95 | ```javascript 96 | var url = Pili.rtmpPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 97 | console.log(url); 98 | // rtmp://live-rtmp.test.com/PiliSDKTest/streamkey 99 | ``` 100 | 101 | #### Generate HLS play URL 102 | 103 | ```javascript 104 | var url = Pili.hlsPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 105 | console.log(url); 106 | // http://live-rtmp.test.com/PiliSDKTest/streamkey.m3u8 107 | ``` 108 | 109 | #### Generate HDL play URL 110 | 111 | ```javascript 112 | var url = Pili.hdlPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 113 | console.log(url); 114 | // http://live-rtmp.test.com/PiliSDKTest/streamkey.flv 115 | ``` 116 | 117 | #### Generate Snapshot play URL 118 | 119 | ```javascript 120 | var url = Pili.snapshotPlayURL('live-rtmp.test.com', 'PiliSDKTest', 'streamkey'); 121 | console.log(url); 122 | // http://live-rtmp.test.com/PiliSDKTest/streamkey.jpg 123 | ``` 124 | 125 | ### Hub 126 | 127 | #### Instantiate a Pili Hub object 128 | 129 | ```javascript 130 | var hub = new Pili.Hub(credentials, HUB); 131 | ``` 132 | 133 | #### Create a new Stream 134 | 135 | ```javascript 136 | hub.createStream(streamKey, function(err, stream) { 137 | 138 | if (!err) { 139 | console.log(stream); 140 | } else { 141 | // Log error 142 | console.log(err, err.errorCode, err.httpCode); 143 | } 144 | }); 145 | ``` 146 | 147 | #### Get a Stream 148 | 149 | ```javascript 150 | var stream = hub.newStream(streamKey); 151 | 152 | stream.loadInfo(function(err) { 153 | if (!err) { 154 | console.log(stream); 155 | } else { 156 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 157 | } 158 | }) 159 | ``` 160 | 161 | #### LiveInfo 162 | 163 | ```javascript 164 | stream.liveInfo(function(err, status) { 165 | if (!err) { 166 | console.log(status); 167 | } else { 168 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 169 | } 170 | }); 171 | ``` 172 | 173 | #### List Stream 174 | 175 | ```javascript 176 | var listOptions = { 177 | 'liveonly': false, 178 | 'prefix': '', 179 | 'limit': 2, 180 | }; 181 | 182 | var listCallBack = function(err, marker, streams) { 183 | if (!err) { 184 | streams.forEach(function(stream) { 185 | console.log(stream); 186 | }); 187 | if (marker) { 188 | listOptions.marker = marker; 189 | hub.listStreams(listOptions, listCallBack); 190 | } 191 | } else { 192 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 193 | } 194 | } 195 | 196 | hub.listStreams(listOptions, listCallBack); 197 | ``` 198 | 199 | #### Get Streams live status 200 | 201 | ```javascript 202 | hub.batchLiveStreamsInfo([streamKey], function (err, items) { 203 | if (!err) { 204 | console.log('live streams: '); 205 | items.forEach(function(item) { 206 | console.log(item); 207 | }); 208 | } 209 | else { 210 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 211 | } 212 | }); 213 | ``` 214 | 215 | ### Stream 216 | 217 | #### Disable a Stream 218 | 219 | ```javascript 220 | stream.disable(-1, function(err) { 221 | console.log(stream.disabledTill); 222 | }); 223 | ``` 224 | 225 | #### Enable a Stream 226 | 227 | ```javascript 228 | stream.enable(function(err) { 229 | console.log(stream.disabledTill); 230 | }); 231 | ``` 232 | 233 | #### Get Stream live status 234 | 235 | ```javascript 236 | stream.liveInfo(function(err, status) { 237 | if (!err) { 238 | console.log(status); 239 | } else { 240 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 241 | } 242 | }); 243 | ``` 244 | 245 | #### Get Stream history activity 246 | 247 | ```javascript 248 | var publishHistoryOptions = { 249 | start : null, // optional, in second, unix timestamp 250 | end : null, // optional, in second, unix timestamp 251 | }; 252 | stream.publishHistory(publishHistoryOptions, function(err, history) { 253 | if (!err) { 254 | console.log(history); 255 | } else { 256 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 257 | } 258 | }) 259 | ``` 260 | 261 | #### Save Stream live playback 262 | 263 | ```javascript 264 | var savePlaybackOptions = { 265 | start : null, // optional, in second, unix timestamp 266 | end : null, // optional, in second, unix timestamp 267 | format: null, // optional, file format 268 | }; 269 | 270 | stream.savePlayback(savePlaybackOptions, function(err, m3u8Name) { 271 | 272 | if (!err) { 273 | console.log(m3u8Name); 274 | } else { 275 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 276 | } 277 | }); 278 | ``` 279 | 280 | #### Save Stream snapshot 281 | 282 | ```javascript 283 | var saveSnapshotOptions = { 284 | fname: streamKey // optional, file name 285 | }; 286 | 287 | stream.saveSnapshot(saveSnapshotOptions, function(err, snapshotName) { 288 | if (!err) { 289 | console.log(snapshotName); 290 | } else { 291 | console.log(err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 292 | } 293 | }); 294 | ``` 295 | 296 | #### Update Stream converts 297 | 298 | ```javascript 299 | stream.updateConverts(['480p', '720p'], function (err) { 300 | if (!err) { 301 | console.log('update converts success'); 302 | } 303 | else { 304 | console.log('update converts error: ' + err + 'error code: ' + err.errorCode + 'http code: ' + err.httpCode); 305 | } 306 | }) 307 | ``` 308 | -------------------------------------------------------------------------------- /lib/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fmt = require('util'); 4 | const Request = require('./request'); 5 | const Util = require('./util'); 6 | const Stream = require('./stream'); 7 | const config = require('./config'); 8 | 9 | var API = {}; 10 | 11 | exports.API = API; 12 | 13 | exports.init = function() { 14 | if (!API.request) { 15 | API.request = new Request(); 16 | } 17 | if (!API.requestRTC) { 18 | API.requestRTC = new Request({ 19 | host: config.const.DEFAULT_RTC_HOST, 20 | apiVersion: "v1" 21 | }); 22 | } 23 | if(!API.requestRTCV2) { 24 | API.requestRTCV2 = new Request({ 25 | host: config.const.DEFAULT_RTC_HOST, 26 | apiVersion: "v2" 27 | }); 28 | } 29 | } 30 | 31 | // client APIs 32 | exports.createStream = function(credentials, hub, key, fn) { 33 | 34 | var data = { 35 | 'key': key 36 | }; 37 | 38 | var path = fmt.format('/hubs/%s/streams', hub); 39 | API.request.post(credentials, path, data, function(err, jsonData) { 40 | if (err) { 41 | fn(err, null); 42 | } else { 43 | var stream = new Stream(credentials, hub, key); 44 | fn(null, stream); 45 | } 46 | }); 47 | } 48 | 49 | exports.listStreams = function(credentials, hub, options, fn) { 50 | 51 | var data = {}; 52 | 53 | if (options) { 54 | if (options.liveonly) { 55 | data['liveonly'] = true; 56 | } 57 | if (options.prefix) { 58 | data['prefix'] = options.prefix; 59 | } 60 | if (options.limit) { 61 | data['limit'] = options.limit; 62 | } 63 | if (options.marker) { 64 | data['marker'] = options.marker; 65 | } 66 | } 67 | 68 | var path = fmt.format('/hubs/%s/streams', hub); 69 | API.request.get(credentials, path, data, function(err, jsonData) { 70 | if (err) { 71 | fn(err, null, null); 72 | } else { 73 | var marker = null; 74 | if (jsonData['marker'] && jsonData['marker'] != '') { 75 | marker = jsonData['marker']; 76 | } 77 | 78 | var list = []; 79 | var jsonList = jsonData['items']; 80 | if (jsonList) { 81 | jsonList.forEach(function(json) { 82 | var stream = new Stream(credentials, hub, json['key']); 83 | list.push(stream); 84 | }) 85 | } 86 | fn(null, marker, list); 87 | } 88 | }); 89 | } 90 | 91 | exports.batchLiveStreamsInfo = function(credentials, hub, items, fn) { 92 | var path = fmt.format('/hubs/%s/livestreams', hub); 93 | var data = {'items': items}; 94 | API.request.post(credentials, path, data, function(err, jsonData) { 95 | if (err) { 96 | fn(err, null); 97 | return; 98 | } 99 | 100 | var list = jsonData['items']; 101 | fn(null, list); 102 | }); 103 | } 104 | 105 | exports.getStreamInfo = function(credentials, hub, key, fn) { 106 | 107 | var path = fmt.format('/hubs/%s/streams/%s', hub, Util.urlsafeBase64Encode(key)); 108 | API.request.get(credentials, path, null, fn); 109 | } 110 | 111 | exports.streamLiveInfo = function(credentials, hub, key, fn) { 112 | 113 | var path = fmt.format('/hubs/%s/streams/%s/live', hub, Util.urlsafeBase64Encode(key)); 114 | API.request.get(credentials, path, null, fn); 115 | } 116 | 117 | exports.disableStream = function(credentials, hub, key, disabledTill, fn) { 118 | 119 | var path = fmt.format('/hubs/%s/streams/%s/disabled', hub, Util.urlsafeBase64Encode(key)); 120 | var data = { 121 | 'disabledTill': disabledTill 122 | } 123 | 124 | API.request.post(credentials, path, data, fn); 125 | } 126 | 127 | exports.savePlayback = function(credentials, hub, key, options, fn) { 128 | 129 | var path = fmt.format('/hubs/%s/streams/%s/saveas', hub, Util.urlsafeBase64Encode(key)); 130 | 131 | var data = {}; 132 | if (options) { 133 | if (options.fname) { 134 | data['fname'] = options.fname; 135 | } 136 | if (options.start) { 137 | data['start'] = options.start; 138 | } 139 | if (options.end) { 140 | data['end'] = options.end; 141 | } 142 | if (options.format) { 143 | data['format'] = options.format; 144 | } 145 | if (options.pipeline) { 146 | data['pipeline'] = options.pipeline; 147 | } 148 | if (options.notify) { 149 | data['notify'] = options.notify; 150 | } 151 | if (options.expireDays) { 152 | data['expireDays'] = options.expireDays; 153 | } 154 | } 155 | 156 | API.request.post(credentials, path, data, fn); 157 | } 158 | 159 | exports.saveSnapshot = function(credentials, hub, key, options, fn) { 160 | 161 | var path = fmt.format('/hubs/%s/streams/%s/snapshot', hub, Util.urlsafeBase64Encode(key)); 162 | 163 | var data = {}; 164 | if (options) { 165 | if (options.fname) { 166 | data['fname'] = options.fname; 167 | } 168 | if (options.time) { 169 | data['time'] = options.time; 170 | } 171 | if (options.format) { 172 | data['format'] = options.format; 173 | } 174 | } 175 | 176 | API.request.post(credentials, path, data, fn); 177 | } 178 | 179 | exports.updateConverts = function(credentials, hub, key, profiles, fn) { 180 | var path = fmt.format('/hubs/%s/streams/%s/converts', hub, Util.urlsafeBase64Encode(key)); 181 | var data = {}; 182 | data['converts'] = profiles; 183 | API.request.post(credentials, path, data, fn); 184 | } 185 | 186 | exports.publishHistory = function(credentials, hub, key, options, fn) { 187 | 188 | var path = fmt.format('/hubs/%s/streams/%s/historyactivity', hub, Util.urlsafeBase64Encode(key)); 189 | 190 | var data = {}; 191 | if (options) { 192 | if (options.start) { 193 | data['start'] = options.start; 194 | } 195 | if (options.end) { 196 | data['end'] = options.end; 197 | } 198 | } 199 | 200 | API.request.get(credentials, path, data, fn); 201 | } 202 | 203 | // URL ------------ 204 | 205 | exports.publishURL = function(credentials, domain, hub, key, expireAfterSec) { 206 | 207 | var expire = Math.floor(Date.now()/1000) + expireAfterSec; 208 | var path = fmt.format('/%s/%s?e=%d', hub, key, expire); 209 | var token = credentials.sign(path); 210 | return fmt.format('rtmp://%s%s&token=%s', domain, path, token); 211 | } 212 | 213 | exports.rtmpPlayURL = function(domain, hub, key) { 214 | return fmt.format('rtmp://%s/%s/%s', domain, hub, key); 215 | } 216 | 217 | exports.hdlPlayURL = function(domain, hub, key) { 218 | return fmt.format('http://%s/%s/%s.flv', domain, hub, key); 219 | } 220 | 221 | exports.hlsPlayURL = function(domain, hub, key) { 222 | return fmt.format('http://%s/%s/%s.m3u8', domain, hub, key); 223 | } 224 | 225 | exports.snapshotPlayURL = function(domain, hub, key) { 226 | return fmt.format('http://%s/%s/%s.jpg', domain, hub, key); 227 | } 228 | 229 | //RTC ------------------------ 230 | exports.createRtcRoom = function(credentials, ownerID, roomName, options, fn) { 231 | 232 | var path = fmt.format('/rooms'); 233 | var data = { 234 | "owner_id": ownerID, 235 | "room_name": roomName 236 | } 237 | 238 | if (options.userMax) { 239 | data["user_max"] = options.userMax; 240 | } 241 | 242 | API.requestRTC.post(credentials, path, data, fn); 243 | } 244 | 245 | exports.getRtcRoom = function(credentials, roomName, fn) { 246 | 247 | var path = fmt.format('/rooms/%s', roomName); 248 | 249 | API.requestRTC.get(credentials, path, {}, fn); 250 | } 251 | 252 | exports.deleteRtcRoom = function(credentials, roomName, fn) { 253 | 254 | var path = fmt.format('/rooms/%s', roomName); 255 | 256 | API.requestRTC.delete(credentials, path, fn); 257 | } 258 | 259 | exports.getRoomMember = function(credentials, roomName, fn) { 260 | 261 | var path = fmt.format('/rooms/%s/users', roomName); 262 | 263 | API.requestRTC.get(credentials, path,{}, fn); 264 | } 265 | 266 | exports.deleteRoomMember = function(credentials, roomName, userId,fn) { 267 | 268 | var path = fmt.format('/rooms/%s/users/%s', roomName, userId); 269 | console.log(path); 270 | 271 | API.requestRTC.delete(credentials, path, fn); 272 | } 273 | 274 | //RTCV2--------------------------------------------- 275 | exports.createRtcRoomV2 = function(credentials, ownerID, roomName, options, fn) { 276 | 277 | var path = fmt.format('/rooms'); 278 | var data = { 279 | "owner_id": ownerID, 280 | "room_name": roomName 281 | } 282 | 283 | if (options.userMax) { 284 | data["user_max"] = options.userMax; 285 | } 286 | 287 | API.requestRTCV2.post(credentials, path, data, fn); 288 | } 289 | 290 | exports.getRtcRoomV2 = function(credentials, roomName, fn) { 291 | 292 | var path = fmt.format('/rooms/%s', roomName); 293 | 294 | API.requestRTCV2.get(credentials, path, {}, fn); 295 | } 296 | 297 | exports.deleteRtcRoomV2 = function(credentials, roomName, fn) { 298 | 299 | var path = fmt.format('/rooms/%s', roomName); 300 | 301 | API.requestRTCV2.delete(credentials, path, fn); 302 | } 303 | 304 | exports.getRoomMemberV2 = function(credentials, roomName, fn) { 305 | var path = fmt.format('/rooms/%s/users', roomName); 306 | 307 | API.requestRTCV2.get(credentials, path,{}, fn); 308 | } 309 | 310 | exports.deleteRoomMemberV2 = function(credentials, roomName, userId,fn) { 311 | 312 | var path = fmt.format('/rooms/%s/users/%s', roomName, userId); 313 | console.log(path); 314 | 315 | API.requestRTCV2.delete(credentials, path, fn); 316 | } --------------------------------------------------------------------------------