UNPKG

@skyway-sdk/rtc-api-client

Version:

The official Next Generation JavaScript SDK for SkyWay

670 lines 28.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.channelFactory = exports.ChannelImpl = void 0; const common_1 = require("@skyway-sdk/common"); const model_1 = __importDefault(require("@skyway-sdk/model")); const errors_1 = require("../errors"); const event = __importStar(require("../model/event")); const util_1 = require("../util"); const log = new common_1.Logger('packages/rtc-api-client/src/domain/channel.ts'); class ChannelImpl { constructor(appId, { id, name, members, metadata, publications, subscriptions, version, }, eventObserver, apiClient, config) { this.appId = appId; this.eventObserver = eventObserver; this.apiClient = apiClient; this.config = config; this.disposed = false; // events this._events = new common_1.Events(); this.onClosed = this._events.make(); this.onMetadataUpdated = this._events.make(); this.onMemberListChanged = this._events.make(); this.onMemberJoined = this._events.make(); this.onMemberLeft = this._events.make(); this.onMemberMetadataUpdated = this._events.make(); this.onPublicationDisabled = this._events.make(); this.onPublicationEnabled = this._events.make(); this.onPublicationListChanged = this._events.make(); this.onStreamPublished = this._events.make(); this.onStreamUnpublished = this._events.make(); this.onPublicationMetadataUpdated = this._events.make(); this.onSubscriptionListChanged = this._events.make(); this.onPublicationSubscribed = this._events.make(); this.onPublicationUnsubscribed = this._events.make(); this.updateChannelMetadata = (metadata) => new Promise((r, f) => { let failed = false; this.apiClient .updateChannelMetadata(this.appId, this.id, metadata) .catch((e) => { failed = true; f(e); }); this.onMetadataUpdated .watch((e) => e.channel.metadata === metadata) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.updateChannelMetadata', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onMetadataUpdated' }), path: log.prefix, error, appId: this.appId, channelId: this.id, })); }); }); this.leave = (channelId, memberId) => new Promise((r, f) => { let failed = false; this.apiClient.leave(this.appId, channelId, memberId).catch((e) => { failed = true; f(e); }); this.onMemberLeft .watch((e) => e.member.id === memberId, this.config.rtcApi.timeout) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.leave', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onMemberLeft' }), path: log.prefix, error, appId: this.appId, channelId: this.id, })); }); }); this.updateMemberMetadata = (memberId, metadata) => new Promise((r, f) => { let failed = false; this.apiClient .updateMemberMetadata(this.appId, this.id, memberId, metadata) .catch((e) => { failed = true; f(e); }); this.onMemberMetadataUpdated .watch((e) => e.member.id === memberId && e.member.metadata === metadata) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.updateMemberMetadata', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onMemberMetadataUpdated' }), path: log.prefix, error, appId: this.appId, channelId: this.id, })); }); }); this.unpublish = (publicationId) => new Promise((r, f) => { let failed = false; this.apiClient .unpublish(this.appId, this.id, publicationId) .catch((e) => { failed = true; f(e); }); this.onStreamUnpublished .watch((e) => e.publication.id === publicationId) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.unpublish', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onStreamUnpublished' }), path: log.prefix, error, payload: { publicationId }, appId: this.appId, channelId: this.id, })); }); }); this.updatePublicationMetadata = (publicationId, metadata) => new Promise((r, f) => { let failed = false; this.apiClient .updatePublicationMetadata(this.appId, this.id, publicationId, metadata) .catch((e) => { failed = true; f(e); }); this.onPublicationMetadataUpdated .watch((e) => e.publication.id === publicationId && e.publication.metadata === metadata) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.updatePublicationMetadata', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onPublicationMetadataUpdated' }), path: log.prefix, error, payload: { publicationId }, appId: this.appId, channelId: this.id, })); }); }); this.disablePublication = (publicationId) => new Promise((r, f) => { let failed = false; this.apiClient .disablePublication(this.appId, this.id, publicationId) .catch((e) => { failed = true; f(e); }); this.onPublicationDisabled .watch((e) => e.publication.id === publicationId) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.disablePublication', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onPublicationDisabled' }), path: log.prefix, error, payload: { publicationId }, appId: this.appId, channelId: this.id, })); }); }); this.enablePublication = (publicationId) => new Promise((r, f) => { let failed = false; this.apiClient .enablePublication(this.appId, this.id, publicationId) .catch((e) => { failed = true; f(e); }); this.onPublicationEnabled .watch((e) => e.publication.id === publicationId) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.enablePublication', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onPublicationEnabled' }), path: log.prefix, error, payload: { publicationId }, appId: this.appId, channelId: this.id, })); }); }); this.unsubscribe = (subscriptionId) => new Promise((r, f) => { let failed = false; this.apiClient .unsubscribe(this.appId, this.id, subscriptionId) .catch((e) => { failed = true; f(e); }); this.onPublicationUnsubscribed .watch((e) => e.subscription.id === subscriptionId) .then(() => r()) .catch((error) => { if (!failed) f((0, util_1.createError)({ operationName: 'ChannelImpl.unsubscribe', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onPublicationUnsubscribed' }), path: log.prefix, error, payload: { subscriptionId }, appId: this.appId, channelId: this.id, })); }); }); this.id = id; this.name = name; this.metadata = metadata; this.members = members; this.publications = publications; this.subscriptions = subscriptions; this.version = version; eventObserver.onEvent.add((event) => { log.debug('received event: ', event); this.version = event.data.channel.version; try { switch (event.type) { case 'ChannelDeleted': { this._channelClosed(); } break; case 'ChannelMetadataUpdated': { this._channelMetadataUpdated(event.data); } break; case 'MemberAdded': { this._memberJoined(event.data); } break; case 'MemberRemoved': { this._memberLeft(event.data); } break; case 'MemberMetadataUpdated': { this._memberMetadataUpdated(event.data); } break; case 'StreamPublished': { this._streamPublished(event.data); } break; case 'StreamUnpublished': { this._streamUnpublished(event.data); } break; case 'PublicationMetadataUpdated': { this._publicationMetadataUpdated(event.data); } break; case 'PublicationDisabled': { this._publicationDisabled(event.data); } break; case 'PublicationEnabled': { this._publicationEnabled(event.data); } break; case 'StreamSubscribed': { this._streamSubscribed(event.data); } break; case 'StreamUnsubscribed': { this._streamUnsubscribed(event.data); } break; } } catch (error) { log.error(error); } }); apiClient.onClose.once(() => { this.dispose(); }); } getMember(id) { return this.members.find((s) => s.id === id); } addMember(member) { const exist = this.getMember(member.id); if (exist) { return exist; } this.members.push(member); return member; } deleteMember(id) { this.members = this.members.filter((m) => m.id !== id); } getPublication(id) { return this.publications.find((s) => s.id === id); } addPublication(summary) { var _a, _b; const exist = this.getPublication(summary.id); if (exist) { return exist; } const publication = Object.assign(Object.assign({}, summary), { channelId: this.id, codecCapabilities: (_a = summary.codecCapabilities) !== null && _a !== void 0 ? _a : [], encodings: (_b = summary.encodings) !== null && _b !== void 0 ? _b : [] }); this.publications.push(publication); return publication; } deletePublication(publicationId) { this.publications = this.publications.filter((p) => p.id !== publicationId); } getSubscription(id) { return this.subscriptions.find((s) => s.id === id); } addSubscription(summary) { const exist = this.getSubscription(summary.id); if (exist) { return exist; } const publication = this.getPublication(summary.publicationId); const subscription = Object.assign(Object.assign({}, summary), { channelId: this.id, publisherId: publication.publisherId, contentType: publication.contentType }); this.subscriptions.push(subscription); return subscription; } deleteSubscription(subscriptionId) { this.subscriptions = this.subscriptions.filter((s) => s.id !== subscriptionId); } _channelClosed() { this.onClosed.emit({}); } _channelMetadataUpdated(event) { this.metadata = event.channel.metadata; this.onMetadataUpdated.emit(event); } _memberJoined(event) { this.addMember(event.member); this.onMemberJoined.emit(event); this.onMemberListChanged.emit({}); } _memberLeft(event) { const member = this.getMember(event.member.id); if (!member) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._memberLeft', info: errors_1.errors.memberNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } this.deleteMember(member.id); this.onMemberLeft.emit({ member }); this.onMemberListChanged.emit({}); } _memberMetadataUpdated(event) { const member = this.getMember(event.member.id); if (!member) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._memberMetadataUpdated', info: errors_1.errors.memberNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } member.metadata = event.member.metadata; this.onMemberMetadataUpdated.emit(event); } _streamPublished(event) { const publication = this.addPublication(event.publication); const outgoing = Object.assign(Object.assign({}, event), { publication }); this.onStreamPublished.emit(outgoing); this.onPublicationListChanged.emit({}); } _streamUnpublished(event) { const publication = this.getPublication(event.publication.id); if (!publication) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._streamUnpublished', info: errors_1.errors.publicationNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } this.deletePublication(publication.id); const outgoing = Object.assign(Object.assign({}, event), { publication }); this.onStreamUnpublished.emit(outgoing); this.onPublicationListChanged.emit({}); } _publicationMetadataUpdated(event) { const publication = this.getPublication(event.publication.id); if (!publication) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._publicationMetadataUpdated', info: errors_1.errors.publicationNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } publication.metadata = event.publication.metadata; const outgoing = Object.assign(Object.assign({}, event), { publication }); this.onPublicationMetadataUpdated.emit(outgoing); } _publicationDisabled(event) { const publication = this.getPublication(event.publication.id); if (!publication) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._publicationDisabled', info: errors_1.errors.publicationNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } publication.isEnabled = event.publication.isEnabled; const outgoing = { publication, }; this.onPublicationDisabled.emit(outgoing); } _publicationEnabled(incoming) { const publication = this.getPublication(incoming.publication.id); if (!publication) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._publicationEnabled', info: errors_1.errors.publicationNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } publication.isEnabled = incoming.publication.isEnabled; const outgoing = { publication, }; this.onPublicationEnabled.emit(outgoing); } _streamSubscribed(incoming) { const subscription = this.addSubscription(incoming.subscription); const outgoing = Object.assign(Object.assign({}, incoming), { subscription }); this.onPublicationSubscribed.emit(outgoing); this.onSubscriptionListChanged.emit({}); } _streamUnsubscribed(event) { const subscription = this.getSubscription(event.subscription.id); if (!subscription) { throw (0, util_1.createError)({ operationName: 'ChannelImpl._streamUnsubscribed', info: errors_1.errors.subscriptionNotFound, path: log.prefix, payload: { event }, appId: this.appId, channelId: this.id, }); } this.deleteSubscription(subscription.id); const outgoing = Object.assign(Object.assign({}, event), { subscription }); this.onPublicationUnsubscribed.emit(outgoing); this.onSubscriptionListChanged.emit({}); } joinChannel(memberInit) { var _a; return __awaiter(this, void 0, void 0, function* () { if (memberInit.type) { memberInit.type = (memberInit.type[0].toUpperCase() + memberInit.type.slice(1)); } if (memberInit.subtype) { memberInit.subtype = (memberInit.subtype[0].toUpperCase() + memberInit.subtype.slice(1)); } log.debug('[start] joinChannel', { memberInit }); const res = yield this.apiClient.join(this.appId, this.id, Object.assign({}, memberInit)); const member = (_a = this.getMember(res.id)) !== null && _a !== void 0 ? _a : (yield this.onMemberJoined .watch((e) => e.member.id === res.id, this.config.rtcApi.timeout) .catch((error) => { throw (0, util_1.createError)({ operationName: 'ChannelImpl.joinChannel', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onMemberJoined' }), path: log.prefix, error, appId: this.appId, channelId: this.id, }); })).member; log.debug('[end] joinChannel', { member }); return member; }); } updateMemberTtl(memberId, ttlSec) { return this.apiClient.updateMemberTtl(this.appId, this.id, memberId, ttlSec); } /**@throws {SkyWayError} */ publish(init) { var _a, _b, _c, _d; return __awaiter(this, void 0, void 0, function* () { const ts = log.debug('[start] apiClient.publish', { init }); const allowedTypes = model_1.default.PublicationType.filter((t) => t !== null); if (init.type && !allowedTypes.includes(init.type)) { throw (0, util_1.createError)({ operationName: 'ChannelImpl.publish', error: new Error('The type in PublicationOptions is invalid.'), info: Object.assign({}, errors_1.errors.invalidPublicationType), path: log.prefix, payload: { init }, appId: this.appId, channelId: this.id, }); } const channelId = this.id; const publicationId = yield this.apiClient.publish(this.appId, Object.assign(Object.assign({}, init), { channel: channelId })); const publicationDto = { id: publicationId, channelId, publisherId: init.publisher, origin: init.origin, contentType: init.contentType, metadata: init.metadata, codecCapabilities: (_a = init.codecCapabilities) !== null && _a !== void 0 ? _a : [], encodings: (_b = init.encodings) !== null && _b !== void 0 ? _b : [], isEnabled: (_c = init.isEnabled) !== null && _c !== void 0 ? _c : true, type: (_d = init.type) !== null && _d !== void 0 ? _d : 'p2p', }; log.elapsed(ts, '[ongoing] apiClient.publish', { publicationDto }); const exist = this.getPublication(publicationId); if (exist) { return exist; } const { publication } = yield this.onStreamPublished .watch((e) => e.publication.id === publicationId, this.config.rtcApi.timeout) .catch((error) => { throw (0, util_1.createError)({ operationName: 'ChannelImpl.publish', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onStreamPublished' }), path: log.prefix, error, payload: { publicationDto }, appId: this.appId, channelId: this.id, }); }); log.elapsed(ts, '[end] apiClient.publish', { publicationDto }); return publication; }); } /**@throws {@link SkyWayError} */ subscribe(init) { return __awaiter(this, void 0, void 0, function* () { const ts = log.debug('[start] apiClient.subscribe', { init }); const subscriptionId = yield this.apiClient.subscribe(this.appId, Object.assign(Object.assign({}, init), { channel: this })); const subscriptionDto = { id: subscriptionId, publicationId: init.publication.id, channelId: this.id, publisherId: init.publication.publisherId, subscriberId: init.subscriber.id, contentType: init.publication.contentType, }; log.elapsed(ts, '[ongoing] apiClient.subscribe', { subscriptionDto }); const exist = this.getSubscription(subscriptionId); if (exist) { log.elapsed(ts, '[end] apiClient.subscribe', { subscriptionDto }); return exist; } const { subscription } = yield this.onPublicationSubscribed .watch((e) => e.subscription.id === subscriptionId, this.config.rtcApi.timeout) .catch((error) => { log.elapsed(ts, '[fail] apiClient.subscribe', error); throw (0, util_1.createError)({ operationName: 'ChannelImpl.subscribe', info: Object.assign(Object.assign({}, errors_1.errors.timeout), { detail: 'onPublicationSubscribed' }), path: log.prefix, error, payload: { subscriptionDto }, }); }); log.elapsed(ts, '[end] apiClient.subscribe', { subscriptionDto }); return subscription; }); } close() { return this.apiClient.deleteChannel(this.appId, this.id); } /** * リソースの解放 * - Channelイベントの購読停止 * - イベントリスナー */ dispose() { if (this.disposed) { return; } this.disposed = true; log.debug('disposed', { id: this.id }); this.eventObserver.dispose(); this._events.dispose(); } } exports.ChannelImpl = ChannelImpl; function channelFactory(appId, eventObserver, api, channelDto, config) { const channel = new ChannelImpl(appId, channelDto, eventObserver, api, config); return channel; } exports.channelFactory = channelFactory; //# sourceMappingURL=channel.js.map