@skyway-sdk/sfu-bot
Version:
The official Next Generation JavaScript SDK for SkyWay
248 lines • 11.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SfuBotMember = void 0;
const common_1 = require("@skyway-sdk/common");
const core_1 = require("@skyway-sdk/core");
const connection_1 = require("./connection");
const const_1 = require("./const");
const errors_1 = require("./errors");
const log = new common_1.Logger('packages/sfu-bot/src/member.ts');
class SfuBotMember extends core_1.MemberImpl {
/**@internal */
constructor(args) {
super(args);
this.side = 'remote';
this.subtype = SfuBotMember.subtype;
this.type = 'bot';
this._connections = {};
/** @description [japanese] forwardingを開始した時に発火するイベント */
this.onForwardingStarted = new common_1.Event();
/** @description [japanese] forwardingを終了した時に発火するイベント */
this.onForwardingStopped = new common_1.Event();
/** @description [japanese] forwardingの数が変化した時に発火するイベント */
this.onForwardingListChanged = new common_1.Event();
this._startForwardQueue = new common_1.PromiseQueue();
this._forwardings = {};
/**
* @description [japanese] Forwardingを停止する
*/
this.stopForwarding = (target) => new Promise((r, f) => __awaiter(this, void 0, void 0, function* () {
const timestamp = log.info('[start] stopForwarding', yield (0, core_1.createLogPayload)({
operationName: 'SfuBotMember.stopForwarding',
channel: this.channel,
}));
if (this.state !== 'joined') {
f((0, core_1.createError)({
operationName: 'SfuBotMember.stopForwarding',
context: this._context,
info: errors_1.errors.sfuBotNotInChannel,
path: log.prefix,
channel: this.channel,
payload: { status: this.state },
}));
return;
}
const forwardingId = typeof target === 'string' ? target : target.id;
const forwarding = this._forwardings[forwardingId];
if (!forwarding) {
f((0, core_1.createError)({
operationName: 'SfuBotMember.stopForwarding',
context: this._context,
info: errors_1.errors.forwardingNotFound,
path: log.prefix,
channel: this.channel,
payload: {
forwardingId,
_forwardings: Object.keys(this._forwardings),
},
}));
return;
}
delete this._forwardings[forwarding.id];
const { promise, fulfilled } = this._api.stopForwarding({
botId: this.id,
forwardingId,
});
let failed = false;
promise.catch((e) => {
failed = true;
f(e);
});
this.onForwardingStopped
.watch((e) => e.forwarding.id === forwardingId, this._context.config.rtcApi.timeout)
.then(() => __awaiter(this, void 0, void 0, function* () {
log.elapsed(timestamp, '[end] stopForwarding', yield (0, core_1.createLogPayload)({
operationName: 'SfuBotMember.stopForwarding',
channel: this.channel,
}));
r();
}))
.catch((error) => {
if (!failed)
f((0, core_1.createError)({
operationName: 'SfuBotMember.stopForwarding',
context: this._context,
info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onForwardingStopped' }),
path: log.prefix,
channel: this.channel,
payload: { fulfilled },
error,
}));
});
}));
this._api = args.api;
this._context = args.context;
this._transportRepository = args.transportRepository;
this.options = args.options;
this.onLeft.once(() => {
log.debug('SfuBotMember left: ', { id: this.id });
Object.values(this._connections).forEach((c) => {
c.close({ reason: 'sfu bot left' });
});
this._connections = {};
});
}
get forwardings() {
return Object.values(this._forwardings);
}
/**@private */
_getConnection(localPersonId) {
return this._connections[localPersonId];
}
/**@private */
_getOrCreateConnection(localPerson) {
var _a;
const connection = (_a = this._getConnection(localPerson.id)) !== null && _a !== void 0 ? _a : this._createConnection(this.channel, localPerson, this);
return connection;
}
/**@private */
_createConnection(channel, localPerson, endpointBot) {
const connection = new connection_1.SFUConnection(endpointBot._api, channel, localPerson, endpointBot, this._transportRepository, this._context);
connection.onClose.once(() => {
delete this._connections[localPerson.id];
});
this._connections[localPerson.id] = connection;
return connection;
}
/**
* @description [japanese] StreamのPublicationをForwardingする
* @throws {SkyWayError}
* @example
* const forwarding = await bot.startForwarding(publication, { maxSubscribers: 99 });
*/
startForwarding(publication, configure = {}) {
return __awaiter(this, void 0, void 0, function* () {
const timestamp = log.info('[start] startForwarding', yield (0, core_1.createLogPayload)({
operationName: 'SfuBotMember.startForwarding',
channel: this.channel,
}));
const res = yield this._startForwardQueue.push(() => this._startForwarding(publication, configure));
log.elapsed(timestamp, '[end] startForwarding', yield (0, core_1.createLogPayload)({
operationName: 'SfuBotMember.startForwarding',
channel: this.channel,
}));
return res;
});
}
_startForwarding(relayed, configure) {
return __awaiter(this, void 0, void 0, function* () {
if (configure.maxSubscribers == undefined) {
configure.maxSubscribers = const_1.defaultMaxSubscribers;
}
if (this.state !== 'joined') {
throw (0, core_1.createError)({
operationName: 'SfuBotMember._startForwarding',
context: this._context,
channel: this.channel,
info: errors_1.errors.sfuBotNotInChannel,
path: log.prefix,
payload: { status: this.state },
});
}
if (!this.channel._getPublication(relayed.id)) {
throw (0, core_1.createError)({
operationName: 'SfuBotMember._startForwarding',
context: this._context,
channel: this.channel,
info: core_1.errors.publicationNotExist,
path: log.prefix,
});
}
const localPerson = this.channel.localPerson;
if (!localPerson) {
throw (0, core_1.createError)({
operationName: 'SfuBotMember._startForwarding',
context: this._context,
channel: this.channel,
info: core_1.errors.localPersonNotJoinedChannel,
path: log.prefix,
});
}
if (localPerson.id !== relayed.publisher.id) {
throw (0, core_1.createError)({
operationName: 'SfuBotMember._startForwarding',
context: this._context,
info: errors_1.errors.remotePublisherId,
path: log.prefix,
channel: this.channel,
});
}
const ts = log.debug('[start] SfuBotMember startForwarding', {
publication: relayed.toJSON(),
configure,
});
const connection = this._getOrCreateConnection(localPerson);
const sender = connection.addSender(relayed);
const forwarding = yield sender
.startForwarding(configure)
.catch((error) => {
throw (0, core_1.createError)({
operationName: 'SfuBotMember._startForwarding',
context: this._context,
info: Object.assign(Object.assign({}, errors_1.errors.internal), { detail: '[failed] SfuBotMember startForwarding' }),
path: log.prefix,
channel: this.channel,
error,
payload: { publication: relayed.toJSON() },
});
});
this._forwardings[forwarding.id] = forwarding;
this.listenStopForwardEvent(forwarding);
this.onForwardingStarted.emit({ forwarding });
this.onForwardingListChanged.emit();
log.elapsed(ts, '[end] SfuBotMember startForwarding', {
forwarding: forwarding.toJSON(),
});
return forwarding;
});
}
listenStopForwardEvent(forwarding) {
const { removeListener } = this.channel.onStreamUnpublished.add((e) => {
if (e.publication.id === forwarding.id) {
removeListener();
forwarding._stop();
const origin = forwarding.originPublication;
const connection = this._getConnection(origin.publisher.id);
if (connection) {
connection.removeSender(origin.id);
}
this.onForwardingStopped.emit({ forwarding });
this.onForwardingListChanged.emit();
}
});
}
/**@private */
_dispose() { }
}
exports.SfuBotMember = SfuBotMember;
SfuBotMember.subtype = 'sfu';
//# sourceMappingURL=member.js.map