@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.mjs CHANGED
@@ -8915,7 +8915,7 @@ var AbstractJob = class {
8915
8915
  return Promise.reject("abstract class");
8916
8916
  }
8917
8917
  start(done, status) {
8918
- this._responseStream = new Promise(async (resolve2, reject) => {
8918
+ this._responseStream = new Promise(async (resolve, reject) => {
8919
8919
  try {
8920
8920
  let retries = 1;
8921
8921
  while (true) {
@@ -8944,7 +8944,7 @@ var AbstractJob = class {
8944
8944
  reject(`response body is empty`);
8945
8945
  break;
8946
8946
  } else {
8947
- resolve2(response.body);
8947
+ resolve(response.body);
8948
8948
  break;
8949
8949
  }
8950
8950
  } catch (error) {
@@ -9035,6 +9035,39 @@ var LoadFromJob = class extends AbstractJob {
9035
9035
  return response;
9036
9036
  }
9037
9037
  };
9038
+ var LoadLiveIngressJob = class extends AbstractJob {
9039
+ constructor(ingressId, getSession, client, requestLogger) {
9040
+ super(getSession, client, requestLogger);
9041
+ this._ingressId = ingressId;
9042
+ }
9043
+ get [Symbol.toStringTag]() {
9044
+ return "loadLiveIngressJob";
9045
+ }
9046
+ async startJob() {
9047
+ const body = {
9048
+ "sourceType": "LIVE_INGRESS",
9049
+ "liveIngressId": this._ingressId
9050
+ };
9051
+ const session = await this._getSession();
9052
+ const headers = {
9053
+ "Authorization": `Bearer ${session.accessToken}`,
9054
+ "Accept": "application/jsonl",
9055
+ "Content-Type": "application/json"
9056
+ };
9057
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
9058
+ this._requestLogger.debug("before PATCH %s with url %s as source", patchUrl, this._ingressId);
9059
+ const response = await this._client.fetch(patchUrl, {
9060
+ headers,
9061
+ method: "PATCH",
9062
+ body: JSON.stringify(body),
9063
+ signal: this._controller.signal,
9064
+ // @ts-ignore
9065
+ dispatcher: this._dispatcher
9066
+ });
9067
+ this._requestLogger.debug("after PATCH %s with url %s as source", patchUrl, this._ingressId);
9068
+ return response;
9069
+ }
9070
+ };
9038
9071
 
9039
9072
  // src/shims/http_client.ts
9040
9073
  var createHttpClient;
@@ -9203,45 +9236,391 @@ var Semaphore = class {
9203
9236
  }
9204
9237
  };
9205
9238
 
9206
- // src/shims/local_file.ts
9207
- var resolve;
9208
- if ("document" in globalThis && "implementation" in globalThis.document) {
9209
- const isFileLike = (value) => value != null && typeof value === "object" && typeof value.name === "string" && typeof value.lastModified === "number";
9210
- resolve = async (params) => {
9211
- if (params.file) {
9212
- if (isFileLike(params.file)) {
9213
- params = {
9214
- mimeType: params.file.type,
9215
- file: params.file
9216
- };
9217
- return params;
9239
+ // node_modules/uuid/dist/esm-node/rng.js
9240
+ import crypto from "crypto";
9241
+ var rnds8Pool = new Uint8Array(256);
9242
+ var poolPtr = rnds8Pool.length;
9243
+ function rng() {
9244
+ if (poolPtr > rnds8Pool.length - 16) {
9245
+ crypto.randomFillSync(rnds8Pool);
9246
+ poolPtr = 0;
9247
+ }
9248
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
9249
+ }
9250
+
9251
+ // node_modules/uuid/dist/esm-node/stringify.js
9252
+ var byteToHex = [];
9253
+ for (let i = 0; i < 256; ++i) {
9254
+ byteToHex.push((i + 256).toString(16).slice(1));
9255
+ }
9256
+ function unsafeStringify(arr, offset = 0) {
9257
+ 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]];
9258
+ }
9259
+
9260
+ // node_modules/uuid/dist/esm-node/native.js
9261
+ import crypto2 from "crypto";
9262
+ var native_default = {
9263
+ randomUUID: crypto2.randomUUID
9264
+ };
9265
+
9266
+ // node_modules/uuid/dist/esm-node/v4.js
9267
+ function v4(options, buf, offset) {
9268
+ if (native_default.randomUUID && !buf && !options) {
9269
+ return native_default.randomUUID();
9270
+ }
9271
+ options = options || {};
9272
+ const rnds = options.random || (options.rng || rng)();
9273
+ rnds[6] = rnds[6] & 15 | 64;
9274
+ rnds[8] = rnds[8] & 63 | 128;
9275
+ if (buf) {
9276
+ offset = offset || 0;
9277
+ for (let i = 0; i < 16; ++i) {
9278
+ buf[offset + i] = rnds[i];
9279
+ }
9280
+ return buf;
9281
+ }
9282
+ return unsafeStringify(rnds);
9283
+ }
9284
+ var v4_default = v4;
9285
+
9286
+ // src/web_rtc_base.ts
9287
+ var WebRtcBase = class {
9288
+ constructor(getSession, client, requestLogger) {
9289
+ this._pc = null;
9290
+ this._getSession = getSession;
9291
+ this._client = client;
9292
+ this._requestLogger = requestLogger;
9293
+ this._eTag = "";
9294
+ this._queuedCandidates = [];
9295
+ this._offerData = null;
9296
+ }
9297
+ static linkToIceServers(links) {
9298
+ const servers = links !== null ? links.split(", ").map((link) => {
9299
+ const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
9300
+ if (!m) {
9301
+ return null;
9302
+ }
9303
+ const iceServer = {
9304
+ urls: [m[1]],
9305
+ username: Whip.unquoteCredential(m[3]),
9306
+ credential: Whip.unquoteCredential(m[4])
9307
+ // credentialType: m[3]? "password" : undefined
9308
+ };
9309
+ return iceServer;
9310
+ }) : [];
9311
+ return servers.filter(function(el) {
9312
+ return el != null;
9313
+ });
9314
+ }
9315
+ static editAnswer(answer, videoCodec, audioCodec, videoBitrate, audioBitrate, audioVoice) {
9316
+ const sections = answer.split("m=");
9317
+ for (let i = 0; i < sections.length; i++) {
9318
+ const section = sections[i];
9319
+ if (section.startsWith("video")) {
9320
+ sections[i] = Whip.setVideoBitrate(Whip.setCodec(section, videoCodec), videoBitrate);
9321
+ } else if (section.startsWith("audio")) {
9322
+ sections[i] = Whip.setAudioBitrate(Whip.setCodec(section, audioCodec), audioBitrate, audioVoice);
9218
9323
  }
9219
9324
  }
9220
- throw new DOMException("resolving a path to a file is not supported in browser");
9221
- };
9222
- } else {
9223
- resolve = async (params) => {
9224
- if (params.file) {
9225
- return params;
9325
+ return sections.join("m=");
9326
+ }
9327
+ static setCodec(section, codec) {
9328
+ const lines = section.split("\r\n");
9329
+ const lines2 = [];
9330
+ const payloadFormats = [];
9331
+ for (const line of lines) {
9332
+ if (!line.startsWith("a=rtpmap:")) {
9333
+ lines2.push(line);
9334
+ } else {
9335
+ if (codec && line.toLowerCase().includes(codec)) {
9336
+ payloadFormats.push(line.slice("a=rtpmap:".length).split(" ")[0]);
9337
+ lines2.push(line);
9338
+ }
9339
+ }
9226
9340
  }
9227
- const mime = require_mime_types();
9228
- const fs = __require("fs");
9229
- let mimeType;
9230
- let stream;
9231
- if (params.filePath) {
9232
- if (params.mimeType) {
9233
- mimeType = params.mimeType;
9341
+ const lines3 = [];
9342
+ for (const line of lines2) {
9343
+ if (line.startsWith("a=fmtp:")) {
9344
+ if (payloadFormats.includes(line.slice("a=fmtp:".length).split(" ")[0])) {
9345
+ lines3.push(line);
9346
+ }
9347
+ } else if (line.startsWith("a=rtcp-fb:")) {
9348
+ if (payloadFormats.includes(line.slice("a=rtcp-fb:".length).split(" ")[0])) {
9349
+ lines3.push(line);
9350
+ }
9234
9351
  } else {
9235
- mimeType = mime.lookup(params.filePath) || void 0;
9352
+ lines3.push(line);
9353
+ }
9354
+ }
9355
+ return lines3.join("\r\n");
9356
+ }
9357
+ static setVideoBitrate(section, bitrate) {
9358
+ let lines = section.split("\r\n");
9359
+ for (let i = 0; i < lines.length; i++) {
9360
+ if (lines[i].startsWith("c=")) {
9361
+ lines = [...lines.slice(0, i + 1), "b=TIAS:" + (bitrate * 1024).toString(), ...lines.slice(i + 1)];
9362
+ break;
9363
+ }
9364
+ }
9365
+ return lines.join("\r\n");
9366
+ }
9367
+ static setAudioBitrate(section, bitrate, voice) {
9368
+ let opusPayloadFormat = "";
9369
+ let lines = section.split("\r\n");
9370
+ for (let i = 0; i < lines.length; i++) {
9371
+ if (lines[i].startsWith("a=rtpmap:") && lines[i].toLowerCase().includes("opus/")) {
9372
+ opusPayloadFormat = lines[i].slice("a=rtpmap:".length).split(" ")[0];
9373
+ break;
9374
+ }
9375
+ }
9376
+ if (opusPayloadFormat === "") {
9377
+ return section;
9378
+ }
9379
+ for (let i = 0; i < lines.length; i++) {
9380
+ if (lines[i].startsWith("a=fmtp:" + opusPayloadFormat + " ")) {
9381
+ if (voice) {
9382
+ lines[i] = "a=fmtp:" + opusPayloadFormat + " minptime=10;useinbandfec=1;maxaveragebitrate=" + ((bitrate ?? 0) * 1024).toString();
9383
+ } else {
9384
+ lines[i] = "a=fmtp:" + opusPayloadFormat + " maxplaybackrate=48000;stereo=1;sprop-stereo=1;maxaveragebitrate" + ((bitrate ?? 0) * 1024).toString();
9385
+ }
9236
9386
  }
9237
- stream = fs.createReadStream(params.filePath);
9238
9387
  }
9239
- params = {
9240
- filePath: params.filePath,
9241
- mimeType,
9242
- file: stream
9388
+ return lines.join("\r\n");
9389
+ }
9390
+ static parseOffer(offer) {
9391
+ let medias = [];
9392
+ let iceUfrag = "";
9393
+ let icePwd = "";
9394
+ for (const line of offer.split("\r\n")) {
9395
+ if (line.startsWith("m=")) {
9396
+ medias.push(line.slice("m=".length));
9397
+ } else if (iceUfrag === "" && line.startsWith("a=ice-ufrag:")) {
9398
+ iceUfrag = line.slice("a=ice-ufrag:".length);
9399
+ } else if (icePwd === "" && line.startsWith("a=ice-pwd:")) {
9400
+ icePwd = line.slice("a=ice-pwd:".length);
9401
+ }
9402
+ }
9403
+ return {
9404
+ iceUfrag,
9405
+ icePwd,
9406
+ medias
9407
+ };
9408
+ }
9409
+ static generateSdpFragment(offerData, candidates) {
9410
+ const candidatesByMedia = /* @__PURE__ */ new Map();
9411
+ for (const candidate of candidates) {
9412
+ if (candidate.sdpMLineIndex !== null) {
9413
+ const mid2 = candidate.sdpMLineIndex;
9414
+ let matched = candidatesByMedia.get(mid2);
9415
+ if (matched === void 0) {
9416
+ matched = [];
9417
+ candidatesByMedia.set(mid2, matched);
9418
+ }
9419
+ matched.push(candidate);
9420
+ }
9421
+ }
9422
+ let frag = "a=ice-ufrag:" + offerData.iceUfrag + "\r\na=ice-pwd:" + offerData.icePwd + "\r\n";
9423
+ let mid = 0;
9424
+ for (const media of offerData.medias) {
9425
+ let matched = candidatesByMedia.get(mid);
9426
+ if (matched !== void 0) {
9427
+ frag += "m=" + media + "\r\na=mid:" + mid + "\r\n";
9428
+ for (const candidate of matched) {
9429
+ frag += "a=" + candidate.candidate + "\r\n";
9430
+ }
9431
+ }
9432
+ mid++;
9433
+ }
9434
+ return frag;
9435
+ }
9436
+ static unquoteCredential(v) {
9437
+ if (!v) {
9438
+ return "";
9439
+ }
9440
+ return JSON.parse(`"${v}"`);
9441
+ }
9442
+ };
9443
+
9444
+ // src/whip.ts
9445
+ var Whip = class _Whip extends WebRtcBase {
9446
+ constructor(stream, getSession, client, requestLogger) {
9447
+ super(getSession, client, requestLogger);
9448
+ this._ingressId = v4_default().toString();
9449
+ this._stream = stream;
9450
+ }
9451
+ ingressId() {
9452
+ if (!this._ingressId) {
9453
+ throw new Error("not initialized");
9454
+ }
9455
+ return this._ingressId;
9456
+ }
9457
+ async start() {
9458
+ const session = await this._getSession();
9459
+ const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9460
+ this._requestLogger.debug("before GET: %s", ingressUrl);
9461
+ const headers = {
9462
+ "Authorization": `Bearer ${session.accessToken}`
9463
+ };
9464
+ const response = await this._client.fetch(ingressUrl, {
9465
+ headers,
9466
+ method: "GET"
9467
+ });
9468
+ if (response.status >= 300) {
9469
+ return Promise.reject(`unknown status code for GET '${ingressUrl}': ${response.status} (${response.statusText})`);
9470
+ }
9471
+ return this.onIceServers(response);
9472
+ }
9473
+ async onIceServers(response) {
9474
+ if (!this._stream) {
9475
+ throw new Error("onIceServers no stream");
9476
+ }
9477
+ const stream = this._stream;
9478
+ const pc = new RTCPeerConnection({
9479
+ iceServers: _Whip.linkToIceServers(response.headers.get("Link"))
9480
+ });
9481
+ this._pc = pc;
9482
+ pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
9483
+ return new Promise(async (resolve, reject) => {
9484
+ pc.oniceconnectionstatechange = () => {
9485
+ switch (pc.iceConnectionState) {
9486
+ case "failed":
9487
+ case "disconnected":
9488
+ reject(`peer connection: ${pc.iceConnectionState}`);
9489
+ break;
9490
+ case "connected":
9491
+ resolve(this);
9492
+ break;
9493
+ }
9494
+ };
9495
+ stream.getTracks().forEach((track) => {
9496
+ this._requestLogger.debug("onIceServers our stream has track: %s", track);
9497
+ pc.addTrack(track, stream);
9498
+ });
9499
+ stream.addEventListener("addtrack", (trackEvent) => {
9500
+ this._requestLogger.debug("onIceServers got lazy track: ", trackEvent.track);
9501
+ pc.addTrack(trackEvent.track, stream);
9502
+ });
9503
+ await this.onLocalOffer(await pc.createOffer());
9504
+ });
9505
+ }
9506
+ async onLocalCandidate(evt) {
9507
+ if (evt.candidate !== null) {
9508
+ if (this._eTag === "") {
9509
+ this._queuedCandidates.push(evt.candidate);
9510
+ } else {
9511
+ await this.sendLocalCandidates([evt.candidate]);
9512
+ }
9513
+ }
9514
+ return this;
9515
+ }
9516
+ async onLocalOffer(offer) {
9517
+ if (!this._pc) {
9518
+ throw new Error("onLocalOffer no peer connection");
9519
+ }
9520
+ if (!offer.sdp) {
9521
+ throw new Error("onLocalOffer no sdp");
9522
+ }
9523
+ this._offerData = _Whip.parseOffer(offer.sdp);
9524
+ await this._pc.setLocalDescription(offer);
9525
+ const session = await this._getSession();
9526
+ const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9527
+ this._requestLogger.debug("before GET: %s", ingressUrl);
9528
+ const headers = {
9529
+ "Authorization": `Bearer ${session.accessToken}`,
9530
+ "Content-Type": "application/sdp"
9531
+ };
9532
+ const response = await this._client.fetch(ingressUrl, {
9533
+ headers,
9534
+ method: "POST",
9535
+ body: offer.sdp
9536
+ });
9537
+ if (response.status != 201) {
9538
+ return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
9539
+ }
9540
+ this._eTag = response.headers.get("ETag") ?? "";
9541
+ return this.onRemoteAnswer(new RTCSessionDescription({
9542
+ type: "answer",
9543
+ sdp: await response.text()
9544
+ }));
9545
+ }
9546
+ async onRemoteAnswer(answer) {
9547
+ if (!this._pc) {
9548
+ throw new Error("onLocalOffer no peer connection");
9549
+ }
9550
+ answer = new RTCSessionDescription({
9551
+ type: "answer",
9552
+ sdp: _Whip.editAnswer(
9553
+ answer.sdp,
9554
+ "h264/90000",
9555
+ void 0,
9556
+ 5e3,
9557
+ void 0,
9558
+ true
9559
+ )
9560
+ });
9561
+ await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
9562
+ if (this._queuedCandidates.length !== 0) {
9563
+ await this.sendLocalCandidates(this._queuedCandidates);
9564
+ this._queuedCandidates = [];
9565
+ }
9566
+ return this;
9567
+ }
9568
+ async sendLocalCandidates(candidates) {
9569
+ if (!this._offerData) {
9570
+ throw new Error("sendLocalCandidates no offerData");
9571
+ }
9572
+ const session = await this._getSession();
9573
+ const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9574
+ this._requestLogger.debug("before PATCH: %s", ingressUrl);
9575
+ const headers = {
9576
+ "Authorization": `Bearer ${session.accessToken}`,
9577
+ "Content-Type": "application/trickle-ice-sdpfrag",
9578
+ "If-Match": this._eTag
9579
+ };
9580
+ const response = await this._client.fetch(ingressUrl, {
9581
+ headers,
9582
+ method: "PATCH",
9583
+ body: _Whip.generateSdpFragment(this._offerData, candidates)
9584
+ });
9585
+ if (response.status != 204) {
9586
+ return Promise.reject(`unknown status code for PATCH '${ingressUrl}': ${response.status} (${response.statusText})`);
9587
+ }
9588
+ return this;
9589
+ }
9590
+ async close() {
9591
+ const pc = this._pc;
9592
+ const stream = this._stream;
9593
+ this._stream = null;
9594
+ this._pc = null;
9595
+ if (stream) {
9596
+ const tracks = stream.getTracks();
9597
+ tracks.forEach((track) => {
9598
+ track.stop();
9599
+ });
9600
+ }
9601
+ if (pc) {
9602
+ return pc.close();
9603
+ }
9604
+ }
9605
+ };
9606
+
9607
+ // src/shims/local_file.ts
9608
+ var resolvePath;
9609
+ if ("document" in globalThis && "implementation" in globalThis.document) {
9610
+ resolvePath = async (source) => {
9611
+ throw new DOMException("resolving a path to a file is not supported in browser");
9612
+ };
9613
+ } else {
9614
+ resolvePath = async (source) => {
9615
+ const mime = require_mime_types();
9616
+ const filehandle = __require("fs/promises");
9617
+ const fd = await filehandle.open(source.path, "r");
9618
+ const stream = fd.createReadStream();
9619
+ const mimeType = mime.lookup(source.path) || "application/octet-stream";
9620
+ return {
9621
+ stream,
9622
+ mimeType
9243
9623
  };
9244
- return params;
9245
9624
  };
9246
9625
  }
9247
9626
 
@@ -9258,6 +9637,8 @@ var Endpoint = class {
9258
9637
  this._client = null;
9259
9638
  this._state = "Idle" /* Idle */;
9260
9639
  this._stateChangeHandler = null;
9640
+ this._ingressEventHandler = null;
9641
+ this._ingressEventWs = null;
9261
9642
  this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9262
9643
  let rootLogger;
9263
9644
  if (options.logger) {
@@ -9272,6 +9653,51 @@ var Endpoint = class {
9272
9653
  this._stateChangeHandler = handler;
9273
9654
  return this;
9274
9655
  }
9656
+ async startIngressWs() {
9657
+ if (this._ingressEventWs || !this._baseUrl || !this._client) {
9658
+ return;
9659
+ }
9660
+ try {
9661
+ const getTokenUrl = `${this._baseUrl}/liveIngress/events/token`;
9662
+ const headers = {
9663
+ "Authorization": await this.authorizationHeader()
9664
+ };
9665
+ this._requestLogger.debug("before GET %s", getTokenUrl);
9666
+ let response = await this._client.fetch(getTokenUrl, { headers });
9667
+ if (response.status == 401) {
9668
+ this._requestLogger.debug("after GET %s: 401, about to retry with fresh access token", getTokenUrl);
9669
+ this._token = null;
9670
+ const headers2 = {
9671
+ "Authorization": await this.authorizationHeader()
9672
+ };
9673
+ this.updateState("FetchConfig" /* FetchConfig */);
9674
+ response = await this._client.fetch(getTokenUrl, { headers: headers2 });
9675
+ }
9676
+ if (response.status != 200) {
9677
+ const message = await response.text();
9678
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9679
+ }
9680
+ const wsAuthToken = await response.json();
9681
+ const wsUrl = this._baseUrl.startsWith("https://") ? `${this._baseUrl.replace("https://", "wss://")}/liveIngress/events/${wsAuthToken.token}` : `${this._baseUrl.replace("http://", "ws://")}/liveIngress/events/${wsAuthToken.token}`;
9682
+ this._ingressEventWs = new WebSocket(wsUrl);
9683
+ this._ingressEventWs.addEventListener("message", (event) => {
9684
+ const ingressEvent = JSON.parse(event.data);
9685
+ if (this._ingressEventHandler) {
9686
+ this._ingressEventHandler(ingressEvent);
9687
+ }
9688
+ });
9689
+ } finally {
9690
+ this.updateState();
9691
+ }
9692
+ }
9693
+ onIngressEvent(handler) {
9694
+ this._ingressEventHandler = handler;
9695
+ this.startIngressWs().then(() => {
9696
+ }).catch((reason) => {
9697
+ this._logger.warn("unexpected error starting the ingress event handler: %s", reason);
9698
+ });
9699
+ return this;
9700
+ }
9275
9701
  state() {
9276
9702
  return this._state;
9277
9703
  }
@@ -9304,39 +9730,130 @@ var Endpoint = class {
9304
9730
  pipelineId: this._pipelineId
9305
9731
  };
9306
9732
  }
9307
- async upload(params) {
9733
+ async liveIngress(stream) {
9734
+ if (!this._baseUrl || !this._client) {
9735
+ return Promise.reject("endpoint not connected, use connect() before ingress()");
9736
+ }
9737
+ const whip = new Whip(stream, async () => {
9738
+ return this.session();
9739
+ }, this._client, this._requestLogger);
9740
+ return whip.start();
9741
+ }
9742
+ async process(source) {
9743
+ if (source.file !== void 0) {
9744
+ return this.uploadFile(source);
9745
+ } else if (source.stream !== void 0) {
9746
+ return this.uploadStream(source);
9747
+ } else if (source.path !== void 0) {
9748
+ return this.uploadPath(source);
9749
+ } else if (source.url !== void 0) {
9750
+ return this.loadFrom(source);
9751
+ } else if (source.ingressId !== void 0) {
9752
+ return this.loadLiveIngress(source);
9753
+ } else {
9754
+ return Promise.reject("unknown source type");
9755
+ }
9756
+ }
9757
+ async uploadFile(source) {
9308
9758
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9309
- return Promise.reject("endpoint not connected, use open() before upload()");
9759
+ return Promise.reject("endpoint not connected, use connect() before upload()");
9310
9760
  }
9311
- params = await resolve(params);
9312
- if (!params.mimeType) {
9313
- return Promise.reject("upload for streams requires a mimeType");
9761
+ await this._limit.acquire();
9762
+ try {
9763
+ this.updateState();
9764
+ const job = new UploadJob(source.file, source.file.type, async () => {
9765
+ return this.session();
9766
+ }, this._client, this._requestLogger);
9767
+ return job.start(() => {
9768
+ this.jobDone(job);
9769
+ }, (statusCode) => {
9770
+ this.jobStatus(job, statusCode);
9771
+ });
9772
+ } catch (e) {
9773
+ this._limit.release();
9774
+ throw e;
9775
+ }
9776
+ }
9777
+ async uploadStream(source) {
9778
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9779
+ return Promise.reject("endpoint not connected, use connect() before upload()");
9314
9780
  }
9315
9781
  await this._limit.acquire();
9316
- this.updateState();
9317
- const job = new UploadJob(params.file, params.mimeType, async () => {
9318
- return this.session();
9319
- }, this._client, this._requestLogger);
9320
- return job.start(() => {
9321
- this.jobDone(job);
9322
- }, (statusCode) => {
9323
- this.jobStatus(job, statusCode);
9324
- });
9782
+ try {
9783
+ this.updateState();
9784
+ const job = new UploadJob(source.stream, source.mimeType, async () => {
9785
+ return this.session();
9786
+ }, this._client, this._requestLogger);
9787
+ return job.start(() => {
9788
+ this.jobDone(job);
9789
+ }, (statusCode) => {
9790
+ this.jobStatus(job, statusCode);
9791
+ });
9792
+ } catch (e) {
9793
+ this._limit.release();
9794
+ throw e;
9795
+ }
9325
9796
  }
9326
- async loadFrom(location) {
9797
+ async uploadPath(source) {
9327
9798
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9328
- throw new Error("endpoint not connected, use open() before loadFrom()");
9799
+ throw new Error("endpoint not connected, use connect() before process()");
9329
9800
  }
9330
9801
  await this._limit.acquire();
9331
- this.updateState();
9332
- const job = new LoadFromJob(location, async () => {
9333
- return this.session();
9334
- }, this._client, this._requestLogger);
9335
- return job.start(() => {
9336
- this.jobDone(job);
9337
- }, (statusCode) => {
9338
- this.jobStatus(job, statusCode);
9339
- });
9802
+ try {
9803
+ this.updateState();
9804
+ const streamSource = await resolvePath(source);
9805
+ const job = new UploadJob(streamSource.stream, streamSource.mimeType, async () => {
9806
+ return this.session();
9807
+ }, this._client, this._requestLogger);
9808
+ return job.start(() => {
9809
+ this.jobDone(job);
9810
+ }, (statusCode) => {
9811
+ this.jobStatus(job, statusCode);
9812
+ });
9813
+ } catch (e) {
9814
+ this._limit.release();
9815
+ throw e;
9816
+ }
9817
+ }
9818
+ async loadFrom(source) {
9819
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9820
+ throw new Error("endpoint not connected, use connect() before loadFrom()");
9821
+ }
9822
+ await this._limit.acquire();
9823
+ try {
9824
+ this.updateState();
9825
+ const job = new LoadFromJob(source.url, async () => {
9826
+ return this.session();
9827
+ }, this._client, this._requestLogger);
9828
+ return job.start(() => {
9829
+ this.jobDone(job);
9830
+ }, (statusCode) => {
9831
+ this.jobStatus(job, statusCode);
9832
+ });
9833
+ } catch (e) {
9834
+ this._limit.release();
9835
+ throw e;
9836
+ }
9837
+ }
9838
+ async loadLiveIngress(source) {
9839
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9840
+ throw new Error("endpoint not connected, use connect() before loadLiveIngress()");
9841
+ }
9842
+ await this._limit.acquire();
9843
+ try {
9844
+ this.updateState();
9845
+ const job = new LoadLiveIngressJob(source.ingressId, async () => {
9846
+ return this.session();
9847
+ }, this._client, this._requestLogger);
9848
+ return job.start(() => {
9849
+ this.jobDone(job);
9850
+ }, (statusCode) => {
9851
+ this.jobStatus(job, statusCode);
9852
+ });
9853
+ } catch (e) {
9854
+ this._limit.release();
9855
+ throw e;
9856
+ }
9340
9857
  }
9341
9858
  // noinspection TypeScriptValidateTypes
9342
9859
  async connect() {
@@ -9375,6 +9892,9 @@ var Endpoint = class {
9375
9892
  }
9376
9893
  this._requestLogger.debug("after PATCH %s", stop_url);
9377
9894
  }
9895
+ if (!this._ingressEventWs && this._ingressEventHandler) {
9896
+ await this.startIngressWs();
9897
+ }
9378
9898
  return result;
9379
9899
  }
9380
9900
  jobDone(job) {
@@ -9537,7 +10057,7 @@ var _EyePopPlot = class _EyePopPlot {
9537
10057
  }
9538
10058
  }
9539
10059
  }
9540
- object(obj, x_scale, y_scale) {
10060
+ object(obj, x_scale = 1, y_scale = 1) {
9541
10061
  const context = this._context;
9542
10062
  const x = obj.x * x_scale;
9543
10063
  const y = obj.y * y_scale;