oak-external-sdk/lib/service/qiniu/QiniuCloud.js

180 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.QiniuCloudInstance = void 0;
const tslib_1 = require("tslib");
require('../../fetch');
const crypto_1 = tslib_1.__importDefault(require("crypto"));
const ts_md5_1 = require("ts-md5");
const buffer_1 = require("buffer");
class QiniuCloudInstance {
accessKey;
secretKey;
constructor(accessKey, secretKey) {
this.accessKey = accessKey;
this.secretKey = secretKey;
}
/**
* 计算客户端上传七牛需要的凭证
* https://developer.qiniu.com/kodo/1312/upload
* @param uploadHost
* @param bucket
* @param key
* @returns
*/
getUploadInfo(uploadHost, bucket, key) {
try {
const scope = key ? `${bucket}:${key}` : bucket;
const uploadToken = this.getToken(scope);
return {
key,
uploadToken,
uploadHost,
bucket,
};
}
catch (err) {
throw err;
}
}
/**
* 计算直播需要的token
* @param method
* @param path
* @param host
* @param rawQuery
* @param contentType
* @param bodyStr
* @returns
*/
getLiveToken(method, path, host, rawQuery, contentType, bodyStr) {
// 1. 添加 Path
let data = `${method} ${path}`;
if (rawQuery) {
data += `?${rawQuery}`;
}
data += `\nHost: ${host}`;
if (contentType) {
data += `\nContent-Type: ${contentType}`;
}
data += '\n\n';
if (bodyStr &&
contentType &&
contentType !== 'application/octet-stream') {
data += bodyStr;
}
const sign = this.hmacSha1(data, this.secretKey);
const encodedSign = this.base64ToUrlSafe(sign);
const toke = 'Qiniu ' + this.accessKey + ':' + encodedSign;
return toke;
}
async getLiveStream(hub, method, streamTitle, host, publishDomain, playDomain, publishKey, playKey, expireAt) {
// 七牛创建直播流接口路径
const path = `/v2/hubs/${hub}/streams`;
// 如果用户没给streamTitle那么随机生成一个
let key = streamTitle;
if (!key) {
key = `class${new Date().getTime()}`;
}
const bodyStr = JSON.stringify({
key,
});
const contentType = 'application/json';
const token = this.getLiveToken(method, path, host);
const url = `https://pili.qiniuapi.com/v2/hubs/${hub}/streams`;
await global.fetch(url, {
method: 'POST',
headers: {
Authorization: token,
'Content-Type': contentType,
},
body: bodyStr,
mode: 'no-cors',
});
const obj = this.getStreamObj(publishDomain, playDomain, hub, publishKey, playKey, streamTitle, expireAt);
return obj;
}
/**
* 计算直播流地址相关信息
* @param publishDomain
* @param playDomain
* @param hub
* @param publishKey
* @param playKey
* @param streamTitle
* @param expireAt
* @returns
*/
getStreamObj(publishDomain, playDomain, hub, publishKey, playKey, streamTitle, expireAt) {
const signStr = `/${hub}/${streamTitle}?expire=${expireAt}`;
const sourcePath = `/${hub}/${streamTitle}`;
const token = this.base64ToUrlSafe(this.hmacSha1(signStr, publishKey));
const rtmpPushUrl = `rtmp://${publishDomain}${signStr}&token=${token}`;
// 生成播放地址
const t = expireAt.toString(16).toLowerCase();
const playSign = ts_md5_1.Md5.hashStr(playKey + sourcePath + t)
.toString()
.toLowerCase();
const rtmpPlayUrl = `https://${playDomain}${sourcePath}.m3u8?sign=${playSign}&t=${t}`;
// obs推流需要的地址和串流密钥
const pcPushUrl = `rtmp://${publishDomain}/${hub}/`;
const streamKey = `${streamTitle}?expire=${expireAt}&token=${token}`;
return {
streamTitle,
hub,
rtmpPushUrl,
rtmpPlayUrl,
pcPushUrl,
streamKey,
expireAt,
};
}
async getPlayBackUrl(hub, playBackDomain, streamTitle, start, end, method, host, rawQuery) {
const encodeStreamTitle = this.base64ToUrlSafe(streamTitle);
const path = `/v2/hubs/${hub}/streams/${encodeStreamTitle}/saveas`;
const bodyStr = JSON.stringify({
fname: streamTitle,
start,
end,
});
const contentType = 'application/json';
const token = this.getLiveToken(method, path, host, rawQuery, contentType, bodyStr);
const url = `https://pili.qiniuapi.com${path}`;
await global.fetch(url, {
method: 'POST',
headers: {
Authorization: token,
'Content-Type': contentType,
},
body: bodyStr,
mode: 'no-cors',
});
return `https://${playBackDomain}/${streamTitle}.m3u8`;
}
getToken(scope) {
// 构造策略
const putPolicy = {
scope: scope,
deadline: 3600 + Math.floor(Date.now() / 1000),
};
// 构造凭证
const encodedFlags = this.urlSafeBase64Encode(JSON.stringify(putPolicy));
const encoded = this.hmacSha1(encodedFlags, this.secretKey);
const encodedSign = this.base64ToUrlSafe(encoded);
const uploadToken = this.accessKey + ':' + encodedSign + ':' + encodedFlags;
return uploadToken;
}
base64ToUrlSafe(v) {
return v.replace(/\//g, '_').replace(/\+/g, '-');
}
hmacSha1(encodedFlags, secretKey) {
const hmac = crypto_1.default.createHmac('sha1', secretKey);
hmac.update(encodedFlags);
return hmac.digest('base64');
}
urlSafeBase64Encode(jsonFlags) {
const encoded = buffer_1.Buffer.from(jsonFlags).toString('base64');
return this.base64ToUrlSafe(encoded);
}
}
exports.QiniuCloudInstance = QiniuCloudInstance;