@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.mjs CHANGED
@@ -9054,7 +9054,7 @@ var LoadLiveIngressJob = class extends AbstractJob {
9054
9054
  "Accept": "application/jsonl",
9055
9055
  "Content-Type": "application/json"
9056
9056
  };
9057
- const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
9057
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=preempt&processing=sync`;
9058
9058
  this._requestLogger.debug("before PATCH %s with url %s as source", patchUrl, this._ingressId);
9059
9059
  const response = await this._client.fetch(patchUrl, {
9060
9060
  headers,
@@ -9283,17 +9283,91 @@ function v4(options, buf, offset) {
9283
9283
  }
9284
9284
  var v4_default = v4;
9285
9285
 
9286
- // src/web_rtc_base.ts
9287
- var WebRtcBase = class {
9288
- constructor(getSession, client, requestLogger) {
9289
- this._pc = null;
9286
+ // src/webrtc_base.ts
9287
+ var WebrtcBase = class _WebrtcBase {
9288
+ constructor(getSession, client, ingressId, urlBasePath, requestLogger) {
9290
9289
  this._getSession = getSession;
9291
9290
  this._client = client;
9292
9291
  this._requestLogger = requestLogger;
9292
+ this._ingressId = ingressId;
9293
+ this._urlPath = `${urlBasePath}/${this._ingressId}`;
9294
+ this._pc = null;
9293
9295
  this._eTag = "";
9294
9296
  this._queuedCandidates = [];
9295
9297
  this._offerData = null;
9296
9298
  }
9299
+ async close() {
9300
+ const pc = this._pc;
9301
+ this._pc = null;
9302
+ if (pc) {
9303
+ return pc.close();
9304
+ }
9305
+ }
9306
+ ingressId() {
9307
+ return this._ingressId;
9308
+ }
9309
+ async onLocalOffer(offer) {
9310
+ if (!this._pc) {
9311
+ throw new Error("onLocalOffer no peer connection");
9312
+ }
9313
+ if (!offer.sdp) {
9314
+ throw new Error("onLocalOffer no sdp");
9315
+ }
9316
+ this._offerData = _WebrtcBase.parseOffer(offer.sdp);
9317
+ await this._pc.setLocalDescription(offer);
9318
+ const session = await this._getSession();
9319
+ const ingressUrl = new URL(this._urlPath, session.baseUrl);
9320
+ this._requestLogger.debug("before POST: %s", ingressUrl);
9321
+ const headers = {
9322
+ "Authorization": `Bearer ${session.accessToken}`,
9323
+ "Content-Type": "application/sdp"
9324
+ };
9325
+ const response = await this._client.fetch(ingressUrl, {
9326
+ headers,
9327
+ method: "POST",
9328
+ body: offer.sdp
9329
+ });
9330
+ if (response.status != 201) {
9331
+ return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
9332
+ }
9333
+ this._eTag = response.headers.get("ETag") ?? "";
9334
+ return this.onRemoteAnswer(new RTCSessionDescription({
9335
+ type: "answer",
9336
+ sdp: await response.text()
9337
+ }));
9338
+ }
9339
+ async sendLocalCandidates(candidates) {
9340
+ if (!this._offerData) {
9341
+ throw new Error("sendLocalCandidates no offerData");
9342
+ }
9343
+ const session = await this._getSession();
9344
+ const ingressUrl = new URL(this._urlPath, session.baseUrl);
9345
+ this._requestLogger.debug("before PATCH: %s", ingressUrl);
9346
+ const headers = {
9347
+ "Authorization": `Bearer ${session.accessToken}`,
9348
+ "Content-Type": "application/trickle-ice-sdpfrag",
9349
+ "If-Match": this._eTag
9350
+ };
9351
+ const response = await this._client.fetch(ingressUrl, {
9352
+ headers,
9353
+ method: "PATCH",
9354
+ body: _WebrtcBase.generateSdpFragment(this._offerData, candidates)
9355
+ });
9356
+ if (response.status != 204) {
9357
+ return Promise.reject(`unknown status code for PATCH '${ingressUrl}': ${response.status} (${response.statusText})`);
9358
+ }
9359
+ return this;
9360
+ }
9361
+ async onLocalCandidate(evt) {
9362
+ if (evt.candidate !== null) {
9363
+ if (this._eTag === "") {
9364
+ this._queuedCandidates.push(evt.candidate);
9365
+ } else {
9366
+ await this.sendLocalCandidates([evt.candidate]);
9367
+ }
9368
+ }
9369
+ return this;
9370
+ }
9297
9371
  static linkToIceServers(links) {
9298
9372
  const servers = links !== null ? links.split(", ").map((link) => {
9299
9373
  const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
@@ -9302,8 +9376,8 @@ var WebRtcBase = class {
9302
9376
  }
9303
9377
  const iceServer = {
9304
9378
  urls: [m[1]],
9305
- username: Whip.unquoteCredential(m[3]),
9306
- credential: Whip.unquoteCredential(m[4])
9379
+ username: WebrtcWhip.unquoteCredential(m[3]),
9380
+ credential: WebrtcWhip.unquoteCredential(m[4])
9307
9381
  // credentialType: m[3]? "password" : undefined
9308
9382
  };
9309
9383
  return iceServer;
@@ -9317,9 +9391,9 @@ var WebRtcBase = class {
9317
9391
  for (let i = 0; i < sections.length; i++) {
9318
9392
  const section = sections[i];
9319
9393
  if (section.startsWith("video")) {
9320
- sections[i] = Whip.setVideoBitrate(Whip.setCodec(section, videoCodec), videoBitrate);
9394
+ sections[i] = WebrtcWhip.setVideoBitrate(WebrtcWhip.setCodec(section, videoCodec), videoBitrate);
9321
9395
  } else if (section.startsWith("audio")) {
9322
- sections[i] = Whip.setAudioBitrate(Whip.setCodec(section, audioCodec), audioBitrate, audioVoice);
9396
+ sections[i] = WebrtcWhip.setAudioBitrate(WebrtcWhip.setCodec(section, audioCodec), audioBitrate, audioVoice);
9323
9397
  }
9324
9398
  }
9325
9399
  return sections.join("m=");
@@ -9441,22 +9515,15 @@ var WebRtcBase = class {
9441
9515
  }
9442
9516
  };
9443
9517
 
9444
- // src/whip.ts
9445
- var Whip = class _Whip extends WebRtcBase {
9518
+ // src/webrtc_whip.ts
9519
+ var WebrtcWhip = class _WebrtcWhip extends WebrtcBase {
9446
9520
  constructor(stream, getSession, client, requestLogger) {
9447
- super(getSession, client, requestLogger);
9448
- this._ingressId = v4_default().toString();
9521
+ super(getSession, client, v4_default().toString(), "/liveIngress/whip", requestLogger);
9449
9522
  this._stream = stream;
9450
9523
  }
9451
- ingressId() {
9452
- if (!this._ingressId) {
9453
- throw new Error("not initialized");
9454
- }
9455
- return this._ingressId;
9456
- }
9457
9524
  async start() {
9458
9525
  const session = await this._getSession();
9459
- const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9526
+ const ingressUrl = new URL(this._urlPath, session.baseUrl);
9460
9527
  this._requestLogger.debug("before GET: %s", ingressUrl);
9461
9528
  const headers = {
9462
9529
  "Authorization": `Bearer ${session.accessToken}`
@@ -9470,13 +9537,42 @@ var Whip = class _Whip extends WebRtcBase {
9470
9537
  }
9471
9538
  return this.onIceServers(response);
9472
9539
  }
9540
+ async stream() {
9541
+ if (this._stream) {
9542
+ return Promise.resolve(this._stream);
9543
+ } else {
9544
+ return Promise.reject("fetching stream failed");
9545
+ }
9546
+ }
9547
+ async onRemoteAnswer(answer) {
9548
+ if (!this._pc) {
9549
+ throw new Error("onLocalOffer no peer connection");
9550
+ }
9551
+ answer = new RTCSessionDescription({
9552
+ type: "answer",
9553
+ sdp: _WebrtcWhip.editAnswer(
9554
+ answer.sdp,
9555
+ "h264/90000",
9556
+ void 0,
9557
+ 5e3,
9558
+ void 0,
9559
+ true
9560
+ )
9561
+ });
9562
+ await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
9563
+ if (this._queuedCandidates.length !== 0) {
9564
+ await this.sendLocalCandidates(this._queuedCandidates);
9565
+ this._queuedCandidates = [];
9566
+ }
9567
+ return this;
9568
+ }
9473
9569
  async onIceServers(response) {
9474
9570
  if (!this._stream) {
9475
9571
  throw new Error("onIceServers no stream");
9476
9572
  }
9477
9573
  const stream = this._stream;
9478
9574
  const pc = new RTCPeerConnection({
9479
- iceServers: _Whip.linkToIceServers(response.headers.get("Link"))
9575
+ iceServers: WebrtcBase.linkToIceServers(response.headers.get("Link"))
9480
9576
  });
9481
9577
  this._pc = pc;
9482
9578
  pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
@@ -9503,61 +9599,63 @@ var Whip = class _Whip extends WebRtcBase {
9503
9599
  await this.onLocalOffer(await pc.createOffer());
9504
9600
  });
9505
9601
  }
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
- }
9602
+ async close() {
9603
+ const stream = this._stream;
9604
+ this._stream = null;
9605
+ if (stream) {
9606
+ const tracks = stream.getTracks();
9607
+ tracks.forEach((track) => {
9608
+ track.stop();
9609
+ });
9513
9610
  }
9514
- return this;
9611
+ return super.close();
9515
9612
  }
9516
- async onLocalOffer(offer) {
9517
- if (!this._pc) {
9518
- throw new Error("onLocalOffer no peer connection");
9613
+ };
9614
+
9615
+ // src/webrtc_whep.ts
9616
+ var WebrtcWhep = class extends WebrtcBase {
9617
+ constructor(ingressId, getSession, client, requestLogger) {
9618
+ super(getSession, client, ingressId, "/liveIngress/whep", requestLogger);
9619
+ this._stream = null;
9620
+ }
9621
+ async close() {
9622
+ const stream = this._stream;
9623
+ this._stream = null;
9624
+ if (stream) {
9625
+ const tracks = stream.getTracks();
9626
+ tracks.forEach((track) => {
9627
+ track.stop();
9628
+ });
9519
9629
  }
9520
- if (!offer.sdp) {
9521
- throw new Error("onLocalOffer no sdp");
9630
+ return super.close();
9631
+ }
9632
+ async stream() {
9633
+ if (this._stream) {
9634
+ return Promise.resolve(this._stream);
9635
+ } else {
9636
+ return Promise.reject("fetching stream failed");
9522
9637
  }
9523
- this._offerData = _Whip.parseOffer(offer.sdp);
9524
- await this._pc.setLocalDescription(offer);
9638
+ }
9639
+ async start() {
9525
9640
  const session = await this._getSession();
9526
- const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
9527
- this._requestLogger.debug("before GET: %s", ingressUrl);
9641
+ const egressUrl = new URL(this._urlPath, session.baseUrl);
9642
+ this._requestLogger.debug("before GET: %s", egressUrl);
9528
9643
  const headers = {
9529
- "Authorization": `Bearer ${session.accessToken}`,
9530
- "Content-Type": "application/sdp"
9644
+ "Authorization": `Bearer ${session.accessToken}`
9531
9645
  };
9532
- const response = await this._client.fetch(ingressUrl, {
9646
+ const response = await this._client.fetch(egressUrl, {
9533
9647
  headers,
9534
- method: "POST",
9535
- body: offer.sdp
9648
+ method: "GET"
9536
9649
  });
9537
- if (response.status != 201) {
9538
- return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
9650
+ if (response.status >= 300) {
9651
+ return Promise.reject(`unknown status code for GET '${egressUrl}': ${response.status} (${response.statusText})`);
9539
9652
  }
9540
- this._eTag = response.headers.get("ETag") ?? "";
9541
- return this.onRemoteAnswer(new RTCSessionDescription({
9542
- type: "answer",
9543
- sdp: await response.text()
9544
- }));
9653
+ return this.onIceServers(response);
9545
9654
  }
9546
9655
  async onRemoteAnswer(answer) {
9547
9656
  if (!this._pc) {
9548
9657
  throw new Error("onLocalOffer no peer connection");
9549
9658
  }
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
9659
  await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
9562
9660
  if (this._queuedCandidates.length !== 0) {
9563
9661
  await this.sendLocalCandidates(this._queuedCandidates);
@@ -9565,42 +9663,38 @@ var Whip = class _Whip extends WebRtcBase {
9565
9663
  }
9566
9664
  return this;
9567
9665
  }
9568
- async sendLocalCandidates(candidates) {
9569
- if (!this._offerData) {
9570
- throw new Error("sendLocalCandidates no offerData");
9666
+ async onIceServers(response) {
9667
+ if (this._stream) {
9668
+ throw new Error("onIceServers has stream");
9571
9669
  }
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)
9670
+ const pc = new RTCPeerConnection({
9671
+ iceServers: WebrtcBase.linkToIceServers(response.headers.get("Link"))
9672
+ });
9673
+ const direction = "sendrecv";
9674
+ pc.addTransceiver("video", { direction });
9675
+ pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
9676
+ this._pc = pc;
9677
+ this._requestLogger.info("before", pc.iceConnectionState);
9678
+ return new Promise(async (resolve, reject) => {
9679
+ pc.ontrack = (evt) => {
9680
+ this._requestLogger.debug("new track:", evt.track.kind);
9681
+ this._stream = evt.streams[0];
9682
+ resolve(this);
9683
+ };
9684
+ pc.oniceconnectionstatechange = () => {
9685
+ switch (pc.iceConnectionState) {
9686
+ case "failed":
9687
+ case "disconnected":
9688
+ this._requestLogger.debug("disconnected");
9689
+ reject(`peer connection: ${pc.iceConnectionState}`);
9690
+ break;
9691
+ case "connected":
9692
+ this._requestLogger.debug("connected");
9693
+ break;
9694
+ }
9695
+ };
9696
+ await this.onLocalOffer(await pc.createOffer());
9584
9697
  });
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
9698
  }
9605
9699
  };
9606
9700
 
@@ -9626,6 +9720,43 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9626
9720
 
9627
9721
  // src/endpoint.ts
9628
9722
  import { pino } from "pino";
9723
+
9724
+ // src/shims/browser_session.ts
9725
+ import { createAuth0Client } from "@auth0/auth0-spa-js";
9726
+ var authenticateBrowserSession;
9727
+ if ("document" in globalThis && "implementation" in globalThis.document) {
9728
+ authenticateBrowserSession = async (auth0, eyepopUrl, popId) => {
9729
+ const options = {
9730
+ clientId: auth0.clientId,
9731
+ domain: auth0.domain,
9732
+ authorizationParams: {
9733
+ scope: auth0.scope,
9734
+ audience: auth0.audience
9735
+ }
9736
+ };
9737
+ const auth0Client = await createAuth0Client(options);
9738
+ if (!await auth0Client.isAuthenticated()) {
9739
+ await auth0Client.loginWithPopup();
9740
+ }
9741
+ const accessToken = await auth0Client.getTokenSilently();
9742
+ if (!accessToken) {
9743
+ return Promise.reject("auth0 login failed");
9744
+ }
9745
+ const session = {
9746
+ eyepopUrl,
9747
+ popId,
9748
+ accessToken,
9749
+ validUntil: Date.now() + 60 * 60 * 1e3
9750
+ };
9751
+ return session;
9752
+ };
9753
+ } else {
9754
+ authenticateBrowserSession = (auth0, eyepopUrl, popId) => {
9755
+ return Promise.reject("auth0 login not supported server-side");
9756
+ };
9757
+ }
9758
+
9759
+ // src/endpoint.ts
9629
9760
  var Endpoint = class {
9630
9761
  constructor(options) {
9631
9762
  this._options = options;
@@ -9649,55 +9780,13 @@ var Endpoint = class {
9649
9780
  this._logger = rootLogger.child({ module: "eyepop" });
9650
9781
  this._requestLogger = this._logger.child({ module: "requests" });
9651
9782
  }
9652
- onStateChanged(handler) {
9653
- this._stateChangeHandler = handler;
9654
- return this;
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();
9783
+ eyepopUrl() {
9784
+ if (this._options.eyepopUrl) {
9785
+ return this._options.eyepopUrl.replace(/\/+$/, "");
9786
+ } else {
9787
+ return "https://api.eyepop.ai";
9691
9788
  }
9692
9789
  }
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
- }
9701
9790
  state() {
9702
9791
  return this._state;
9703
9792
  }
@@ -9710,6 +9799,80 @@ var Endpoint = class {
9710
9799
  popId() {
9711
9800
  return this._options.popId || null;
9712
9801
  }
9802
+ onStateChanged(handler) {
9803
+ this._stateChangeHandler = handler;
9804
+ return this;
9805
+ }
9806
+ onIngressEvent(handler) {
9807
+ this._ingressEventHandler = handler;
9808
+ this.startIngressWs().then(() => {
9809
+ }).catch((reason) => {
9810
+ this._logger.warn("unexpected error starting the ingress event handler: %s", reason);
9811
+ });
9812
+ return this;
9813
+ }
9814
+ async connect() {
9815
+ if (this._client) {
9816
+ this._logger.warn("endpoint already connected");
9817
+ return this;
9818
+ }
9819
+ if (!this._options.eyepopUrl) {
9820
+ return Promise.reject("option eyepopUrl or environment variable EYEPOP_URL is required");
9821
+ }
9822
+ if (!this._options.popId) {
9823
+ return Promise.reject("option popId or environment variable EYEPOP_POP_ID is required");
9824
+ }
9825
+ if (this._options.auth === void 0) {
9826
+ return Promise.reject("cannot connect without defined auth option");
9827
+ }
9828
+ if (this._options.auth.session !== void 0) {
9829
+ this._token = this._options.auth.session.accessToken;
9830
+ this._expire_token_time = this._options.auth.session.validUntil / 1e3;
9831
+ } else if (this._options.auth.oAuth2 !== void 0) {
9832
+ } else if (this._options.auth.secretKey !== void 0) {
9833
+ } else {
9834
+ return Promise.reject("option secretKey or environment variable EYEPOP_SECRET_KEY is required");
9835
+ }
9836
+ this._client = await createHttpClient();
9837
+ let result = await this.reconnect();
9838
+ if (this._baseUrl && this._options.stopJobs) {
9839
+ const stop_url = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=preempt&processing=sync`;
9840
+ const body = { "sourceType": "NONE" };
9841
+ const headers = {
9842
+ "Authorization": await this.authorizationHeader(),
9843
+ "Content-Type": "application/json"
9844
+ };
9845
+ this._requestLogger.debug("before PATCH %s", stop_url);
9846
+ let response = await this._client.fetch(stop_url, {
9847
+ method: "PATCH",
9848
+ headers,
9849
+ body: JSON.stringify(body)
9850
+ });
9851
+ if (response.status >= 300) {
9852
+ const message = await response.text();
9853
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9854
+ }
9855
+ this._requestLogger.debug("after PATCH %s", stop_url);
9856
+ }
9857
+ if (!this._ingressEventWs && this._ingressEventHandler) {
9858
+ await this.startIngressWs();
9859
+ }
9860
+ return result;
9861
+ }
9862
+ async disconnect(wait = true) {
9863
+ if (wait && this._limit) {
9864
+ for (let j = 0; j < this._options.jobQueueLength; j++) {
9865
+ await this._limit.acquire();
9866
+ }
9867
+ }
9868
+ this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9869
+ if (this._client) {
9870
+ await this._client.close();
9871
+ this._client = null;
9872
+ }
9873
+ this._baseUrl = null;
9874
+ this._pipelineId = null;
9875
+ }
9713
9876
  async session() {
9714
9877
  await this.currentAccessToken();
9715
9878
  if (this._token == null || this._expire_token_time == null) {
@@ -9734,11 +9897,20 @@ var Endpoint = class {
9734
9897
  if (!this._baseUrl || !this._client) {
9735
9898
  return Promise.reject("endpoint not connected, use connect() before ingress()");
9736
9899
  }
9737
- const whip = new Whip(stream, async () => {
9900
+ const whip = new WebrtcWhip(stream, async () => {
9738
9901
  return this.session();
9739
9902
  }, this._client, this._requestLogger);
9740
9903
  return whip.start();
9741
9904
  }
9905
+ async liveEgress(ingressId) {
9906
+ if (!this._baseUrl || !this._client) {
9907
+ return Promise.reject("endpoint not connected, use connect() before ingress()");
9908
+ }
9909
+ const whep = new WebrtcWhep(ingressId, async () => {
9910
+ return this.session();
9911
+ }, this._client, this._requestLogger);
9912
+ return whep.start();
9913
+ }
9742
9914
  async process(source) {
9743
9915
  if (source.file !== void 0) {
9744
9916
  return this.uploadFile(source);
@@ -9754,6 +9926,43 @@ var Endpoint = class {
9754
9926
  return Promise.reject("unknown source type");
9755
9927
  }
9756
9928
  }
9929
+ async startIngressWs() {
9930
+ if (this._ingressEventWs || !this._baseUrl || !this._client) {
9931
+ return;
9932
+ }
9933
+ try {
9934
+ const getTokenUrl = `${this._baseUrl}/liveIngress/events/token`;
9935
+ const headers = {
9936
+ "Authorization": await this.authorizationHeader()
9937
+ };
9938
+ this._requestLogger.debug("before GET %s", getTokenUrl);
9939
+ let response = await this._client.fetch(getTokenUrl, { headers });
9940
+ if (response.status == 401) {
9941
+ this._requestLogger.debug("after GET %s: 401, about to retry with fresh access token", getTokenUrl);
9942
+ this._token = null;
9943
+ const headers2 = {
9944
+ "Authorization": await this.authorizationHeader()
9945
+ };
9946
+ this.updateState("FetchConfig" /* FetchConfig */);
9947
+ response = await this._client.fetch(getTokenUrl, { headers: headers2 });
9948
+ }
9949
+ if (response.status != 200) {
9950
+ const message = await response.text();
9951
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9952
+ }
9953
+ const wsAuthToken = await response.json();
9954
+ const wsUrl = this._baseUrl.startsWith("https://") ? `${this._baseUrl.replace("https://", "wss://")}/liveIngress/events/${wsAuthToken.token}` : `${this._baseUrl.replace("http://", "ws://")}/liveIngress/events/${wsAuthToken.token}`;
9955
+ this._ingressEventWs = new WebSocket(wsUrl);
9956
+ this._ingressEventWs.addEventListener("message", (event) => {
9957
+ const ingressEvent = JSON.parse(event.data);
9958
+ if (this._ingressEventHandler) {
9959
+ this._ingressEventHandler(ingressEvent);
9960
+ }
9961
+ });
9962
+ } finally {
9963
+ this.updateState();
9964
+ }
9965
+ }
9757
9966
  async uploadFile(source) {
9758
9967
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
9759
9968
  return Promise.reject("endpoint not connected, use connect() before upload()");
@@ -9855,48 +10064,6 @@ var Endpoint = class {
9855
10064
  throw e;
9856
10065
  }
9857
10066
  }
9858
- // noinspection TypeScriptValidateTypes
9859
- async connect() {
9860
- if (this._client) {
9861
- console.info("endpoint already connected");
9862
- return this;
9863
- }
9864
- if (!this._options.eyepopUrl) {
9865
- return Promise.reject("option eyepopUrl or environment variable EYEPOP_URL is required");
9866
- }
9867
- if (!this._options.popId) {
9868
- return Promise.reject("option popId or environment variable EYEPOP_POP_ID is required");
9869
- }
9870
- if (this._options.auth && this._options.auth.session) {
9871
- this._token = this._options.auth.session.accessToken;
9872
- this._expire_token_time = this._options.auth.session.validUntil / 1e3;
9873
- } else {
9874
- if (!this._options.auth || !this._options.auth.secretKey) {
9875
- return Promise.reject("option secretKey or environment variable EYEPOP_SECRET_KEY is required");
9876
- }
9877
- }
9878
- this._client = await createHttpClient();
9879
- let result = await this.reconnect();
9880
- if (this._baseUrl && this._options.stopJobs) {
9881
- const stop_url = `${this._baseUrl}/pipelines/${this._pipelineId}/source?mode=preempt&processing=sync`;
9882
- const body = { "sourceType": "NONE" };
9883
- const headers = {
9884
- "Authorization": await this.authorizationHeader(),
9885
- "Content-Type": "application/json"
9886
- };
9887
- this._requestLogger.debug("before PATCH %s", stop_url);
9888
- let response = await this._client.fetch(stop_url, { method: "PATCH", headers, body: JSON.stringify(body) });
9889
- if (response.status >= 300) {
9890
- const message = await response.text();
9891
- return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9892
- }
9893
- this._requestLogger.debug("after PATCH %s", stop_url);
9894
- }
9895
- if (!this._ingressEventWs && this._ingressEventHandler) {
9896
- await this.startIngressWs();
9897
- }
9898
- return result;
9899
- }
9900
10067
  jobDone(job) {
9901
10068
  this._limit?.release();
9902
10069
  this.updateState();
@@ -9983,20 +10150,6 @@ var Endpoint = class {
9983
10150
  return Promise.resolve(this);
9984
10151
  }
9985
10152
  }
9986
- async disconnect(wait = true) {
9987
- if (wait && this._limit) {
9988
- for (let j = 0; j < this._options.jobQueueLength; j++) {
9989
- await this._limit.acquire();
9990
- }
9991
- }
9992
- this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9993
- if (this._client) {
9994
- await this._client.close();
9995
- this._client = null;
9996
- }
9997
- this._baseUrl = null;
9998
- this._pipelineId = null;
9999
- }
10000
10153
  async authorizationHeader() {
10001
10154
  return `Bearer ${await this.currentAccessToken()}`;
10002
10155
  }
@@ -10004,41 +10157,49 @@ var Endpoint = class {
10004
10157
  const now = Date.now() / 1e3;
10005
10158
  if (!this._token || this._expire_token_time < now) {
10006
10159
  this.updateState("Authenticating" /* Authenticating */);
10007
- if (!this._client) {
10008
- return Promise.reject("endpoint not connected");
10009
- } else if (!this._options.auth || !this._options.auth.secretKey) {
10010
- return Promise.reject("temporary access token expired");
10011
- }
10012
- const body = { "secret_key": this._options.auth.secretKey };
10013
- const headers = { "Content-Type": "application/json" };
10014
- const post_url = `${this.eyepopUrl()}/authentication/token`;
10015
- this._requestLogger.debug("before POST %s", post_url);
10016
- const response = await this._client.fetch(post_url, {
10017
- method: "POST",
10018
- headers,
10019
- body: JSON.stringify(body)
10020
- });
10021
- if (response.status != 200) {
10022
- const message = await response.text();
10023
- return Promise.reject(`Unexpected status ${response.status}: ${message}`);
10160
+ try {
10161
+ if (!this._client) {
10162
+ return Promise.reject("endpoint not connected");
10163
+ } else if (this._options.auth.session !== void 0) {
10164
+ return Promise.reject("temporary access token expired");
10165
+ } else if (this._options.auth.oAuth2 !== void 0 && this._options.eyepopUrl && this._options.popId) {
10166
+ const session = await authenticateBrowserSession(
10167
+ this._options.auth.oAuth2,
10168
+ this._options.eyepopUrl,
10169
+ this._options.popId
10170
+ );
10171
+ this._token = session.accessToken;
10172
+ this._expire_token_time = session.validUntil / 1e3;
10173
+ } else if (this._options.auth.secretKey !== void 0 && this._options.eyepopUrl) {
10174
+ const secretKeyAuth = this._options.auth;
10175
+ const body = { "secret_key": secretKeyAuth.secretKey };
10176
+ const headers = { "Content-Type": "application/json" };
10177
+ const post_url = `${this.eyepopUrl()}/authentication/token`;
10178
+ this._requestLogger.debug("before POST %s", post_url);
10179
+ const response = await this._client.fetch(post_url, {
10180
+ method: "POST",
10181
+ headers,
10182
+ body: JSON.stringify(body)
10183
+ });
10184
+ if (response.status != 200) {
10185
+ const message = await response.text();
10186
+ return Promise.reject(`Unexpected status ${response.status}: ${message}`);
10187
+ }
10188
+ this._requestLogger.debug("after POST %s", post_url);
10189
+ const data = await response.json();
10190
+ const token = data;
10191
+ this._token = token.access_token;
10192
+ this._expire_token_time = now + token.expires_in - 60;
10193
+ } else {
10194
+ return Promise.reject("no valid auth option");
10195
+ }
10196
+ } finally {
10197
+ this.updateState();
10024
10198
  }
10025
- this._requestLogger.debug("after POST %s", post_url);
10026
- const data = await response.json();
10027
- const token = data;
10028
- this._token = token.access_token;
10029
- this._expire_token_time = now + token.expires_in - 60;
10030
- this.updateState();
10031
10199
  }
10032
10200
  this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - now);
10033
10201
  return this._token;
10034
10202
  }
10035
- eyepopUrl() {
10036
- if (this._options.eyepopUrl) {
10037
- return this._options.eyepopUrl.replace(/\/+$/, "");
10038
- } else {
10039
- return "https://api.eyepop.ai";
10040
- }
10041
- }
10042
10203
  };
10043
10204
 
10044
10205
  // src/visualize.ts
@@ -10154,40 +10315,6 @@ _EyePopPlot.colors = {
10154
10315
  };
10155
10316
  var EyePopPlot = _EyePopPlot;
10156
10317
 
10157
- // src/shims/browser_session.ts
10158
- var authenticateBrowserSession;
10159
- if ("document" in globalThis && "implementation" in globalThis.document) {
10160
- authenticateBrowserSession = (oauthUrl, eyepopUrl, popId) => {
10161
- const queryString = window.location.search;
10162
- const urlParams = new URLSearchParams(queryString);
10163
- let token = urlParams.get("pop_token");
10164
- if (token) {
10165
- urlParams.delete("pop_token");
10166
- const url = `${window.location.pathname}?${urlParams}`;
10167
- window.history.replaceState({}, document.title, url);
10168
- return {
10169
- eyepopUrl,
10170
- popId,
10171
- accessToken: token,
10172
- validUntil: Date.now() + 60 * 60 * 1e3
10173
- };
10174
- } else {
10175
- const url = `${oauthUrl}?url=${encodeURIComponent(window.location.href)}`;
10176
- window.location.replace(url);
10177
- return {
10178
- eyepopUrl,
10179
- popId,
10180
- accessToken: "in-progress",
10181
- validUntil: 0
10182
- };
10183
- }
10184
- };
10185
- } else {
10186
- authenticateBrowserSession = (oauthUrl, eyepopUrl, popId) => {
10187
- return null;
10188
- };
10189
- }
10190
-
10191
10318
  // src/index.ts
10192
10319
  var readEnv = (env) => {
10193
10320
  if (typeof process !== "undefined") {
@@ -10195,7 +10322,7 @@ var readEnv = (env) => {
10195
10322
  }
10196
10323
  return void 0;
10197
10324
  };
10198
- var EyePopSdk = class {
10325
+ var _EyePopSdk = class _EyePopSdk {
10199
10326
  static endpoint({
10200
10327
  auth = this.defaultAuth,
10201
10328
  popId = readEnv("EYEPOP_POP_ID"),
@@ -10206,17 +10333,30 @@ var EyePopSdk = class {
10206
10333
  logger,
10207
10334
  ...opts
10208
10335
  } = {}) {
10209
- if (auth.oAuth && popId) {
10210
- let oauthUrl = "https://dashboard.eyepop.ai/sdkauth";
10211
- if (typeof auth.oAuth == "string") {
10212
- oauthUrl = auth.oAuth;
10336
+ if (auth.oAuth2 !== void 0 && popId) {
10337
+ if (typeof auth.oAuth2 === "boolean") {
10338
+ auth = {
10339
+ oAuth2: {
10340
+ domain: "eyepop.us.auth0.com",
10341
+ clientId: "Lb9ubA9Hf3jlaqWLUx8XgA0zvotgViCl",
10342
+ audience: "https://api.eyepop.ai",
10343
+ scope: "admin:clouds"
10344
+ }
10345
+ };
10346
+ }
10347
+ }
10348
+ if (auth.session !== void 0) {
10349
+ if (auth.session.popId) {
10350
+ popId = auth.session.popId;
10351
+ }
10352
+ if (auth.session.eyepopUrl) {
10353
+ eyepopUrl = auth.session.eyepopUrl;
10213
10354
  }
10214
- auth.session = authenticateBrowserSession(oauthUrl, eyepopUrl ?? "https://api.eyepop.ai", popId) ?? void 0;
10215
10355
  }
10216
10356
  const options = {
10217
10357
  auth,
10218
- popId: auth.session ? auth.session.popId : popId,
10219
- eyepopUrl: auth.session ? auth.session.eyepopUrl : eyepopUrl ?? "https://api.eyepop.ai",
10358
+ popId,
10359
+ eyepopUrl: eyepopUrl ?? "https://api.eyepop.ai",
10220
10360
  autoStart,
10221
10361
  stopJobs,
10222
10362
  jobQueueLength,
@@ -10229,9 +10369,11 @@ var EyePopSdk = class {
10229
10369
  return new EyePopPlot(context);
10230
10370
  }
10231
10371
  };
10232
- EyePopSdk.defaultAuth = {
10233
- secretKey: readEnv("EYEPOP_SECRET_KEY")
10234
- };
10372
+ _EyePopSdk.envSecretKey = readEnv("EYEPOP_SECRET_KEY");
10373
+ _EyePopSdk.defaultAuth = _EyePopSdk.envSecretKey ? {
10374
+ secretKey: _EyePopSdk.envSecretKey
10375
+ } : void 0;
10376
+ var EyePopSdk = _EyePopSdk;
10235
10377
  var src_default = EyePopSdk;
10236
10378
  export {
10237
10379
  EndpointState,