@eyepop.ai/eyepop 3.25.3 → 3.25.4
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/eyepop.index.d.mts +42 -7
- package/dist/eyepop.index.d.ts +42 -7
- package/dist/eyepop.index.js +107 -40
- package/dist/eyepop.index.js.map +1 -1
- package/dist/eyepop.index.mjs +106 -40
- package/dist/eyepop.index.mjs.map +1 -1
- package/dist/eyepop.min.js +1 -1
- package/package.json +1 -1
package/dist/eyepop.index.mjs
CHANGED
|
@@ -8935,7 +8935,7 @@ var Endpoint = class {
|
|
|
8935
8935
|
this._expire_token_time = null;
|
|
8936
8936
|
this._client = null;
|
|
8937
8937
|
this._statusRetryHandlers = /* @__PURE__ */ new Map();
|
|
8938
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
8938
|
+
this._statusRetryHandlers.set(401, (statusCode) => this.statusHandler401(statusCode));
|
|
8939
8939
|
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
8940
8940
|
this._state = "Idle" /* Idle */;
|
|
8941
8941
|
this._message = null;
|
|
@@ -8951,7 +8951,7 @@ var Endpoint = class {
|
|
|
8951
8951
|
}
|
|
8952
8952
|
setStatusRetryHandlers(statusRetryHandlers = void 0) {
|
|
8953
8953
|
this._statusRetryHandlers = new Map(statusRetryHandlers);
|
|
8954
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
8954
|
+
this._statusRetryHandlers.set(401, (statusCode) => this.statusHandler401(statusCode));
|
|
8955
8955
|
}
|
|
8956
8956
|
eyepopUrl() {
|
|
8957
8957
|
if (this._options.eyepopUrl) {
|
|
@@ -11497,6 +11497,8 @@ if (isBrowser) {
|
|
|
11497
11497
|
var PIPELINE_PATH = "/api/v1/pipeline";
|
|
11498
11498
|
var SUBPROTOCOL = "eyepop.v1";
|
|
11499
11499
|
var MAX_OPEN_SOURCES = 256;
|
|
11500
|
+
var PIPELINE_SOCKETS = 4;
|
|
11501
|
+
var EXTRA_SOCKET_RETRY_MS = 10 * 1e3;
|
|
11500
11502
|
var COMBINED_UPLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
11501
11503
|
var CHUNK = 1;
|
|
11502
11504
|
var COMBINED = 2;
|
|
@@ -11616,6 +11618,8 @@ var PipelineSocket = class {
|
|
|
11616
11618
|
// The ids of sources sent on this socket that have neither ended nor been refused, as the
|
|
11617
11619
|
// pipeline API counts them, cancelled ones included.
|
|
11618
11620
|
this.held = /* @__PURE__ */ new Set();
|
|
11621
|
+
// The held sources that are live. A live source never ends, so nothing queued behind it plays.
|
|
11622
|
+
this.lives = /* @__PURE__ */ new Set();
|
|
11619
11623
|
// The held sources the pipeline API took: only these end, so only these make room.
|
|
11620
11624
|
this.accepted = /* @__PURE__ */ new Set();
|
|
11621
11625
|
// The ends on this socket so far.
|
|
@@ -11624,6 +11628,7 @@ var PipelineSocket = class {
|
|
|
11624
11628
|
// the next send waits. Null once none is held.
|
|
11625
11629
|
this.cap = null;
|
|
11626
11630
|
this._roomWaiters = [];
|
|
11631
|
+
this.heartbeat = null;
|
|
11627
11632
|
this.closed = new Promise((resolve) => this._markClosed = resolve);
|
|
11628
11633
|
}
|
|
11629
11634
|
/** A source ended or the socket closed: wake the send waiting for room. */
|
|
@@ -11657,6 +11662,10 @@ var PipelineSocket = class {
|
|
|
11657
11662
|
return !this._closed && this.ws.readyState === 1;
|
|
11658
11663
|
}
|
|
11659
11664
|
markClosed() {
|
|
11665
|
+
if (this.heartbeat !== null) {
|
|
11666
|
+
clearInterval(this.heartbeat);
|
|
11667
|
+
this.heartbeat = null;
|
|
11668
|
+
}
|
|
11660
11669
|
this._closed = true;
|
|
11661
11670
|
this._markClosed();
|
|
11662
11671
|
this.freed();
|
|
@@ -11745,14 +11754,14 @@ var PipelineBackend = class {
|
|
|
11745
11754
|
constructor(ctx) {
|
|
11746
11755
|
this.ctx = ctx;
|
|
11747
11756
|
this._pipelineApiUrl = null;
|
|
11748
|
-
this.
|
|
11757
|
+
this._sockets = [];
|
|
11749
11758
|
this._connecting = null;
|
|
11750
11759
|
// Every job's setSource and every changePop goes out through this chain, in the order submitted.
|
|
11751
11760
|
this._sendChain = Promise.resolve();
|
|
11752
11761
|
this._disconnecting = false;
|
|
11753
11762
|
this._nextId = 0;
|
|
11754
11763
|
this._warned = /* @__PURE__ */ new Set();
|
|
11755
|
-
this.
|
|
11764
|
+
this._noExtraSocketUntil = 0;
|
|
11756
11765
|
// Reconnects at once since a source last played.
|
|
11757
11766
|
this._reconnects = 0;
|
|
11758
11767
|
this._currentPop = ctx.options.pop ?? null;
|
|
@@ -11766,13 +11775,9 @@ var PipelineBackend = class {
|
|
|
11766
11775
|
async disconnect(closeEndpoint) {
|
|
11767
11776
|
await closeEndpoint();
|
|
11768
11777
|
this._disconnecting = true;
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
}
|
|
11773
|
-
const socket = this._socket;
|
|
11774
|
-
this._socket = null;
|
|
11775
|
-
if (socket !== null) {
|
|
11778
|
+
const sockets = this._sockets;
|
|
11779
|
+
this._sockets = [];
|
|
11780
|
+
for (const socket of sockets) {
|
|
11776
11781
|
socket.ws.close(1e3);
|
|
11777
11782
|
await socket.closed;
|
|
11778
11783
|
}
|
|
@@ -11790,21 +11795,55 @@ var PipelineBackend = class {
|
|
|
11790
11795
|
this._sendChain = result.catch(() => void 0);
|
|
11791
11796
|
return result;
|
|
11792
11797
|
}
|
|
11793
|
-
|
|
11798
|
+
openSockets() {
|
|
11799
|
+
return this._sockets.filter((socket) => socket.open);
|
|
11800
|
+
}
|
|
11801
|
+
/** An open socket, or a new one with the last pop set on it. */
|
|
11794
11802
|
async ensureSocket() {
|
|
11795
|
-
|
|
11796
|
-
|
|
11803
|
+
const open = this.openSockets()[0];
|
|
11804
|
+
if (open !== void 0) {
|
|
11805
|
+
return open;
|
|
11797
11806
|
}
|
|
11798
11807
|
if (this._connecting === null) {
|
|
11799
|
-
this._connecting = this.openSocket().finally(() => this._connecting = null);
|
|
11808
|
+
this._connecting = this.openSocket(true).finally(() => this._connecting = null);
|
|
11800
11809
|
}
|
|
11801
11810
|
return await this._connecting;
|
|
11802
11811
|
}
|
|
11803
|
-
|
|
11812
|
+
/**
|
|
11813
|
+
* The socket for the next job; the caller runs it in submission order. A live job goes on a socket
|
|
11814
|
+
* of its own, an idle one or a new one, which `pipelineSockets` does not count. Any other job skips
|
|
11815
|
+
* the sockets with a live source: a new one while every other socket has a source and fewer than
|
|
11816
|
+
* `pipelineSockets` are open, else the one with room and the fewest sources.
|
|
11817
|
+
*/
|
|
11818
|
+
async socketForJob(job) {
|
|
11819
|
+
if (job.live) {
|
|
11820
|
+
return this.openSockets().find((socket) => socket.held.size === 0) ?? await this.openSocket(true);
|
|
11821
|
+
}
|
|
11822
|
+
const sockets = this.openSockets().filter((socket) => socket.lives.size === 0);
|
|
11823
|
+
if (sockets.length === 0) {
|
|
11824
|
+
return await this.openSocket(true);
|
|
11825
|
+
}
|
|
11826
|
+
if (sockets.length < (this.ctx.options.pipelineSockets ?? PIPELINE_SOCKETS) && sockets.every((socket) => socket.held.size > 0) && Date.now() >= this._noExtraSocketUntil) {
|
|
11827
|
+
try {
|
|
11828
|
+
return await this.openSocket(false);
|
|
11829
|
+
} catch (error) {
|
|
11830
|
+
this._noExtraSocketUntil = Date.now() + EXTRA_SOCKET_RETRY_MS;
|
|
11831
|
+
this.ctx.requestLogger.info("could not open another pipeline API socket: %s", error);
|
|
11832
|
+
}
|
|
11833
|
+
}
|
|
11834
|
+
const rank = (socket) => (socket.hasRoom ? 0 : MAX_OPEN_SOURCES + 1) + socket.held.size;
|
|
11835
|
+
return sockets.reduce((best, socket) => rank(socket) < rank(best) ? socket : best);
|
|
11836
|
+
}
|
|
11837
|
+
/** A new socket with the last pop set on it; `patient` waits through 503 with Retry-After. */
|
|
11838
|
+
async openSocket(patient) {
|
|
11804
11839
|
if (this.ctx.client() === null) {
|
|
11805
11840
|
throw new PipelineSocketClosedError(void 0, "the endpoint is not connected");
|
|
11806
11841
|
}
|
|
11807
|
-
const ws = await this.upgrade(await this.socketUrl());
|
|
11842
|
+
const ws = await this.upgrade(await this.socketUrl(), patient);
|
|
11843
|
+
if (this._disconnecting) {
|
|
11844
|
+
ws.close(1e3);
|
|
11845
|
+
throw new PipelineSocketClosedError(void 0, "the endpoint disconnected");
|
|
11846
|
+
}
|
|
11808
11847
|
const socket = new PipelineSocket(ws);
|
|
11809
11848
|
ws.onmessage = (event) => {
|
|
11810
11849
|
if (typeof event.data === "string") {
|
|
@@ -11818,31 +11857,29 @@ var PipelineBackend = class {
|
|
|
11818
11857
|
ws.onerror = () => {
|
|
11819
11858
|
};
|
|
11820
11859
|
ws.onclose = (event) => this.closed(socket, event.code, event.reason);
|
|
11821
|
-
this.
|
|
11822
|
-
this.startHeartbeat(
|
|
11860
|
+
this._sockets.push(socket);
|
|
11861
|
+
this.startHeartbeat(socket);
|
|
11823
11862
|
if (this._currentPop !== null) {
|
|
11824
11863
|
try {
|
|
11825
11864
|
await this.setPopOn(socket, this._currentPop);
|
|
11826
11865
|
} catch (error) {
|
|
11827
|
-
this.
|
|
11866
|
+
this._sockets = this._sockets.filter((other) => other !== socket);
|
|
11828
11867
|
ws.close(1e3);
|
|
11829
11868
|
throw error;
|
|
11830
11869
|
}
|
|
11831
11870
|
}
|
|
11832
11871
|
return socket;
|
|
11833
11872
|
}
|
|
11834
|
-
startHeartbeat(
|
|
11835
|
-
|
|
11836
|
-
clearInterval(this._heartbeat);
|
|
11837
|
-
}
|
|
11873
|
+
startHeartbeat(socket) {
|
|
11874
|
+
const ws = socket.ws;
|
|
11838
11875
|
const ping = ws.ping;
|
|
11839
11876
|
if (typeof ping === "function") {
|
|
11840
|
-
|
|
11877
|
+
socket.heartbeat = setInterval(() => {
|
|
11841
11878
|
if (ws.readyState === 1) {
|
|
11842
11879
|
ping.call(ws);
|
|
11843
11880
|
}
|
|
11844
11881
|
}, HEARTBEAT_MS);
|
|
11845
|
-
|
|
11882
|
+
socket.heartbeat.unref?.();
|
|
11846
11883
|
}
|
|
11847
11884
|
}
|
|
11848
11885
|
async socketUrl() {
|
|
@@ -11880,7 +11917,7 @@ var PipelineBackend = class {
|
|
|
11880
11917
|
return (await this.ctx.credentialHeader()).replace(/^Bearer /, "");
|
|
11881
11918
|
}
|
|
11882
11919
|
/** Open the socket, waiting through 503 with Retry-After and exchanging the API key again after a 401. */
|
|
11883
|
-
async upgrade(url) {
|
|
11920
|
+
async upgrade(url, patient) {
|
|
11884
11921
|
const deadline = Date.now() + MAX_RETRY_MS;
|
|
11885
11922
|
let rejected = false;
|
|
11886
11923
|
while (true) {
|
|
@@ -11898,7 +11935,7 @@ var PipelineBackend = class {
|
|
|
11898
11935
|
continue;
|
|
11899
11936
|
}
|
|
11900
11937
|
const wait = error.status === 503 && error.retryAfter !== void 0 ? Math.max(error.retryAfter, 1) * 1e3 : void 0;
|
|
11901
|
-
if (wait === void 0 || Date.now() + wait > deadline) {
|
|
11938
|
+
if (!patient || wait === void 0 || Date.now() + wait > deadline) {
|
|
11902
11939
|
throw error;
|
|
11903
11940
|
}
|
|
11904
11941
|
this.ctx.requestLogger.info("the pipeline API answered 503, retrying in %d ms", wait);
|
|
@@ -11908,9 +11945,7 @@ var PipelineBackend = class {
|
|
|
11908
11945
|
}
|
|
11909
11946
|
closed(socket, code, reason) {
|
|
11910
11947
|
socket.markClosed();
|
|
11911
|
-
|
|
11912
|
-
this._socket = null;
|
|
11913
|
-
}
|
|
11948
|
+
this._sockets = this._sockets.filter((other) => other !== socket);
|
|
11914
11949
|
const error = new PipelineSocketClosedError(code, reason);
|
|
11915
11950
|
socket.sources.forEach((source) => source.close(error));
|
|
11916
11951
|
socket.commands.forEach((command) => command.reject(error));
|
|
@@ -11936,12 +11971,14 @@ var PipelineBackend = class {
|
|
|
11936
11971
|
const source = typeof id === "string" ? socket.sources.get(id) : void 0;
|
|
11937
11972
|
if (type === "end") {
|
|
11938
11973
|
socket.held.delete(id);
|
|
11974
|
+
socket.lives.delete(id);
|
|
11939
11975
|
socket.accepted.delete(id);
|
|
11940
11976
|
socket.freed();
|
|
11941
11977
|
} else if (type === "result" && socket.held.has(id)) {
|
|
11942
11978
|
socket.accepted.add(id);
|
|
11943
11979
|
} else if (type === "error" && message["error"] !== "unknown_command") {
|
|
11944
11980
|
socket.held.delete(id);
|
|
11981
|
+
socket.lives.delete(id);
|
|
11945
11982
|
socket.wake();
|
|
11946
11983
|
}
|
|
11947
11984
|
if (type === "send" || type === "credit") {
|
|
@@ -11983,6 +12020,10 @@ var PipelineBackend = class {
|
|
|
11983
12020
|
command(socket, message) {
|
|
11984
12021
|
const id = this.newId(message["type"]);
|
|
11985
12022
|
return new Promise((resolve, reject) => {
|
|
12023
|
+
if (!socket.open) {
|
|
12024
|
+
reject(new PipelineSocketClosedError(void 0, "the socket closed"));
|
|
12025
|
+
return;
|
|
12026
|
+
}
|
|
11986
12027
|
socket.commands.set(id, { resolve, reject });
|
|
11987
12028
|
socket.sendJson({ ...message, id });
|
|
11988
12029
|
});
|
|
@@ -11991,15 +12032,28 @@ var PipelineBackend = class {
|
|
|
11991
12032
|
pop() {
|
|
11992
12033
|
return this._currentPop;
|
|
11993
12034
|
}
|
|
11994
|
-
/**
|
|
12035
|
+
/**
|
|
12036
|
+
* Set the pop on every open socket's pipeline; a pop the first refuses rejects, and the last pop it
|
|
12037
|
+
* took stays. shortcut: a refusal from a later socket, which the first took, leaves the earlier ones
|
|
12038
|
+
* on the new pop; a refusal is the pop's own fault, so every socket answers it alike.
|
|
12039
|
+
*/
|
|
11995
12040
|
async changePop(pop) {
|
|
11996
12041
|
if (this.ctx.client() === null) {
|
|
11997
12042
|
this._currentPop = pop;
|
|
11998
12043
|
return;
|
|
11999
12044
|
}
|
|
12000
12045
|
await this.inOrder(async () => {
|
|
12001
|
-
|
|
12002
|
-
|
|
12046
|
+
const sockets = this.openSockets();
|
|
12047
|
+
if (sockets.length > 0) {
|
|
12048
|
+
for (const socket of sockets) {
|
|
12049
|
+
try {
|
|
12050
|
+
await this.setPopOn(socket, pop);
|
|
12051
|
+
} catch (error) {
|
|
12052
|
+
if (!(error instanceof PipelineSocketClosedError)) {
|
|
12053
|
+
throw error;
|
|
12054
|
+
}
|
|
12055
|
+
}
|
|
12056
|
+
}
|
|
12003
12057
|
this._currentPop = pop;
|
|
12004
12058
|
return;
|
|
12005
12059
|
}
|
|
@@ -12160,22 +12214,30 @@ var PipelineBackend = class {
|
|
|
12160
12214
|
}
|
|
12161
12215
|
/** Send the job's setSource; the caller runs it in submission order. */
|
|
12162
12216
|
async setSource(job) {
|
|
12163
|
-
if (job.cancelled) {
|
|
12164
|
-
return null;
|
|
12165
|
-
}
|
|
12166
|
-
let socket = await this.ensureSocket();
|
|
12167
|
-
await socket.room(job);
|
|
12168
12217
|
if (job.cancelled) {
|
|
12169
12218
|
return null;
|
|
12170
12219
|
}
|
|
12171
12220
|
const combined = await combinedMedia(job);
|
|
12172
|
-
socket
|
|
12221
|
+
let socket;
|
|
12222
|
+
while (true) {
|
|
12223
|
+
socket = await this.socketForJob(job);
|
|
12224
|
+
await socket.room(job);
|
|
12225
|
+
if (job.cancelled) {
|
|
12226
|
+
return null;
|
|
12227
|
+
}
|
|
12228
|
+
if (socket.open) {
|
|
12229
|
+
break;
|
|
12230
|
+
}
|
|
12231
|
+
}
|
|
12173
12232
|
const source = new OpenSource(this.newId("source"));
|
|
12174
12233
|
if (combined === null) {
|
|
12175
12234
|
uploadKeys(job).forEach((key) => source.uploads.set(key, new Credit()));
|
|
12176
12235
|
}
|
|
12177
12236
|
socket.sources.set(source.id, source);
|
|
12178
12237
|
socket.held.add(source.id);
|
|
12238
|
+
if (job.live) {
|
|
12239
|
+
socket.lives.add(source.id);
|
|
12240
|
+
}
|
|
12179
12241
|
source.endedBefore = socket.ended;
|
|
12180
12242
|
job.openSource = source;
|
|
12181
12243
|
job.stopOnPipeline = () => {
|
|
@@ -12451,6 +12513,9 @@ var WorkerEndpoint = class extends Endpoint {
|
|
|
12451
12513
|
if (options.jobQueueLength > MAX_OPEN_SOURCES) {
|
|
12452
12514
|
throw new PipelineConfigurationError("job_queue_length", `jobQueueLength ${options.jobQueueLength} exceeds ${MAX_OPEN_SOURCES}, the open sources a pipeline API socket takes`);
|
|
12453
12515
|
}
|
|
12516
|
+
if (options.pipelineSockets !== void 0 && !(Number.isInteger(options.pipelineSockets) && options.pipelineSockets > 0)) {
|
|
12517
|
+
throw new PipelineConfigurationError("pipeline_sockets", `pipelineSockets ${options.pipelineSockets} is not a positive whole number`);
|
|
12518
|
+
}
|
|
12454
12519
|
}
|
|
12455
12520
|
super(options);
|
|
12456
12521
|
const context = {
|
|
@@ -14425,6 +14490,7 @@ export {
|
|
|
14425
14490
|
ModelType,
|
|
14426
14491
|
MotionModel,
|
|
14427
14492
|
PIPELINE_PATH,
|
|
14493
|
+
PIPELINE_SOCKETS,
|
|
14428
14494
|
PipelineBackend,
|
|
14429
14495
|
PipelineCommandError,
|
|
14430
14496
|
PipelineConfigurationError,
|