@eyepop.ai/eyepop 0.12.0 → 0.13.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
@@ -9083,7 +9083,7 @@ var LoadLiveIngressJob = class extends AbstractJob {
9083
9083
  "Accept": "application/jsonl",
9084
9084
  "Content-Type": "application/json"
9085
9085
  };
9086
- const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
9086
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=preempt&processing=sync`;
9087
9087
  this._requestLogger.debug("before PATCH %s with url %s as source", patchUrl, this._ingressId);
9088
9088
  const response = await this._client.fetch(patchUrl, {
9089
9089
  headers,
@@ -9312,17 +9312,91 @@ function v4(options, buf, offset) {
9312
9312
  }
9313
9313
  var v4_default = v4;
9314
9314
 
9315
- // src/web_rtc_base.ts
9316
- var WebRtcBase = class {
9317
- constructor(getSession, client, requestLogger) {
9318
- this._pc = null;
9315
+ // src/webrtc_base.ts
9316
+ var WebrtcBase = class _WebrtcBase {
9317
+ constructor(getSession, client, ingressId, urlBasePath, requestLogger) {
9319
9318
  this._getSession = getSession;
9320
9319
  this._client = client;
9321
9320
  this._requestLogger = requestLogger;
9321
+ this._ingressId = ingressId;
9322
+ this._urlPath = `${urlBasePath}/${this._ingressId}`;
9323
+ this._pc = null;
9322
9324
  this._eTag = "";
9323
9325
  this._queuedCandidates = [];
9324
9326
  this._offerData = null;
9325
9327
  }
9328
+ async close() {
9329
+ const pc = this._pc;
9330
+ this._pc = null;
9331
+ if (pc) {
9332
+ return pc.close();
9333
+ }
9334
+ }
9335
+ ingressId() {
9336
+ return this._ingressId;
9337
+ }
9338
+ async onLocalOffer(offer) {
9339
+ if (!this._pc) {
9340
+ throw new Error("onLocalOffer no peer connection");
9341
+ }
9342
+ if (!offer.sdp) {
9343
+ throw new Error("onLocalOffer no sdp");
9344
+ }
9345
+ this._offerData = _WebrtcBase.parseOffer(offer.sdp);
9346
+ await this._pc.setLocalDescription(offer);
9347
+ const session = await this._getSession();
9348
+ const ingressUrl = new URL(this._urlPath, session.baseUrl);
9349
+ this._requestLogger.debug("before POST: %s", ingressUrl);
9350
+ const headers = {
9351
+ "Authorization": `Bearer ${session.accessToken}`,
9352
+ "Content-Type": "application/sdp"
9353
+ };
9354
+ const response = await this._client.fetch(ingressUrl, {
9355
+ headers,
9356
+ method: "POST",
9357
+ body: offer.sdp
9358
+ });
9359
+ if (response.status != 201) {
9360
+ return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
9361
+ }
9362
+ this._eTag = response.headers.get("ETag") ?? "";
9363
+ return this.onRemoteAnswer(new RTCSessionDescription({
9364
+ type: "answer",
9365
+ sdp: await response.text()
9366
+ }));
9367
+ }
9368
+ async sendLocalCandidates(candidates) {
9369
+ if (!this._offerData) {
9370
+ throw new Error("sendLocalCandidates no offerData");
9371
+ }
9372
+ const session = await this._getSession();
9373
+ const ingressUrl = new URL(this._urlPath, session.baseUrl);
9374
+ this._requestLogger.debug("before PATCH: %s", ingressUrl);
9375
+ const headers = {
9376
+ "Authorization": `Bearer ${session.accessToken}`,
9377
+ "Content-Type": "application/trickle-ice-sdpfrag",
9378
+ "If-Match": this._eTag
9379
+ };
9380
+ const response = await this._client.fetch(ingressUrl, {
9381
+ headers,
9382
+ method: "PATCH",
9383
+ body: _WebrtcBase.generateSdpFragment(this._offerData, candidates)
9384
+ });
9385
+ if (response.status != 204) {
9386
+ return Promise.reject(`unknown status code for PATCH '${ingressUrl}': ${response.status} (${response.statusText})`);
9387
+ }
9388
+ return this;
9389
+ }
9390
+ async onLocalCandidate(evt) {
9391
+ if (evt.candidate !== null) {
9392
+ if (this._eTag === "") {
9393
+ this._queuedCandidates.push(evt.candidate);
9394
+ } else {
9395
+ await this.sendLocalCandidates([evt.candidate]);
9396
+ }
9397
+ }
9398
+ return this;
9399
+ }
9326
9400
  static linkToIceServers(links) {
9327
9401
  const servers = links !== null ? links.split(", ").map((link) => {
9328
9402
  const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
@@ -9331,8 +9405,8 @@ var WebRtcBase = class {
9331
9405
  }
9332
9406
  const iceServer = {
9333
9407
  urls: [m[1]],
9334
- username: Whip.unquoteCredential(m[3]),
9335
- credential: Whip.unquoteCredential(m[4])
9408
+ username: WebrtcWhip.unquoteCredential(m[3]),
9409
+ credential: WebrtcWhip.unquoteCredential(m[4])
9336
9410
  // credentialType: m[3]? "password" : undefined
9337
9411
  };
9338
9412
  return iceServer;
@@ -9346,9 +9420,9 @@ var WebRtcBase = class {
9346
9420
  for (let i = 0; i < sections.length; i++) {
9347
9421
  const section = sections[i];
9348
9422
  if (section.startsWith("video")) {
9349
- sections[i] = Whip.setVideoBitrate(Whip.setCodec(section, videoCodec), videoBitrate);
9423
+ sections[i] = WebrtcWhip.setVideoBitrate(WebrtcWhip.setCodec(section, videoCodec), videoBitrate);
9350
9424
  } else if (section.startsWith("audio")) {
9351
- sections[i] = Whip.setAudioBitrate(Whip.setCodec(section, audioCodec), audioBitrate, audioVoice);
9425
+ sections[i] = WebrtcWhip.setAudioBitrate(WebrtcWhip.setCodec(section, audioCodec), audioBitrate, audioVoice);
9352
9426
  }
9353
9427
  }
9354
9428
  return sections.join("m=");
@@ -9470,22 +9544,15 @@ var WebRtcBase = class {
9470
9544
  }
9471
9545
  };
9472
9546
 
9473
- // src/whip.ts
9474
- var Whip = class _Whip extends WebRtcBase {
9547
+ // src/webrtc_whip.ts
9548
+ var WebrtcWhip = class _WebrtcWhip extends WebrtcBase {
9475
9549
  constructor(stream, getSession, client, requestLogger) {
9476
- super(getSession, client, requestLogger);
9477
- this._ingressId = v4_default().toString();
9550
+ super(getSession, client, v4_default().toString(), "/liveIngress/whip", requestLogger);
9478
9551
  this._stream = stream;
9479
9552
  }
9480
- ingressId() {
9481
- if (!this._ingressId) {
9482
- throw new Error("not initialized");
9483
- }
9484
- return this._ingressId;
9485
- }
9486
9553
  async start() {
9487
9554
  const session = await this._getSession();
9488
- const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9555
+ const ingressUrl = new URL(this._urlPath, session.baseUrl);
9489
9556
  this._requestLogger.debug("before GET: %s", ingressUrl);
9490
9557
  const headers = {
9491
9558
  "Authorization": `Bearer ${session.accessToken}`
@@ -9499,13 +9566,42 @@ var Whip = class _Whip extends WebRtcBase {
9499
9566
  }
9500
9567
  return this.onIceServers(response);
9501
9568
  }
9569
+ async stream() {
9570
+ if (this._stream) {
9571
+ return Promise.resolve(this._stream);
9572
+ } else {
9573
+ return Promise.reject("fetching stream failed");
9574
+ }
9575
+ }
9576
+ async onRemoteAnswer(answer) {
9577
+ if (!this._pc) {
9578
+ throw new Error("onLocalOffer no peer connection");
9579
+ }
9580
+ answer = new RTCSessionDescription({
9581
+ type: "answer",
9582
+ sdp: _WebrtcWhip.editAnswer(
9583
+ answer.sdp,
9584
+ "h264/90000",
9585
+ void 0,
9586
+ 5e3,
9587
+ void 0,
9588
+ true
9589
+ )
9590
+ });
9591
+ await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
9592
+ if (this._queuedCandidates.length !== 0) {
9593
+ await this.sendLocalCandidates(this._queuedCandidates);
9594
+ this._queuedCandidates = [];
9595
+ }
9596
+ return this;
9597
+ }
9502
9598
  async onIceServers(response) {
9503
9599
  if (!this._stream) {
9504
9600
  throw new Error("onIceServers no stream");
9505
9601
  }
9506
9602
  const stream = this._stream;
9507
9603
  const pc = new RTCPeerConnection({
9508
- iceServers: _Whip.linkToIceServers(response.headers.get("Link"))
9604
+ iceServers: WebrtcBase.linkToIceServers(response.headers.get("Link"))
9509
9605
  });
9510
9606
  this._pc = pc;
9511
9607
  pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
@@ -9532,61 +9628,63 @@ var Whip = class _Whip extends WebRtcBase {
9532
9628
  await this.onLocalOffer(await pc.createOffer());
9533
9629
  });
9534
9630
  }
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
- }
9631
+ async close() {
9632
+ const stream = this._stream;
9633
+ this._stream = null;
9634
+ if (stream) {
9635
+ const tracks = stream.getTracks();
9636
+ tracks.forEach((track) => {
9637
+ track.stop();
9638
+ });
9542
9639
  }
9543
- return this;
9640
+ return super.close();
9544
9641
  }
9545
- async onLocalOffer(offer) {
9546
- if (!this._pc) {
9547
- throw new Error("onLocalOffer no peer connection");
9642
+ };
9643
+
9644
+ // src/webrtc_whep.ts
9645
+ var WebrtcWhep = class extends WebrtcBase {
9646
+ constructor(ingressId, getSession, client, requestLogger) {
9647
+ super(getSession, client, ingressId, "/liveIngress/whep", requestLogger);
9648
+ this._stream = null;
9649
+ }
9650
+ async close() {
9651
+ const stream = this._stream;
9652
+ this._stream = null;
9653
+ if (stream) {
9654
+ const tracks = stream.getTracks();
9655
+ tracks.forEach((track) => {
9656
+ track.stop();
9657
+ });
9548
9658
  }
9549
- if (!offer.sdp) {
9550
- throw new Error("onLocalOffer no sdp");
9659
+ return super.close();
9660
+ }
9661
+ async stream() {
9662
+ if (this._stream) {
9663
+ return Promise.resolve(this._stream);
9664
+ } else {
9665
+ return Promise.reject("fetching stream failed");
9551
9666
  }
9552
- this._offerData = _Whip.parseOffer(offer.sdp);
9553
- await this._pc.setLocalDescription(offer);
9667
+ }
9668
+ async start() {
9554
9669
  const session = await this._getSession();
9555
- const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9556
- this._requestLogger.debug("before GET: %s", ingressUrl);
9670
+ const egressUrl = new URL(this._urlPath, session.baseUrl);
9671
+ this._requestLogger.debug("before GET: %s", egressUrl);
9557
9672
  const headers = {
9558
- "Authorization": `Bearer ${session.accessToken}`,
9559
- "Content-Type": "application/sdp"
9673
+ "Authorization": `Bearer ${session.accessToken}`
9560
9674
  };
9561
- const response = await this._client.fetch(ingressUrl, {
9675
+ const response = await this._client.fetch(egressUrl, {
9562
9676
  headers,
9563
- method: "POST",
9564
- body: offer.sdp
9677
+ method: "GET"
9565
9678
  });
9566
- if (response.status != 201) {
9567
- return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
9679
+ if (response.status >= 300) {
9680
+ return Promise.reject(`unknown status code for GET '${egressUrl}': ${response.status} (${response.statusText})`);
9568
9681
  }
9569
- this._eTag = response.headers.get("ETag") ?? "";
9570
- return this.onRemoteAnswer(new RTCSessionDescription({
9571
- type: "answer",
9572
- sdp: await response.text()
9573
- }));
9682
+ return this.onIceServers(response);
9574
9683
  }
9575
9684
  async onRemoteAnswer(answer) {
9576
9685
  if (!this._pc) {
9577
9686
  throw new Error("onLocalOffer no peer connection");
9578
9687
  }
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
9688
  await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
9591
9689
  if (this._queuedCandidates.length !== 0) {
9592
9690
  await this.sendLocalCandidates(this._queuedCandidates);
@@ -9594,42 +9692,38 @@ var Whip = class _Whip extends WebRtcBase {
9594
9692
  }
9595
9693
  return this;
9596
9694
  }
9597
- async sendLocalCandidates(candidates) {
9598
- if (!this._offerData) {
9599
- throw new Error("sendLocalCandidates no offerData");
9695
+ async onIceServers(response) {
9696
+ if (this._stream) {
9697
+ throw new Error("onIceServers has stream");
9600
9698
  }
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)
9699
+ const pc = new RTCPeerConnection({
9700
+ iceServers: WebrtcBase.linkToIceServers(response.headers.get("Link"))
9701
+ });
9702
+ const direction = "sendrecv";
9703
+ pc.addTransceiver("video", { direction });
9704
+ pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
9705
+ this._pc = pc;
9706
+ this._requestLogger.info("before", pc.iceConnectionState);
9707
+ return new Promise(async (resolve, reject) => {
9708
+ pc.ontrack = (evt) => {
9709
+ this._requestLogger.debug("new track:", evt.track.kind);
9710
+ this._stream = evt.streams[0];
9711
+ resolve(this);
9712
+ };
9713
+ pc.oniceconnectionstatechange = () => {
9714
+ switch (pc.iceConnectionState) {
9715
+ case "failed":
9716
+ case "disconnected":
9717
+ this._requestLogger.debug("disconnected");
9718
+ reject(`peer connection: ${pc.iceConnectionState}`);
9719
+ break;
9720
+ case "connected":
9721
+ this._requestLogger.debug("connected");
9722
+ break;
9723
+ }
9724
+ };
9725
+ await this.onLocalOffer(await pc.createOffer());
9613
9726
  });
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
9727
  }
9634
9728
  };
9635
9729
 
@@ -9655,6 +9749,43 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9655
9749
 
9656
9750
  // src/endpoint.ts
9657
9751
  var import_pino = require("pino");
9752
+
9753
+ // src/shims/browser_session.ts
9754
+ var import_auth0_spa_js = require("@auth0/auth0-spa-js");
9755
+ var authenticateBrowserSession;
9756
+ if ("document" in globalThis && "implementation" in globalThis.document) {
9757
+ authenticateBrowserSession = async (auth0, eyepopUrl, popId) => {
9758
+ const options = {
9759
+ clientId: auth0.clientId,
9760
+ domain: auth0.domain,
9761
+ authorizationParams: {
9762
+ scope: auth0.scope,
9763
+ audience: auth0.audience
9764
+ }
9765
+ };
9766
+ const auth0Client = await (0, import_auth0_spa_js.createAuth0Client)(options);
9767
+ if (!await auth0Client.isAuthenticated()) {
9768
+ await auth0Client.loginWithPopup();
9769
+ }
9770
+ const accessToken = await auth0Client.getTokenSilently();
9771
+ if (!accessToken) {
9772
+ return Promise.reject("auth0 login failed");
9773
+ }
9774
+ const session = {
9775
+ eyepopUrl,
9776
+ popId,
9777
+ accessToken,
9778
+ validUntil: Date.now() + 60 * 60 * 1e3
9779
+ };
9780
+ return session;
9781
+ };
9782
+ } else {
9783
+ authenticateBrowserSession = (auth0, eyepopUrl, popId) => {
9784
+ return Promise.reject("auth0 login not supported server-side");
9785
+ };
9786
+ }
9787
+
9788
+ // src/endpoint.ts
9658
9789
  var Endpoint = class {
9659
9790
  constructor(options) {
9660
9791
  this._options = options;
@@ -9678,55 +9809,13 @@ var Endpoint = class {
9678
9809
  this._logger = rootLogger.child({ module: "eyepop" });
9679
9810
  this._requestLogger = this._logger.child({ module: "requests" });
9680
9811
  }
9681
- onStateChanged(handler) {
9682
- this._stateChangeHandler = handler;
9683
- return this;
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();
9812
+ eyepopUrl() {
9813
+ if (this._options.eyepopUrl) {
9814
+ return this._options.eyepopUrl.replace(/\/+$/, "");
9815
+ } else {
9816
+ return "https://api.eyepop.ai";
9720
9817
  }
9721
9818
  }
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
- }
9730
9819
  state() {
9731
9820
  return this._state;
9732
9821
  }
@@ -9739,6 +9828,80 @@ var Endpoint = class {
9739
9828
  popId() {
9740
9829
  return this._options.popId || null;
9741
9830
  }
9831
+ onStateChanged(handler) {
9832
+ this._stateChangeHandler = handler;
9833
+ return this;
9834
+ }
9835
+ onIngressEvent(handler) {
9836
+ this._ingressEventHandler = handler;
9837
+ this.startIngressWs().then(() => {
9838
+ }).catch((reason) => {
9839
+ this._logger.warn("unexpected error starting the ingress event handler: %s", reason);
9840
+ });
9841
+ return this;
9842
+ }
9843
+ async connect() {
9844
+ if (this._client) {
9845
+ this._logger.warn("endpoint already connected");
9846
+ return this;
9847
+ }
9848
+ if (!this._options.eyepopUrl) {
9849
+ return Promise.reject("option eyepopUrl or environment variable EYEPOP_URL is required");
9850
+ }
9851
+ if (!this._options.popId) {
9852
+ return Promise.reject("option popId or environment variable EYEPOP_POP_ID is required");
9853
+ }
9854
+ if (this._options.auth === void 0) {
9855
+ return Promise.reject("cannot connect without defined auth option");
9856
+ }
9857
+ if (this._options.auth.session !== void 0) {
9858
+ this._token = this._options.auth.session.accessToken;
9859
+ this._expire_token_time = this._options.auth.session.validUntil / 1e3;
9860
+ } else if (this._options.auth.oAuth2 !== void 0) {
9861
+ } else if (this._options.auth.secretKey !== void 0) {
9862
+ } else {
9863
+ return Promise.reject("option secretKey or environment variable EYEPOP_SECRET_KEY is required");
9864
+ }
9865
+ this._client = await createHttpClient();
9866
+ let result = await this.reconnect();
9867
+ if (this._baseUrl && this._options.stopJobs) {
9868
+ const stop_url = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=preempt&processing=sync`;
9869
+ const body = { "sourceType": "NONE" };
9870
+ const headers = {
9871
+ "Authorization": await this.authorizationHeader(),
9872
+ "Content-Type": "application/json"
9873
+ };
9874
+ this._requestLogger.debug("before PATCH %s", stop_url);
9875
+ let response = await this._client.fetch(stop_url, {
9876
+ method: "PATCH",
9877
+ headers,
9878
+ body: JSON.stringify(body)
9879
+ });
9880
+ if (response.status >= 300) {
9881
+ const message = await response.text();
9882
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9883
+ }
9884
+ this._requestLogger.debug("after PATCH %s", stop_url);
9885
+ }
9886
+ if (!this._ingressEventWs && this._ingressEventHandler) {
9887
+ await this.startIngressWs();
9888
+ }
9889
+ return result;
9890
+ }
9891
+ async disconnect(wait = true) {
9892
+ if (wait && this._limit) {
9893
+ for (let j = 0; j < this._options.jobQueueLength; j++) {
9894
+ await this._limit.acquire();
9895
+ }
9896
+ }
9897
+ this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9898
+ if (this._client) {
9899
+ await this._client.close();
9900
+ this._client = null;
9901
+ }
9902
+ this._baseUrl = null;
9903
+ this._pipelineId = null;
9904
+ }
9742
9905
  async session() {
9743
9906
  await this.currentAccessToken();
9744
9907
  if (this._token == null || this._expire_token_time == null) {
@@ -9763,11 +9926,20 @@ var Endpoint = class {
9763
9926
  if (!this._baseUrl || !this._client) {
9764
9927
  return Promise.reject("endpoint not connected, use connect() before ingress()");
9765
9928
  }
9766
- const whip = new Whip(stream, async () => {
9929
+ const whip = new WebrtcWhip(stream, async () => {
9767
9930
  return this.session();
9768
9931
  }, this._client, this._requestLogger);
9769
9932
  return whip.start();
9770
9933
  }
9934
+ async liveEgress(ingressId) {
9935
+ if (!this._baseUrl || !this._client) {
9936
+ return Promise.reject("endpoint not connected, use connect() before ingress()");
9937
+ }
9938
+ const whep = new WebrtcWhep(ingressId, async () => {
9939
+ return this.session();
9940
+ }, this._client, this._requestLogger);
9941
+ return whep.start();
9942
+ }
9771
9943
  async process(source) {
9772
9944
  if (source.file !== void 0) {
9773
9945
  return this.uploadFile(source);
@@ -9783,6 +9955,43 @@ var Endpoint = class {
9783
9955
  return Promise.reject("unknown source type");
9784
9956
  }
9785
9957
  }
9958
+ async startIngressWs() {
9959
+ if (this._ingressEventWs || !this._baseUrl || !this._client) {
9960
+ return;
9961
+ }
9962
+ try {
9963
+ const getTokenUrl = `${this._baseUrl}/liveIngress/events/token`;
9964
+ const headers = {
9965
+ "Authorization": await this.authorizationHeader()
9966
+ };
9967
+ this._requestLogger.debug("before GET %s", getTokenUrl);
9968
+ let response = await this._client.fetch(getTokenUrl, { headers });
9969
+ if (response.status == 401) {
9970
+ this._requestLogger.debug("after GET %s: 401, about to retry with fresh access token", getTokenUrl);
9971
+ this._token = null;
9972
+ const headers2 = {
9973
+ "Authorization": await this.authorizationHeader()
9974
+ };
9975
+ this.updateState("FetchConfig" /* FetchConfig */);
9976
+ response = await this._client.fetch(getTokenUrl, { headers: headers2 });
9977
+ }
9978
+ if (response.status != 200) {
9979
+ const message = await response.text();
9980
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9981
+ }
9982
+ const wsAuthToken = await response.json();
9983
+ const wsUrl = this._baseUrl.startsWith("https://") ? `${this._baseUrl.replace("https://", "wss://")}/liveIngress/events/${wsAuthToken.token}` : `${this._baseUrl.replace("http://", "ws://")}/liveIngress/events/${wsAuthToken.token}`;
9984
+ this._ingressEventWs = new WebSocket(wsUrl);
9985
+ this._ingressEventWs.addEventListener("message", (event) => {
9986
+ const ingressEvent = JSON.parse(event.data);
9987
+ if (this._ingressEventHandler) {
9988
+ this._ingressEventHandler(ingressEvent);
9989
+ }
9990
+ });
9991
+ } finally {
9992
+ this.updateState();
9993
+ }
9994
+ }
9786
9995
  async uploadFile(source) {
9787
9996
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9788
9997
  return Promise.reject("endpoint not connected, use connect() before upload()");
@@ -9884,48 +10093,6 @@ var Endpoint = class {
9884
10093
  throw e;
9885
10094
  }
9886
10095
  }
9887
- // noinspection TypeScriptValidateTypes
9888
- async connect() {
9889
- if (this._client) {
9890
- console.info("endpoint already connected");
9891
- return this;
9892
- }
9893
- if (!this._options.eyepopUrl) {
9894
- return Promise.reject("option eyepopUrl or environment variable EYEPOP_URL is required");
9895
- }
9896
- if (!this._options.popId) {
9897
- return Promise.reject("option popId or environment variable EYEPOP_POP_ID is required");
9898
- }
9899
- if (this._options.auth && this._options.auth.session) {
9900
- this._token = this._options.auth.session.accessToken;
9901
- this._expire_token_time = this._options.auth.session.validUntil / 1e3;
9902
- } else {
9903
- if (!this._options.auth || !this._options.auth.secretKey) {
9904
- return Promise.reject("option secretKey or environment variable EYEPOP_SECRET_KEY is required");
9905
- }
9906
- }
9907
- this._client = await createHttpClient();
9908
- let result = await this.reconnect();
9909
- if (this._baseUrl && this._options.stopJobs) {
9910
- const stop_url = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=preempt&processing=sync`;
9911
- const body = { "sourceType": "NONE" };
9912
- const headers = {
9913
- "Authorization": await this.authorizationHeader(),
9914
- "Content-Type": "application/json"
9915
- };
9916
- this._requestLogger.debug("before PATCH %s", stop_url);
9917
- let response = await this._client.fetch(stop_url, { method: "PATCH", headers, body: JSON.stringify(body) });
9918
- if (response.status >= 300) {
9919
- const message = await response.text();
9920
- return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9921
- }
9922
- this._requestLogger.debug("after PATCH %s", stop_url);
9923
- }
9924
- if (!this._ingressEventWs && this._ingressEventHandler) {
9925
- await this.startIngressWs();
9926
- }
9927
- return result;
9928
- }
9929
10096
  jobDone(job) {
9930
10097
  this._limit?.release();
9931
10098
  this.updateState();
@@ -10012,20 +10179,6 @@ var Endpoint = class {
10012
10179
  return Promise.resolve(this);
10013
10180
  }
10014
10181
  }
10015
- async disconnect(wait = true) {
10016
- if (wait && this._limit) {
10017
- for (let j = 0; j < this._options.jobQueueLength; j++) {
10018
- await this._limit.acquire();
10019
- }
10020
- }
10021
- this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
10022
- if (this._client) {
10023
- await this._client.close();
10024
- this._client = null;
10025
- }
10026
- this._baseUrl = null;
10027
- this._pipelineId = null;
10028
- }
10029
10182
  async authorizationHeader() {
10030
10183
  return `Bearer ${await this.currentAccessToken()}`;
10031
10184
  }
@@ -10033,41 +10186,49 @@ var Endpoint = class {
10033
10186
  const now = Date.now() / 1e3;
10034
10187
  if (!this._token || this._expire_token_time < now) {
10035
10188
  this.updateState("Authenticating" /* Authenticating */);
10036
- if (!this._client) {
10037
- return Promise.reject("endpoint not connected");
10038
- } else if (!this._options.auth || !this._options.auth.secretKey) {
10039
- return Promise.reject("temporary access token expired");
10040
- }
10041
- const body = { "secret_key": this._options.auth.secretKey };
10042
- const headers = { "Content-Type": "application/json" };
10043
- const post_url = `${this.eyepopUrl()}/authentication/token`;
10044
- this._requestLogger.debug("before POST %s", post_url);
10045
- const response = await this._client.fetch(post_url, {
10046
- method: "POST",
10047
- headers,
10048
- body: JSON.stringify(body)
10049
- });
10050
- if (response.status != 200) {
10051
- const message = await response.text();
10052
- return Promise.reject(`Unexpected status ${response.status}: ${message}`);
10189
+ try {
10190
+ if (!this._client) {
10191
+ return Promise.reject("endpoint not connected");
10192
+ } else if (this._options.auth.session !== void 0) {
10193
+ return Promise.reject("temporary access token expired");
10194
+ } else if (this._options.auth.oAuth2 !== void 0 && this._options.eyepopUrl && this._options.popId) {
10195
+ const session = await authenticateBrowserSession(
10196
+ this._options.auth.oAuth2,
10197
+ this._options.eyepopUrl,
10198
+ this._options.popId
10199
+ );
10200
+ this._token = session.accessToken;
10201
+ this._expire_token_time = session.validUntil / 1e3;
10202
+ } else if (this._options.auth.secretKey !== void 0 && this._options.eyepopUrl) {
10203
+ const secretKeyAuth = this._options.auth;
10204
+ const body = { "secret_key": secretKeyAuth.secretKey };
10205
+ const headers = { "Content-Type": "application/json" };
10206
+ const post_url = `${this.eyepopUrl()}/authentication/token`;
10207
+ this._requestLogger.debug("before POST %s", post_url);
10208
+ const response = await this._client.fetch(post_url, {
10209
+ method: "POST",
10210
+ headers,
10211
+ body: JSON.stringify(body)
10212
+ });
10213
+ if (response.status != 200) {
10214
+ const message = await response.text();
10215
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
10216
+ }
10217
+ this._requestLogger.debug("after POST %s", post_url);
10218
+ const data = await response.json();
10219
+ const token = data;
10220
+ this._token = token.access_token;
10221
+ this._expire_token_time = now + token.expires_in - 60;
10222
+ } else {
10223
+ return Promise.reject("no valid auth option");
10224
+ }
10225
+ } finally {
10226
+ this.updateState();
10053
10227
  }
10054
- this._requestLogger.debug("after POST %s", post_url);
10055
- const data = await response.json();
10056
- const token = data;
10057
- this._token = token.access_token;
10058
- this._expire_token_time = now + token.expires_in - 60;
10059
- this.updateState();
10060
10228
  }
10061
10229
  this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - now);
10062
10230
  return this._token;
10063
10231
  }
10064
- eyepopUrl() {
10065
- if (this._options.eyepopUrl) {
10066
- return this._options.eyepopUrl.replace(/\/+$/, "");
10067
- } else {
10068
- return "https://api.eyepop.ai";
10069
- }
10070
- }
10071
10232
  };
10072
10233
 
10073
10234
  // src/visualize.ts
@@ -10183,40 +10344,6 @@ _EyePopPlot.colors = {
10183
10344
  };
10184
10345
  var EyePopPlot = _EyePopPlot;
10185
10346
 
10186
- // src/shims/browser_session.ts
10187
- var authenticateBrowserSession;
10188
- if ("document" in globalThis && "implementation" in globalThis.document) {
10189
- authenticateBrowserSession = (oauthUrl, eyepopUrl, popId) => {
10190
- const queryString = window.location.search;
10191
- const urlParams = new URLSearchParams(queryString);
10192
- let token = urlParams.get("pop_token");
10193
- if (token) {
10194
- urlParams.delete("pop_token");
10195
- const url = `${window.location.pathname}?${urlParams}`;
10196
- window.history.replaceState({}, document.title, url);
10197
- return {
10198
- eyepopUrl,
10199
- popId,
10200
- accessToken: token,
10201
- validUntil: Date.now() + 60 * 60 * 1e3
10202
- };
10203
- } else {
10204
- const url = `${oauthUrl}?url=${encodeURIComponent(window.location.href)}`;
10205
- window.location.replace(url);
10206
- return {
10207
- eyepopUrl,
10208
- popId,
10209
- accessToken: "in-progress",
10210
- validUntil: 0
10211
- };
10212
- }
10213
- };
10214
- } else {
10215
- authenticateBrowserSession = (oauthUrl, eyepopUrl, popId) => {
10216
- return null;
10217
- };
10218
- }
10219
-
10220
10347
  // src/index.ts
10221
10348
  var readEnv = (env) => {
10222
10349
  if (typeof process !== "undefined") {
@@ -10224,7 +10351,7 @@ var readEnv = (env) => {
10224
10351
  }
10225
10352
  return void 0;
10226
10353
  };
10227
- var EyePopSdk = class {
10354
+ var _EyePopSdk = class _EyePopSdk {
10228
10355
  static endpoint({
10229
10356
  auth = this.defaultAuth,
10230
10357
  popId = readEnv("EYEPOP_POP_ID"),
@@ -10235,17 +10362,30 @@ var EyePopSdk = class {
10235
10362
  logger,
10236
10363
  ...opts
10237
10364
  } = {}) {
10238
- if (auth.oAuth && popId) {
10239
- let oauthUrl = "https://dashboard.eyepop.ai/sdkauth";
10240
- if (typeof auth.oAuth == "string") {
10241
- oauthUrl = auth.oAuth;
10365
+ if (auth.oAuth2 !== void 0 && popId) {
10366
+ if (typeof auth.oAuth2 === "boolean") {
10367
+ auth = {
10368
+ oAuth2: {
10369
+ domain: "eyepop.us.auth0.com",
10370
+ clientId: "Lb9ubA9Hf3jlaqWLUx8XgA0zvotgViCl",
10371
+ audience: "https://api.eyepop.ai",
10372
+ scope: "admin:clouds"
10373
+ }
10374
+ };
10375
+ }
10376
+ }
10377
+ if (auth.session !== void 0) {
10378
+ if (auth.session.popId) {
10379
+ popId = auth.session.popId;
10380
+ }
10381
+ if (auth.session.eyepopUrl) {
10382
+ eyepopUrl = auth.session.eyepopUrl;
10242
10383
  }
10243
- auth.session = authenticateBrowserSession(oauthUrl, eyepopUrl ?? "https://api.eyepop.ai", popId) ?? void 0;
10244
10384
  }
10245
10385
  const options = {
10246
10386
  auth,
10247
- popId: auth.session ? auth.session.popId : popId,
10248
- eyepopUrl: auth.session ? auth.session.eyepopUrl : eyepopUrl ?? "https://api.eyepop.ai",
10387
+ popId,
10388
+ eyepopUrl: eyepopUrl ?? "https://api.eyepop.ai",
10249
10389
  autoStart,
10250
10390
  stopJobs,
10251
10391
  jobQueueLength,
@@ -10258,9 +10398,11 @@ var EyePopSdk = class {
10258
10398
  return new EyePopPlot(context);
10259
10399
  }
10260
10400
  };
10261
- EyePopSdk.defaultAuth = {
10262
- secretKey: readEnv("EYEPOP_SECRET_KEY")
10263
- };
10401
+ _EyePopSdk.envSecretKey = readEnv("EYEPOP_SECRET_KEY");
10402
+ _EyePopSdk.defaultAuth = _EyePopSdk.envSecretKey ? {
10403
+ secretKey: _EyePopSdk.envSecretKey
10404
+ } : void 0;
10405
+ var EyePopSdk = _EyePopSdk;
10264
10406
  var src_default = EyePopSdk;
10265
10407
  // Annotate the CommonJS export names for ESM import in node:
10266
10408
  0 && (module.exports = {