@eyepop.ai/eyepop 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __commonJS = (cb, mod) => function __require() {
7
9
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
@@ -18,6 +20,14 @@ var __copyProps = (to, from, except, desc) => {
18
20
  }
19
21
  return to;
20
22
  };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
21
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
32
 
23
33
  // node_modules/mime-db/db.json
@@ -8934,7 +8944,7 @@ var AbstractJob = class {
8934
8944
  return Promise.reject("abstract class");
8935
8945
  }
8936
8946
  start(done, status) {
8937
- this._responseStream = new Promise(async (resolve2, reject) => {
8947
+ this._responseStream = new Promise(async (resolve, reject) => {
8938
8948
  try {
8939
8949
  let retries = 1;
8940
8950
  while (true) {
@@ -8963,7 +8973,7 @@ var AbstractJob = class {
8963
8973
  reject(`response body is empty`);
8964
8974
  break;
8965
8975
  } else {
8966
- resolve2(response.body);
8976
+ resolve(response.body);
8967
8977
  break;
8968
8978
  }
8969
8979
  } catch (error) {
@@ -9054,6 +9064,39 @@ var LoadFromJob = class extends AbstractJob {
9054
9064
  return response;
9055
9065
  }
9056
9066
  };
9067
+ var LoadLiveIngressJob = class extends AbstractJob {
9068
+ constructor(ingressId, getSession, client, requestLogger) {
9069
+ super(getSession, client, requestLogger);
9070
+ this._ingressId = ingressId;
9071
+ }
9072
+ get [Symbol.toStringTag]() {
9073
+ return "loadLiveIngressJob";
9074
+ }
9075
+ async startJob() {
9076
+ const body = {
9077
+ "sourceType": "LIVE_INGRESS",
9078
+ "liveIngressId": this._ingressId
9079
+ };
9080
+ const session = await this._getSession();
9081
+ const headers = {
9082
+ "Authorization": `Bearer ${session.accessToken}`,
9083
+ "Accept": "application/jsonl",
9084
+ "Content-Type": "application/json"
9085
+ };
9086
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
9087
+ this._requestLogger.debug("before PATCH %s with url %s as source", patchUrl, this._ingressId);
9088
+ const response = await this._client.fetch(patchUrl, {
9089
+ headers,
9090
+ method: "PATCH",
9091
+ body: JSON.stringify(body),
9092
+ signal: this._controller.signal,
9093
+ // @ts-ignore
9094
+ dispatcher: this._dispatcher
9095
+ });
9096
+ this._requestLogger.debug("after PATCH %s with url %s as source", patchUrl, this._ingressId);
9097
+ return response;
9098
+ }
9099
+ };
9057
9100
 
9058
9101
  // src/shims/http_client.ts
9059
9102
  var createHttpClient;
@@ -9222,45 +9265,391 @@ var Semaphore = class {
9222
9265
  }
9223
9266
  };
9224
9267
 
9225
- // src/shims/local_file.ts
9226
- var resolve;
9227
- if ("document" in globalThis && "implementation" in globalThis.document) {
9228
- const isFileLike = (value) => value != null && typeof value === "object" && typeof value.name === "string" && typeof value.lastModified === "number";
9229
- resolve = async (params) => {
9230
- if (params.file) {
9231
- if (isFileLike(params.file)) {
9232
- params = {
9233
- mimeType: params.file.type,
9234
- file: params.file
9235
- };
9236
- return params;
9268
+ // node_modules/uuid/dist/esm-node/rng.js
9269
+ var import_crypto = __toESM(require("crypto"));
9270
+ var rnds8Pool = new Uint8Array(256);
9271
+ var poolPtr = rnds8Pool.length;
9272
+ function rng() {
9273
+ if (poolPtr > rnds8Pool.length - 16) {
9274
+ import_crypto.default.randomFillSync(rnds8Pool);
9275
+ poolPtr = 0;
9276
+ }
9277
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
9278
+ }
9279
+
9280
+ // node_modules/uuid/dist/esm-node/stringify.js
9281
+ var byteToHex = [];
9282
+ for (let i = 0; i < 256; ++i) {
9283
+ byteToHex.push((i + 256).toString(16).slice(1));
9284
+ }
9285
+ function unsafeStringify(arr, offset = 0) {
9286
+ return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
9287
+ }
9288
+
9289
+ // node_modules/uuid/dist/esm-node/native.js
9290
+ var import_crypto2 = __toESM(require("crypto"));
9291
+ var native_default = {
9292
+ randomUUID: import_crypto2.default.randomUUID
9293
+ };
9294
+
9295
+ // node_modules/uuid/dist/esm-node/v4.js
9296
+ function v4(options, buf, offset) {
9297
+ if (native_default.randomUUID && !buf && !options) {
9298
+ return native_default.randomUUID();
9299
+ }
9300
+ options = options || {};
9301
+ const rnds = options.random || (options.rng || rng)();
9302
+ rnds[6] = rnds[6] & 15 | 64;
9303
+ rnds[8] = rnds[8] & 63 | 128;
9304
+ if (buf) {
9305
+ offset = offset || 0;
9306
+ for (let i = 0; i < 16; ++i) {
9307
+ buf[offset + i] = rnds[i];
9308
+ }
9309
+ return buf;
9310
+ }
9311
+ return unsafeStringify(rnds);
9312
+ }
9313
+ var v4_default = v4;
9314
+
9315
+ // src/web_rtc_base.ts
9316
+ var WebRtcBase = class {
9317
+ constructor(getSession, client, requestLogger) {
9318
+ this._pc = null;
9319
+ this._getSession = getSession;
9320
+ this._client = client;
9321
+ this._requestLogger = requestLogger;
9322
+ this._eTag = "";
9323
+ this._queuedCandidates = [];
9324
+ this._offerData = null;
9325
+ }
9326
+ static linkToIceServers(links) {
9327
+ const servers = links !== null ? links.split(", ").map((link) => {
9328
+ const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
9329
+ if (!m) {
9330
+ return null;
9331
+ }
9332
+ const iceServer = {
9333
+ urls: [m[1]],
9334
+ username: Whip.unquoteCredential(m[3]),
9335
+ credential: Whip.unquoteCredential(m[4])
9336
+ // credentialType: m[3]? "password" : undefined
9337
+ };
9338
+ return iceServer;
9339
+ }) : [];
9340
+ return servers.filter(function(el) {
9341
+ return el != null;
9342
+ });
9343
+ }
9344
+ static editAnswer(answer, videoCodec, audioCodec, videoBitrate, audioBitrate, audioVoice) {
9345
+ const sections = answer.split("m=");
9346
+ for (let i = 0; i < sections.length; i++) {
9347
+ const section = sections[i];
9348
+ if (section.startsWith("video")) {
9349
+ sections[i] = Whip.setVideoBitrate(Whip.setCodec(section, videoCodec), videoBitrate);
9350
+ } else if (section.startsWith("audio")) {
9351
+ sections[i] = Whip.setAudioBitrate(Whip.setCodec(section, audioCodec), audioBitrate, audioVoice);
9237
9352
  }
9238
9353
  }
9239
- throw new DOMException("resolving a path to a file is not supported in browser");
9240
- };
9241
- } else {
9242
- resolve = async (params) => {
9243
- if (params.file) {
9244
- return params;
9354
+ return sections.join("m=");
9355
+ }
9356
+ static setCodec(section, codec) {
9357
+ const lines = section.split("\r\n");
9358
+ const lines2 = [];
9359
+ const payloadFormats = [];
9360
+ for (const line of lines) {
9361
+ if (!line.startsWith("a=rtpmap:")) {
9362
+ lines2.push(line);
9363
+ } else {
9364
+ if (codec && line.toLowerCase().includes(codec)) {
9365
+ payloadFormats.push(line.slice("a=rtpmap:".length).split(" ")[0]);
9366
+ lines2.push(line);
9367
+ }
9368
+ }
9245
9369
  }
9246
- const mime = require_mime_types();
9247
- const fs = require("fs");
9248
- let mimeType;
9249
- let stream;
9250
- if (params.filePath) {
9251
- if (params.mimeType) {
9252
- mimeType = params.mimeType;
9370
+ const lines3 = [];
9371
+ for (const line of lines2) {
9372
+ if (line.startsWith("a=fmtp:")) {
9373
+ if (payloadFormats.includes(line.slice("a=fmtp:".length).split(" ")[0])) {
9374
+ lines3.push(line);
9375
+ }
9376
+ } else if (line.startsWith("a=rtcp-fb:")) {
9377
+ if (payloadFormats.includes(line.slice("a=rtcp-fb:".length).split(" ")[0])) {
9378
+ lines3.push(line);
9379
+ }
9253
9380
  } else {
9254
- mimeType = mime.lookup(params.filePath) || void 0;
9381
+ lines3.push(line);
9382
+ }
9383
+ }
9384
+ return lines3.join("\r\n");
9385
+ }
9386
+ static setVideoBitrate(section, bitrate) {
9387
+ let lines = section.split("\r\n");
9388
+ for (let i = 0; i < lines.length; i++) {
9389
+ if (lines[i].startsWith("c=")) {
9390
+ lines = [...lines.slice(0, i + 1), "b=TIAS:" + (bitrate * 1024).toString(), ...lines.slice(i + 1)];
9391
+ break;
9392
+ }
9393
+ }
9394
+ return lines.join("\r\n");
9395
+ }
9396
+ static setAudioBitrate(section, bitrate, voice) {
9397
+ let opusPayloadFormat = "";
9398
+ let lines = section.split("\r\n");
9399
+ for (let i = 0; i < lines.length; i++) {
9400
+ if (lines[i].startsWith("a=rtpmap:") && lines[i].toLowerCase().includes("opus/")) {
9401
+ opusPayloadFormat = lines[i].slice("a=rtpmap:".length).split(" ")[0];
9402
+ break;
9403
+ }
9404
+ }
9405
+ if (opusPayloadFormat === "") {
9406
+ return section;
9407
+ }
9408
+ for (let i = 0; i < lines.length; i++) {
9409
+ if (lines[i].startsWith("a=fmtp:" + opusPayloadFormat + " ")) {
9410
+ if (voice) {
9411
+ lines[i] = "a=fmtp:" + opusPayloadFormat + " minptime=10;useinbandfec=1;maxaveragebitrate=" + ((bitrate ?? 0) * 1024).toString();
9412
+ } else {
9413
+ lines[i] = "a=fmtp:" + opusPayloadFormat + " maxplaybackrate=48000;stereo=1;sprop-stereo=1;maxaveragebitrate" + ((bitrate ?? 0) * 1024).toString();
9414
+ }
9255
9415
  }
9256
- stream = fs.createReadStream(params.filePath);
9257
9416
  }
9258
- params = {
9259
- filePath: params.filePath,
9260
- mimeType,
9261
- file: stream
9417
+ return lines.join("\r\n");
9418
+ }
9419
+ static parseOffer(offer) {
9420
+ let medias = [];
9421
+ let iceUfrag = "";
9422
+ let icePwd = "";
9423
+ for (const line of offer.split("\r\n")) {
9424
+ if (line.startsWith("m=")) {
9425
+ medias.push(line.slice("m=".length));
9426
+ } else if (iceUfrag === "" && line.startsWith("a=ice-ufrag:")) {
9427
+ iceUfrag = line.slice("a=ice-ufrag:".length);
9428
+ } else if (icePwd === "" && line.startsWith("a=ice-pwd:")) {
9429
+ icePwd = line.slice("a=ice-pwd:".length);
9430
+ }
9431
+ }
9432
+ return {
9433
+ iceUfrag,
9434
+ icePwd,
9435
+ medias
9436
+ };
9437
+ }
9438
+ static generateSdpFragment(offerData, candidates) {
9439
+ const candidatesByMedia = /* @__PURE__ */ new Map();
9440
+ for (const candidate of candidates) {
9441
+ if (candidate.sdpMLineIndex !== null) {
9442
+ const mid2 = candidate.sdpMLineIndex;
9443
+ let matched = candidatesByMedia.get(mid2);
9444
+ if (matched === void 0) {
9445
+ matched = [];
9446
+ candidatesByMedia.set(mid2, matched);
9447
+ }
9448
+ matched.push(candidate);
9449
+ }
9450
+ }
9451
+ let frag = "a=ice-ufrag:" + offerData.iceUfrag + "\r\na=ice-pwd:" + offerData.icePwd + "\r\n";
9452
+ let mid = 0;
9453
+ for (const media of offerData.medias) {
9454
+ let matched = candidatesByMedia.get(mid);
9455
+ if (matched !== void 0) {
9456
+ frag += "m=" + media + "\r\na=mid:" + mid + "\r\n";
9457
+ for (const candidate of matched) {
9458
+ frag += "a=" + candidate.candidate + "\r\n";
9459
+ }
9460
+ }
9461
+ mid++;
9462
+ }
9463
+ return frag;
9464
+ }
9465
+ static unquoteCredential(v) {
9466
+ if (!v) {
9467
+ return "";
9468
+ }
9469
+ return JSON.parse(`"${v}"`);
9470
+ }
9471
+ };
9472
+
9473
+ // src/whip.ts
9474
+ var Whip = class _Whip extends WebRtcBase {
9475
+ constructor(stream, getSession, client, requestLogger) {
9476
+ super(getSession, client, requestLogger);
9477
+ this._ingressId = v4_default().toString();
9478
+ this._stream = stream;
9479
+ }
9480
+ ingressId() {
9481
+ if (!this._ingressId) {
9482
+ throw new Error("not initialized");
9483
+ }
9484
+ return this._ingressId;
9485
+ }
9486
+ async start() {
9487
+ const session = await this._getSession();
9488
+ const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9489
+ this._requestLogger.debug("before GET: %s", ingressUrl);
9490
+ const headers = {
9491
+ "Authorization": `Bearer ${session.accessToken}`
9492
+ };
9493
+ const response = await this._client.fetch(ingressUrl, {
9494
+ headers,
9495
+ method: "GET"
9496
+ });
9497
+ if (response.status >= 300) {
9498
+ return Promise.reject(`unknown status code for GET '${ingressUrl}': ${response.status} (${response.statusText})`);
9499
+ }
9500
+ return this.onIceServers(response);
9501
+ }
9502
+ async onIceServers(response) {
9503
+ if (!this._stream) {
9504
+ throw new Error("onIceServers no stream");
9505
+ }
9506
+ const stream = this._stream;
9507
+ const pc = new RTCPeerConnection({
9508
+ iceServers: _Whip.linkToIceServers(response.headers.get("Link"))
9509
+ });
9510
+ this._pc = pc;
9511
+ pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
9512
+ return new Promise(async (resolve, reject) => {
9513
+ pc.oniceconnectionstatechange = () => {
9514
+ switch (pc.iceConnectionState) {
9515
+ case "failed":
9516
+ case "disconnected":
9517
+ reject(`peer connection: ${pc.iceConnectionState}`);
9518
+ break;
9519
+ case "connected":
9520
+ resolve(this);
9521
+ break;
9522
+ }
9523
+ };
9524
+ stream.getTracks().forEach((track) => {
9525
+ this._requestLogger.debug("onIceServers our stream has track: %s", track);
9526
+ pc.addTrack(track, stream);
9527
+ });
9528
+ stream.addEventListener("addtrack", (trackEvent) => {
9529
+ this._requestLogger.debug("onIceServers got lazy track: ", trackEvent.track);
9530
+ pc.addTrack(trackEvent.track, stream);
9531
+ });
9532
+ await this.onLocalOffer(await pc.createOffer());
9533
+ });
9534
+ }
9535
+ async onLocalCandidate(evt) {
9536
+ if (evt.candidate !== null) {
9537
+ if (this._eTag === "") {
9538
+ this._queuedCandidates.push(evt.candidate);
9539
+ } else {
9540
+ await this.sendLocalCandidates([evt.candidate]);
9541
+ }
9542
+ }
9543
+ return this;
9544
+ }
9545
+ async onLocalOffer(offer) {
9546
+ if (!this._pc) {
9547
+ throw new Error("onLocalOffer no peer connection");
9548
+ }
9549
+ if (!offer.sdp) {
9550
+ throw new Error("onLocalOffer no sdp");
9551
+ }
9552
+ this._offerData = _Whip.parseOffer(offer.sdp);
9553
+ await this._pc.setLocalDescription(offer);
9554
+ const session = await this._getSession();
9555
+ const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9556
+ this._requestLogger.debug("before GET: %s", ingressUrl);
9557
+ const headers = {
9558
+ "Authorization": `Bearer ${session.accessToken}`,
9559
+ "Content-Type": "application/sdp"
9560
+ };
9561
+ const response = await this._client.fetch(ingressUrl, {
9562
+ headers,
9563
+ method: "POST",
9564
+ body: offer.sdp
9565
+ });
9566
+ if (response.status != 201) {
9567
+ return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
9568
+ }
9569
+ this._eTag = response.headers.get("ETag") ?? "";
9570
+ return this.onRemoteAnswer(new RTCSessionDescription({
9571
+ type: "answer",
9572
+ sdp: await response.text()
9573
+ }));
9574
+ }
9575
+ async onRemoteAnswer(answer) {
9576
+ if (!this._pc) {
9577
+ throw new Error("onLocalOffer no peer connection");
9578
+ }
9579
+ answer = new RTCSessionDescription({
9580
+ type: "answer",
9581
+ sdp: _Whip.editAnswer(
9582
+ answer.sdp,
9583
+ "h264/90000",
9584
+ void 0,
9585
+ 5e3,
9586
+ void 0,
9587
+ true
9588
+ )
9589
+ });
9590
+ await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
9591
+ if (this._queuedCandidates.length !== 0) {
9592
+ await this.sendLocalCandidates(this._queuedCandidates);
9593
+ this._queuedCandidates = [];
9594
+ }
9595
+ return this;
9596
+ }
9597
+ async sendLocalCandidates(candidates) {
9598
+ if (!this._offerData) {
9599
+ throw new Error("sendLocalCandidates no offerData");
9600
+ }
9601
+ const session = await this._getSession();
9602
+ const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9603
+ this._requestLogger.debug("before PATCH: %s", ingressUrl);
9604
+ const headers = {
9605
+ "Authorization": `Bearer ${session.accessToken}`,
9606
+ "Content-Type": "application/trickle-ice-sdpfrag",
9607
+ "If-Match": this._eTag
9608
+ };
9609
+ const response = await this._client.fetch(ingressUrl, {
9610
+ headers,
9611
+ method: "PATCH",
9612
+ body: _Whip.generateSdpFragment(this._offerData, candidates)
9613
+ });
9614
+ if (response.status != 204) {
9615
+ return Promise.reject(`unknown status code for PATCH '${ingressUrl}': ${response.status} (${response.statusText})`);
9616
+ }
9617
+ return this;
9618
+ }
9619
+ async close() {
9620
+ const pc = this._pc;
9621
+ const stream = this._stream;
9622
+ this._stream = null;
9623
+ this._pc = null;
9624
+ if (stream) {
9625
+ const tracks = stream.getTracks();
9626
+ tracks.forEach((track) => {
9627
+ track.stop();
9628
+ });
9629
+ }
9630
+ if (pc) {
9631
+ return pc.close();
9632
+ }
9633
+ }
9634
+ };
9635
+
9636
+ // src/shims/local_file.ts
9637
+ var resolvePath;
9638
+ if ("document" in globalThis && "implementation" in globalThis.document) {
9639
+ resolvePath = async (source) => {
9640
+ throw new DOMException("resolving a path to a file is not supported in browser");
9641
+ };
9642
+ } else {
9643
+ resolvePath = async (source) => {
9644
+ const mime = require_mime_types();
9645
+ const filehandle = require("fs/promises");
9646
+ const fd = await filehandle.open(source.path, "r");
9647
+ const stream = fd.createReadStream();
9648
+ const mimeType = mime.lookup(source.path) || "application/octet-stream";
9649
+ return {
9650
+ stream,
9651
+ mimeType
9262
9652
  };
9263
- return params;
9264
9653
  };
9265
9654
  }
9266
9655
 
@@ -9277,6 +9666,8 @@ var Endpoint = class {
9277
9666
  this._client = null;
9278
9667
  this._state = "Idle" /* Idle */;
9279
9668
  this._stateChangeHandler = null;
9669
+ this._ingressEventHandler = null;
9670
+ this._ingressEventWs = null;
9280
9671
  this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9281
9672
  let rootLogger;
9282
9673
  if (options.logger) {
@@ -9291,6 +9682,51 @@ var Endpoint = class {
9291
9682
  this._stateChangeHandler = handler;
9292
9683
  return this;
9293
9684
  }
9685
+ async startIngressWs() {
9686
+ if (this._ingressEventWs || !this._baseUrl || !this._client) {
9687
+ return;
9688
+ }
9689
+ try {
9690
+ const getTokenUrl = `${this._baseUrl}/liveIngress/events/token`;
9691
+ const headers = {
9692
+ "Authorization": await this.authorizationHeader()
9693
+ };
9694
+ this._requestLogger.debug("before GET %s", getTokenUrl);
9695
+ let response = await this._client.fetch(getTokenUrl, { headers });
9696
+ if (response.status == 401) {
9697
+ this._requestLogger.debug("after GET %s: 401, about to retry with fresh access token", getTokenUrl);
9698
+ this._token = null;
9699
+ const headers2 = {
9700
+ "Authorization": await this.authorizationHeader()
9701
+ };
9702
+ this.updateState("FetchConfig" /* FetchConfig */);
9703
+ response = await this._client.fetch(getTokenUrl, { headers: headers2 });
9704
+ }
9705
+ if (response.status != 200) {
9706
+ const message = await response.text();
9707
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9708
+ }
9709
+ const wsAuthToken = await response.json();
9710
+ const wsUrl = this._baseUrl.startsWith("https://") ? `${this._baseUrl.replace("https://", "wss://")}/liveIngress/events/${wsAuthToken.token}` : `${this._baseUrl.replace("http://", "ws://")}/liveIngress/events/${wsAuthToken.token}`;
9711
+ this._ingressEventWs = new WebSocket(wsUrl);
9712
+ this._ingressEventWs.addEventListener("message", (event) => {
9713
+ const ingressEvent = JSON.parse(event.data);
9714
+ if (this._ingressEventHandler) {
9715
+ this._ingressEventHandler(ingressEvent);
9716
+ }
9717
+ });
9718
+ } finally {
9719
+ this.updateState();
9720
+ }
9721
+ }
9722
+ onIngressEvent(handler) {
9723
+ this._ingressEventHandler = handler;
9724
+ this.startIngressWs().then(() => {
9725
+ }).catch((reason) => {
9726
+ this._logger.warn("unexpected error starting the ingress event handler: %s", reason);
9727
+ });
9728
+ return this;
9729
+ }
9294
9730
  state() {
9295
9731
  return this._state;
9296
9732
  }
@@ -9323,39 +9759,130 @@ var Endpoint = class {
9323
9759
  pipelineId: this._pipelineId
9324
9760
  };
9325
9761
  }
9326
- async upload(params) {
9762
+ async liveIngress(stream) {
9763
+ if (!this._baseUrl || !this._client) {
9764
+ return Promise.reject("endpoint not connected, use connect() before ingress()");
9765
+ }
9766
+ const whip = new Whip(stream, async () => {
9767
+ return this.session();
9768
+ }, this._client, this._requestLogger);
9769
+ return whip.start();
9770
+ }
9771
+ async process(source) {
9772
+ if (source.file !== void 0) {
9773
+ return this.uploadFile(source);
9774
+ } else if (source.stream !== void 0) {
9775
+ return this.uploadStream(source);
9776
+ } else if (source.path !== void 0) {
9777
+ return this.uploadPath(source);
9778
+ } else if (source.url !== void 0) {
9779
+ return this.loadFrom(source);
9780
+ } else if (source.ingressId !== void 0) {
9781
+ return this.loadLiveIngress(source);
9782
+ } else {
9783
+ return Promise.reject("unknown source type");
9784
+ }
9785
+ }
9786
+ async uploadFile(source) {
9327
9787
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9328
- return Promise.reject("endpoint not connected, use open() before upload()");
9788
+ return Promise.reject("endpoint not connected, use connect() before upload()");
9329
9789
  }
9330
- params = await resolve(params);
9331
- if (!params.mimeType) {
9332
- return Promise.reject("upload for streams requires a mimeType");
9790
+ await this._limit.acquire();
9791
+ try {
9792
+ this.updateState();
9793
+ const job = new UploadJob(source.file, source.file.type, async () => {
9794
+ return this.session();
9795
+ }, this._client, this._requestLogger);
9796
+ return job.start(() => {
9797
+ this.jobDone(job);
9798
+ }, (statusCode) => {
9799
+ this.jobStatus(job, statusCode);
9800
+ });
9801
+ } catch (e) {
9802
+ this._limit.release();
9803
+ throw e;
9804
+ }
9805
+ }
9806
+ async uploadStream(source) {
9807
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9808
+ return Promise.reject("endpoint not connected, use connect() before upload()");
9333
9809
  }
9334
9810
  await this._limit.acquire();
9335
- this.updateState();
9336
- const job = new UploadJob(params.file, params.mimeType, async () => {
9337
- return this.session();
9338
- }, this._client, this._requestLogger);
9339
- return job.start(() => {
9340
- this.jobDone(job);
9341
- }, (statusCode) => {
9342
- this.jobStatus(job, statusCode);
9343
- });
9811
+ try {
9812
+ this.updateState();
9813
+ const job = new UploadJob(source.stream, source.mimeType, async () => {
9814
+ return this.session();
9815
+ }, this._client, this._requestLogger);
9816
+ return job.start(() => {
9817
+ this.jobDone(job);
9818
+ }, (statusCode) => {
9819
+ this.jobStatus(job, statusCode);
9820
+ });
9821
+ } catch (e) {
9822
+ this._limit.release();
9823
+ throw e;
9824
+ }
9344
9825
  }
9345
- async loadFrom(location) {
9826
+ async uploadPath(source) {
9346
9827
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9347
- throw new Error("endpoint not connected, use open() before loadFrom()");
9828
+ throw new Error("endpoint not connected, use connect() before process()");
9348
9829
  }
9349
9830
  await this._limit.acquire();
9350
- this.updateState();
9351
- const job = new LoadFromJob(location, async () => {
9352
- return this.session();
9353
- }, this._client, this._requestLogger);
9354
- return job.start(() => {
9355
- this.jobDone(job);
9356
- }, (statusCode) => {
9357
- this.jobStatus(job, statusCode);
9358
- });
9831
+ try {
9832
+ this.updateState();
9833
+ const streamSource = await resolvePath(source);
9834
+ const job = new UploadJob(streamSource.stream, streamSource.mimeType, async () => {
9835
+ return this.session();
9836
+ }, this._client, this._requestLogger);
9837
+ return job.start(() => {
9838
+ this.jobDone(job);
9839
+ }, (statusCode) => {
9840
+ this.jobStatus(job, statusCode);
9841
+ });
9842
+ } catch (e) {
9843
+ this._limit.release();
9844
+ throw e;
9845
+ }
9846
+ }
9847
+ async loadFrom(source) {
9848
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9849
+ throw new Error("endpoint not connected, use connect() before loadFrom()");
9850
+ }
9851
+ await this._limit.acquire();
9852
+ try {
9853
+ this.updateState();
9854
+ const job = new LoadFromJob(source.url, async () => {
9855
+ return this.session();
9856
+ }, this._client, this._requestLogger);
9857
+ return job.start(() => {
9858
+ this.jobDone(job);
9859
+ }, (statusCode) => {
9860
+ this.jobStatus(job, statusCode);
9861
+ });
9862
+ } catch (e) {
9863
+ this._limit.release();
9864
+ throw e;
9865
+ }
9866
+ }
9867
+ async loadLiveIngress(source) {
9868
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9869
+ throw new Error("endpoint not connected, use connect() before loadLiveIngress()");
9870
+ }
9871
+ await this._limit.acquire();
9872
+ try {
9873
+ this.updateState();
9874
+ const job = new LoadLiveIngressJob(source.ingressId, async () => {
9875
+ return this.session();
9876
+ }, this._client, this._requestLogger);
9877
+ return job.start(() => {
9878
+ this.jobDone(job);
9879
+ }, (statusCode) => {
9880
+ this.jobStatus(job, statusCode);
9881
+ });
9882
+ } catch (e) {
9883
+ this._limit.release();
9884
+ throw e;
9885
+ }
9359
9886
  }
9360
9887
  // noinspection TypeScriptValidateTypes
9361
9888
  async connect() {
@@ -9394,6 +9921,9 @@ var Endpoint = class {
9394
9921
  }
9395
9922
  this._requestLogger.debug("after PATCH %s", stop_url);
9396
9923
  }
9924
+ if (!this._ingressEventWs && this._ingressEventHandler) {
9925
+ await this.startIngressWs();
9926
+ }
9397
9927
  return result;
9398
9928
  }
9399
9929
  jobDone(job) {
@@ -9556,7 +10086,7 @@ var _EyePopPlot = class _EyePopPlot {
9556
10086
  }
9557
10087
  }
9558
10088
  }
9559
- object(obj, x_scale, y_scale) {
10089
+ object(obj, x_scale = 1, y_scale = 1) {
9560
10090
  const context = this._context;
9561
10091
  const x = obj.x * x_scale;
9562
10092
  const y = obj.y * y_scale;