@eyepop.ai/eyepop 3.25.2 → 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 +53 -15
- package/dist/eyepop.index.d.ts +53 -15
- package/dist/eyepop.index.js +200 -64
- package/dist/eyepop.index.js.map +1 -1
- package/dist/eyepop.index.mjs +199 -64
- package/dist/eyepop.index.mjs.map +1 -1
- package/dist/eyepop.min.js +1 -1
- package/package.json +1 -1
package/dist/eyepop.index.js
CHANGED
|
@@ -8698,6 +8698,7 @@ __export(index_exports, {
|
|
|
8698
8698
|
ModelType: () => ModelType,
|
|
8699
8699
|
MotionModel: () => MotionModel,
|
|
8700
8700
|
PIPELINE_PATH: () => PIPELINE_PATH,
|
|
8701
|
+
PIPELINE_SOCKETS: () => PIPELINE_SOCKETS,
|
|
8701
8702
|
PipelineBackend: () => PipelineBackend,
|
|
8702
8703
|
PipelineCommandError: () => PipelineCommandError,
|
|
8703
8704
|
PipelineConfigurationError: () => PipelineConfigurationError,
|
|
@@ -9050,7 +9051,7 @@ var Endpoint = class {
|
|
|
9050
9051
|
this._expire_token_time = null;
|
|
9051
9052
|
this._client = null;
|
|
9052
9053
|
this._statusRetryHandlers = /* @__PURE__ */ new Map();
|
|
9053
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
9054
|
+
this._statusRetryHandlers.set(401, (statusCode) => this.statusHandler401(statusCode));
|
|
9054
9055
|
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
9055
9056
|
this._state = "Idle" /* Idle */;
|
|
9056
9057
|
this._message = null;
|
|
@@ -9066,7 +9067,7 @@ var Endpoint = class {
|
|
|
9066
9067
|
}
|
|
9067
9068
|
setStatusRetryHandlers(statusRetryHandlers = void 0) {
|
|
9068
9069
|
this._statusRetryHandlers = new Map(statusRetryHandlers);
|
|
9069
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
9070
|
+
this._statusRetryHandlers.set(401, (statusCode) => this.statusHandler401(statusCode));
|
|
9070
9071
|
}
|
|
9071
9072
|
eyepopUrl() {
|
|
9072
9073
|
if (this._options.eyepopUrl) {
|
|
@@ -9329,7 +9330,7 @@ var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
|
|
|
9329
9330
|
PredictionVersion3[PredictionVersion3["V3"] = 3] = "V3";
|
|
9330
9331
|
return PredictionVersion3;
|
|
9331
9332
|
})(PredictionVersion || {});
|
|
9332
|
-
var DEFAULT_PREDICTION_VERSION =
|
|
9333
|
+
var DEFAULT_PREDICTION_VERSION = 3 /* V3 */;
|
|
9333
9334
|
var VideoMode = /* @__PURE__ */ ((VideoMode2) => {
|
|
9334
9335
|
VideoMode2["STREAM"] = "stream";
|
|
9335
9336
|
VideoMode2["BUFFER"] = "buffer";
|
|
@@ -11040,14 +11041,6 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11040
11041
|
}
|
|
11041
11042
|
return this.ctx.options.pop || null;
|
|
11042
11043
|
}
|
|
11043
|
-
/**
|
|
11044
|
-
* A select forward's results come only at V3. Only a Pop that selects asks
|
|
11045
|
-
* for it, so a worker too old to know V3 - which also rejects that Pop - is
|
|
11046
|
-
* never sent a version it would not accept.
|
|
11047
|
-
*/
|
|
11048
|
-
predictionVersion() {
|
|
11049
|
-
return popSelects(this.pop()) ? 3 /* V3 */ : DEFAULT_PREDICTION_VERSION;
|
|
11050
|
-
}
|
|
11051
11044
|
async changePop(pop) {
|
|
11052
11045
|
if (this.ctx.options.popId == "transient" /* Transient */) {
|
|
11053
11046
|
await this.changeTransientPop(pop);
|
|
@@ -11148,7 +11141,7 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11148
11141
|
return { stream: streamSource.stream, mimeType: streamSource.mimeType };
|
|
11149
11142
|
})
|
|
11150
11143
|
);
|
|
11151
|
-
return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger,
|
|
11144
|
+
return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
|
|
11152
11145
|
} catch (e) {
|
|
11153
11146
|
limit.release();
|
|
11154
11147
|
throw e;
|
|
@@ -11167,7 +11160,7 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11167
11160
|
stream,
|
|
11168
11161
|
mimeType: mimeTypes?.[i] ?? null
|
|
11169
11162
|
}));
|
|
11170
|
-
return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger,
|
|
11163
|
+
return this.start(new UploadGroupJob(sources, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
|
|
11171
11164
|
} catch (e) {
|
|
11172
11165
|
limit.release();
|
|
11173
11166
|
throw e;
|
|
@@ -11182,7 +11175,7 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11182
11175
|
await limit.acquire();
|
|
11183
11176
|
try {
|
|
11184
11177
|
this.ctx.updateState();
|
|
11185
|
-
return this.start(new LoadFromGroupJob(urls, params, async () => this.session(), client, this.ctx.requestLogger,
|
|
11178
|
+
return this.start(new LoadFromGroupJob(urls, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
|
|
11186
11179
|
} catch (e) {
|
|
11187
11180
|
limit.release();
|
|
11188
11181
|
throw e;
|
|
@@ -11226,14 +11219,14 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11226
11219
|
client,
|
|
11227
11220
|
this.ctx.requestLogger,
|
|
11228
11221
|
void 0,
|
|
11229
|
-
|
|
11222
|
+
DEFAULT_PREDICTION_VERSION
|
|
11230
11223
|
),
|
|
11231
11224
|
true
|
|
11232
11225
|
);
|
|
11233
11226
|
}
|
|
11234
11227
|
async uploadStream(source, params) {
|
|
11235
11228
|
return await this.startConnected(
|
|
11236
|
-
async (client) => new UploadJob(source.stream, source.mimeType, source.size, source.videoMode, params, async () => this.session(), client, this.ctx.requestLogger, void 0,
|
|
11229
|
+
async (client) => new UploadJob(source.stream, source.mimeType, source.size, source.videoMode, params, async () => this.session(), client, this.ctx.requestLogger, void 0, DEFAULT_PREDICTION_VERSION),
|
|
11237
11230
|
true
|
|
11238
11231
|
);
|
|
11239
11232
|
}
|
|
@@ -11258,7 +11251,7 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11258
11251
|
client,
|
|
11259
11252
|
this.ctx.requestLogger,
|
|
11260
11253
|
uploadBodyFactory,
|
|
11261
|
-
|
|
11254
|
+
DEFAULT_PREDICTION_VERSION
|
|
11262
11255
|
);
|
|
11263
11256
|
});
|
|
11264
11257
|
}
|
|
@@ -11266,13 +11259,13 @@ var _SessionBackend = class _SessionBackend {
|
|
|
11266
11259
|
if (source.url.startsWith("file://")) {
|
|
11267
11260
|
return await this.uploadPath({ path: source.url.substring("file://".length) }, params);
|
|
11268
11261
|
}
|
|
11269
|
-
return await this.startConnected(async (client) => new LoadFromJob(source.url, params, async () => this.session(), client, this.ctx.requestLogger,
|
|
11262
|
+
return await this.startConnected(async (client) => new LoadFromJob(source.url, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
|
|
11270
11263
|
}
|
|
11271
11264
|
async loadFromAssetUuid(source, params) {
|
|
11272
|
-
return await this.startConnected(async (client) => new LoadFromAssetUuidJob(source.assetUuid, params, async () => this.session(), client, this.ctx.requestLogger,
|
|
11265
|
+
return await this.startConnected(async (client) => new LoadFromAssetUuidJob(source.assetUuid, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
|
|
11273
11266
|
}
|
|
11274
11267
|
async loadMediaStream(source, params) {
|
|
11275
|
-
return await this.startConnected(async (client) => new LoadMediaStreamJob(source.mediaStream, params, async () => this.session(), client, this.ctx.requestLogger,
|
|
11268
|
+
return await this.startConnected(async (client) => new LoadMediaStreamJob(source.mediaStream, params, async () => this.session(), client, this.ctx.requestLogger, DEFAULT_PREDICTION_VERSION));
|
|
11276
11269
|
}
|
|
11277
11270
|
async reconnect() {
|
|
11278
11271
|
if (this.ctx.options.popId == "transient" /* Transient */) {
|
|
@@ -11620,6 +11613,8 @@ if (isBrowser) {
|
|
|
11620
11613
|
var PIPELINE_PATH = "/api/v1/pipeline";
|
|
11621
11614
|
var SUBPROTOCOL = "eyepop.v1";
|
|
11622
11615
|
var MAX_OPEN_SOURCES = 256;
|
|
11616
|
+
var PIPELINE_SOCKETS = 4;
|
|
11617
|
+
var EXTRA_SOCKET_RETRY_MS = 10 * 1e3;
|
|
11623
11618
|
var COMBINED_UPLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
11624
11619
|
var CHUNK = 1;
|
|
11625
11620
|
var COMBINED = 2;
|
|
@@ -11722,6 +11717,8 @@ var OpenSource = class {
|
|
|
11722
11717
|
this.id = id;
|
|
11723
11718
|
this.inbox = new Inbox();
|
|
11724
11719
|
this.uploads = /* @__PURE__ */ new Map();
|
|
11720
|
+
// The socket's ended count when the setSource went out.
|
|
11721
|
+
this.endedBefore = 0;
|
|
11725
11722
|
}
|
|
11726
11723
|
close(error) {
|
|
11727
11724
|
this.uploads.forEach((upload) => upload.end());
|
|
@@ -11734,14 +11731,60 @@ var PipelineSocket = class {
|
|
|
11734
11731
|
this.sources = /* @__PURE__ */ new Map();
|
|
11735
11732
|
this.commands = /* @__PURE__ */ new Map();
|
|
11736
11733
|
this._closed = false;
|
|
11734
|
+
// The ids of sources sent on this socket that have neither ended nor been refused, as the
|
|
11735
|
+
// pipeline API counts them, cancelled ones included.
|
|
11736
|
+
this.held = /* @__PURE__ */ new Set();
|
|
11737
|
+
// The held sources that are live. A live source never ends, so nothing queued behind it plays.
|
|
11738
|
+
this.lives = /* @__PURE__ */ new Set();
|
|
11739
|
+
// The held sources the pipeline API took: only these end, so only these make room.
|
|
11740
|
+
this.accepted = /* @__PURE__ */ new Set();
|
|
11741
|
+
// The ends on this socket so far.
|
|
11742
|
+
this.ended = 0;
|
|
11743
|
+
// How many sources the socket had taken when it last refused queue_full: while as many are held,
|
|
11744
|
+
// the next send waits. Null once none is held.
|
|
11745
|
+
this.cap = null;
|
|
11746
|
+
this._roomWaiters = [];
|
|
11747
|
+
this.heartbeat = null;
|
|
11737
11748
|
this.closed = new Promise((resolve) => this._markClosed = resolve);
|
|
11738
11749
|
}
|
|
11750
|
+
/** A source ended or the socket closed: wake the send waiting for room. */
|
|
11751
|
+
freed() {
|
|
11752
|
+
this.ended += 1;
|
|
11753
|
+
this.wake();
|
|
11754
|
+
}
|
|
11755
|
+
/** A held source ended or was refused: wake the send waiting for room. */
|
|
11756
|
+
wake() {
|
|
11757
|
+
if (this.held.size === 0) {
|
|
11758
|
+
this.cap = null;
|
|
11759
|
+
}
|
|
11760
|
+
const waiters = this._roomWaiters;
|
|
11761
|
+
this._roomWaiters = [];
|
|
11762
|
+
waiters.forEach((wake) => wake());
|
|
11763
|
+
}
|
|
11764
|
+
get hasRoom() {
|
|
11765
|
+
return this.cap === null || this.held.size < this.cap;
|
|
11766
|
+
}
|
|
11767
|
+
/**
|
|
11768
|
+
* Wait, while the socket is full, until a source ends, it closes or the job is cancelled. The
|
|
11769
|
+
* caller sends in order, so one send waits here and each end lets one go, instead of every
|
|
11770
|
+
* refused source sending its media again at once.
|
|
11771
|
+
*/
|
|
11772
|
+
async room(job) {
|
|
11773
|
+
while (!this.hasRoom && this.open && !job.cancelled) {
|
|
11774
|
+
await Promise.race([new Promise((resolve) => this._roomWaiters.push(resolve)), job.whenCancelled]);
|
|
11775
|
+
}
|
|
11776
|
+
}
|
|
11739
11777
|
get open() {
|
|
11740
11778
|
return !this._closed && this.ws.readyState === 1;
|
|
11741
11779
|
}
|
|
11742
11780
|
markClosed() {
|
|
11781
|
+
if (this.heartbeat !== null) {
|
|
11782
|
+
clearInterval(this.heartbeat);
|
|
11783
|
+
this.heartbeat = null;
|
|
11784
|
+
}
|
|
11743
11785
|
this._closed = true;
|
|
11744
11786
|
this._markClosed();
|
|
11787
|
+
this.freed();
|
|
11745
11788
|
}
|
|
11746
11789
|
sendJson(message) {
|
|
11747
11790
|
this.ws.send(JSON.stringify(message));
|
|
@@ -11760,6 +11803,8 @@ var PipelineJob = class {
|
|
|
11760
11803
|
this._results = new Inbox();
|
|
11761
11804
|
this._consumed = false;
|
|
11762
11805
|
this._cancelled = false;
|
|
11806
|
+
// Settles when the job is cancelled, so that a send waiting for room ends at once.
|
|
11807
|
+
this.whenCancelled = new Promise((resolve) => this._markCancelled = resolve);
|
|
11763
11808
|
this.openSource = null;
|
|
11764
11809
|
// Stops the job's source on the pipeline, once it was sent.
|
|
11765
11810
|
this.stopOnPipeline = null;
|
|
@@ -11799,6 +11844,7 @@ var PipelineJob = class {
|
|
|
11799
11844
|
return;
|
|
11800
11845
|
}
|
|
11801
11846
|
this._cancelled = true;
|
|
11847
|
+
this._markCancelled();
|
|
11802
11848
|
this._results.push(null);
|
|
11803
11849
|
this.openSource?.inbox.push(CANCELLED);
|
|
11804
11850
|
this.stopOnPipeline?.();
|
|
@@ -11824,14 +11870,14 @@ var PipelineBackend = class {
|
|
|
11824
11870
|
constructor(ctx) {
|
|
11825
11871
|
this.ctx = ctx;
|
|
11826
11872
|
this._pipelineApiUrl = null;
|
|
11827
|
-
this.
|
|
11873
|
+
this._sockets = [];
|
|
11828
11874
|
this._connecting = null;
|
|
11829
11875
|
// Every job's setSource and every changePop goes out through this chain, in the order submitted.
|
|
11830
11876
|
this._sendChain = Promise.resolve();
|
|
11831
11877
|
this._disconnecting = false;
|
|
11832
11878
|
this._nextId = 0;
|
|
11833
11879
|
this._warned = /* @__PURE__ */ new Set();
|
|
11834
|
-
this.
|
|
11880
|
+
this._noExtraSocketUntil = 0;
|
|
11835
11881
|
// Reconnects at once since a source last played.
|
|
11836
11882
|
this._reconnects = 0;
|
|
11837
11883
|
this._currentPop = ctx.options.pop ?? null;
|
|
@@ -11845,13 +11891,9 @@ var PipelineBackend = class {
|
|
|
11845
11891
|
async disconnect(closeEndpoint) {
|
|
11846
11892
|
await closeEndpoint();
|
|
11847
11893
|
this._disconnecting = true;
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
}
|
|
11852
|
-
const socket = this._socket;
|
|
11853
|
-
this._socket = null;
|
|
11854
|
-
if (socket !== null) {
|
|
11894
|
+
const sockets = this._sockets;
|
|
11895
|
+
this._sockets = [];
|
|
11896
|
+
for (const socket of sockets) {
|
|
11855
11897
|
socket.ws.close(1e3);
|
|
11856
11898
|
await socket.closed;
|
|
11857
11899
|
}
|
|
@@ -11869,21 +11911,55 @@ var PipelineBackend = class {
|
|
|
11869
11911
|
this._sendChain = result.catch(() => void 0);
|
|
11870
11912
|
return result;
|
|
11871
11913
|
}
|
|
11872
|
-
|
|
11914
|
+
openSockets() {
|
|
11915
|
+
return this._sockets.filter((socket) => socket.open);
|
|
11916
|
+
}
|
|
11917
|
+
/** An open socket, or a new one with the last pop set on it. */
|
|
11873
11918
|
async ensureSocket() {
|
|
11874
|
-
|
|
11875
|
-
|
|
11919
|
+
const open = this.openSockets()[0];
|
|
11920
|
+
if (open !== void 0) {
|
|
11921
|
+
return open;
|
|
11876
11922
|
}
|
|
11877
11923
|
if (this._connecting === null) {
|
|
11878
|
-
this._connecting = this.openSocket().finally(() => this._connecting = null);
|
|
11924
|
+
this._connecting = this.openSocket(true).finally(() => this._connecting = null);
|
|
11879
11925
|
}
|
|
11880
11926
|
return await this._connecting;
|
|
11881
11927
|
}
|
|
11882
|
-
|
|
11928
|
+
/**
|
|
11929
|
+
* The socket for the next job; the caller runs it in submission order. A live job goes on a socket
|
|
11930
|
+
* of its own, an idle one or a new one, which `pipelineSockets` does not count. Any other job skips
|
|
11931
|
+
* the sockets with a live source: a new one while every other socket has a source and fewer than
|
|
11932
|
+
* `pipelineSockets` are open, else the one with room and the fewest sources.
|
|
11933
|
+
*/
|
|
11934
|
+
async socketForJob(job) {
|
|
11935
|
+
if (job.live) {
|
|
11936
|
+
return this.openSockets().find((socket) => socket.held.size === 0) ?? await this.openSocket(true);
|
|
11937
|
+
}
|
|
11938
|
+
const sockets = this.openSockets().filter((socket) => socket.lives.size === 0);
|
|
11939
|
+
if (sockets.length === 0) {
|
|
11940
|
+
return await this.openSocket(true);
|
|
11941
|
+
}
|
|
11942
|
+
if (sockets.length < (this.ctx.options.pipelineSockets ?? PIPELINE_SOCKETS) && sockets.every((socket) => socket.held.size > 0) && Date.now() >= this._noExtraSocketUntil) {
|
|
11943
|
+
try {
|
|
11944
|
+
return await this.openSocket(false);
|
|
11945
|
+
} catch (error) {
|
|
11946
|
+
this._noExtraSocketUntil = Date.now() + EXTRA_SOCKET_RETRY_MS;
|
|
11947
|
+
this.ctx.requestLogger.info("could not open another pipeline API socket: %s", error);
|
|
11948
|
+
}
|
|
11949
|
+
}
|
|
11950
|
+
const rank = (socket) => (socket.hasRoom ? 0 : MAX_OPEN_SOURCES + 1) + socket.held.size;
|
|
11951
|
+
return sockets.reduce((best, socket) => rank(socket) < rank(best) ? socket : best);
|
|
11952
|
+
}
|
|
11953
|
+
/** A new socket with the last pop set on it; `patient` waits through 503 with Retry-After. */
|
|
11954
|
+
async openSocket(patient) {
|
|
11883
11955
|
if (this.ctx.client() === null) {
|
|
11884
11956
|
throw new PipelineSocketClosedError(void 0, "the endpoint is not connected");
|
|
11885
11957
|
}
|
|
11886
|
-
const ws = await this.upgrade(await this.socketUrl());
|
|
11958
|
+
const ws = await this.upgrade(await this.socketUrl(), patient);
|
|
11959
|
+
if (this._disconnecting) {
|
|
11960
|
+
ws.close(1e3);
|
|
11961
|
+
throw new PipelineSocketClosedError(void 0, "the endpoint disconnected");
|
|
11962
|
+
}
|
|
11887
11963
|
const socket = new PipelineSocket(ws);
|
|
11888
11964
|
ws.onmessage = (event) => {
|
|
11889
11965
|
if (typeof event.data === "string") {
|
|
@@ -11897,31 +11973,29 @@ var PipelineBackend = class {
|
|
|
11897
11973
|
ws.onerror = () => {
|
|
11898
11974
|
};
|
|
11899
11975
|
ws.onclose = (event) => this.closed(socket, event.code, event.reason);
|
|
11900
|
-
this.
|
|
11901
|
-
this.startHeartbeat(
|
|
11976
|
+
this._sockets.push(socket);
|
|
11977
|
+
this.startHeartbeat(socket);
|
|
11902
11978
|
if (this._currentPop !== null) {
|
|
11903
11979
|
try {
|
|
11904
11980
|
await this.setPopOn(socket, this._currentPop);
|
|
11905
11981
|
} catch (error) {
|
|
11906
|
-
this.
|
|
11982
|
+
this._sockets = this._sockets.filter((other) => other !== socket);
|
|
11907
11983
|
ws.close(1e3);
|
|
11908
11984
|
throw error;
|
|
11909
11985
|
}
|
|
11910
11986
|
}
|
|
11911
11987
|
return socket;
|
|
11912
11988
|
}
|
|
11913
|
-
startHeartbeat(
|
|
11914
|
-
|
|
11915
|
-
clearInterval(this._heartbeat);
|
|
11916
|
-
}
|
|
11989
|
+
startHeartbeat(socket) {
|
|
11990
|
+
const ws = socket.ws;
|
|
11917
11991
|
const ping = ws.ping;
|
|
11918
11992
|
if (typeof ping === "function") {
|
|
11919
|
-
|
|
11993
|
+
socket.heartbeat = setInterval(() => {
|
|
11920
11994
|
if (ws.readyState === 1) {
|
|
11921
11995
|
ping.call(ws);
|
|
11922
11996
|
}
|
|
11923
11997
|
}, HEARTBEAT_MS);
|
|
11924
|
-
|
|
11998
|
+
socket.heartbeat.unref?.();
|
|
11925
11999
|
}
|
|
11926
12000
|
}
|
|
11927
12001
|
async socketUrl() {
|
|
@@ -11959,7 +12033,7 @@ var PipelineBackend = class {
|
|
|
11959
12033
|
return (await this.ctx.credentialHeader()).replace(/^Bearer /, "");
|
|
11960
12034
|
}
|
|
11961
12035
|
/** Open the socket, waiting through 503 with Retry-After and exchanging the API key again after a 401. */
|
|
11962
|
-
async upgrade(url) {
|
|
12036
|
+
async upgrade(url, patient) {
|
|
11963
12037
|
const deadline = Date.now() + MAX_RETRY_MS;
|
|
11964
12038
|
let rejected = false;
|
|
11965
12039
|
while (true) {
|
|
@@ -11977,7 +12051,7 @@ var PipelineBackend = class {
|
|
|
11977
12051
|
continue;
|
|
11978
12052
|
}
|
|
11979
12053
|
const wait = error.status === 503 && error.retryAfter !== void 0 ? Math.max(error.retryAfter, 1) * 1e3 : void 0;
|
|
11980
|
-
if (wait === void 0 || Date.now() + wait > deadline) {
|
|
12054
|
+
if (!patient || wait === void 0 || Date.now() + wait > deadline) {
|
|
11981
12055
|
throw error;
|
|
11982
12056
|
}
|
|
11983
12057
|
this.ctx.requestLogger.info("the pipeline API answered 503, retrying in %d ms", wait);
|
|
@@ -11987,9 +12061,7 @@ var PipelineBackend = class {
|
|
|
11987
12061
|
}
|
|
11988
12062
|
closed(socket, code, reason) {
|
|
11989
12063
|
socket.markClosed();
|
|
11990
|
-
|
|
11991
|
-
this._socket = null;
|
|
11992
|
-
}
|
|
12064
|
+
this._sockets = this._sockets.filter((other) => other !== socket);
|
|
11993
12065
|
const error = new PipelineSocketClosedError(code, reason);
|
|
11994
12066
|
socket.sources.forEach((source) => source.close(error));
|
|
11995
12067
|
socket.commands.forEach((command) => command.reject(error));
|
|
@@ -12013,6 +12085,18 @@ var PipelineBackend = class {
|
|
|
12013
12085
|
const type = message["type"];
|
|
12014
12086
|
const id = message["id"];
|
|
12015
12087
|
const source = typeof id === "string" ? socket.sources.get(id) : void 0;
|
|
12088
|
+
if (type === "end") {
|
|
12089
|
+
socket.held.delete(id);
|
|
12090
|
+
socket.lives.delete(id);
|
|
12091
|
+
socket.accepted.delete(id);
|
|
12092
|
+
socket.freed();
|
|
12093
|
+
} else if (type === "result" && socket.held.has(id)) {
|
|
12094
|
+
socket.accepted.add(id);
|
|
12095
|
+
} else if (type === "error" && message["error"] !== "unknown_command") {
|
|
12096
|
+
socket.held.delete(id);
|
|
12097
|
+
socket.lives.delete(id);
|
|
12098
|
+
socket.wake();
|
|
12099
|
+
}
|
|
12016
12100
|
if (type === "send" || type === "credit") {
|
|
12017
12101
|
const upload = source?.uploads.get(message["member"] ?? null);
|
|
12018
12102
|
upload?.grant(Number(type === "send" ? message["credit"] : message["bytes"]));
|
|
@@ -12052,6 +12136,10 @@ var PipelineBackend = class {
|
|
|
12052
12136
|
command(socket, message) {
|
|
12053
12137
|
const id = this.newId(message["type"]);
|
|
12054
12138
|
return new Promise((resolve, reject) => {
|
|
12139
|
+
if (!socket.open) {
|
|
12140
|
+
reject(new PipelineSocketClosedError(void 0, "the socket closed"));
|
|
12141
|
+
return;
|
|
12142
|
+
}
|
|
12055
12143
|
socket.commands.set(id, { resolve, reject });
|
|
12056
12144
|
socket.sendJson({ ...message, id });
|
|
12057
12145
|
});
|
|
@@ -12060,15 +12148,28 @@ var PipelineBackend = class {
|
|
|
12060
12148
|
pop() {
|
|
12061
12149
|
return this._currentPop;
|
|
12062
12150
|
}
|
|
12063
|
-
/**
|
|
12151
|
+
/**
|
|
12152
|
+
* Set the pop on every open socket's pipeline; a pop the first refuses rejects, and the last pop it
|
|
12153
|
+
* took stays. shortcut: a refusal from a later socket, which the first took, leaves the earlier ones
|
|
12154
|
+
* on the new pop; a refusal is the pop's own fault, so every socket answers it alike.
|
|
12155
|
+
*/
|
|
12064
12156
|
async changePop(pop) {
|
|
12065
12157
|
if (this.ctx.client() === null) {
|
|
12066
12158
|
this._currentPop = pop;
|
|
12067
12159
|
return;
|
|
12068
12160
|
}
|
|
12069
12161
|
await this.inOrder(async () => {
|
|
12070
|
-
|
|
12071
|
-
|
|
12162
|
+
const sockets = this.openSockets();
|
|
12163
|
+
if (sockets.length > 0) {
|
|
12164
|
+
for (const socket of sockets) {
|
|
12165
|
+
try {
|
|
12166
|
+
await this.setPopOn(socket, pop);
|
|
12167
|
+
} catch (error) {
|
|
12168
|
+
if (!(error instanceof PipelineSocketClosedError)) {
|
|
12169
|
+
throw error;
|
|
12170
|
+
}
|
|
12171
|
+
}
|
|
12172
|
+
}
|
|
12072
12173
|
this._currentPop = pop;
|
|
12073
12174
|
return;
|
|
12074
12175
|
}
|
|
@@ -12171,7 +12272,12 @@ var PipelineBackend = class {
|
|
|
12171
12272
|
}
|
|
12172
12273
|
};
|
|
12173
12274
|
}
|
|
12174
|
-
/**
|
|
12275
|
+
/**
|
|
12276
|
+
* Run the job's source to its end. A live source refused overloaded goes once more on a new socket.
|
|
12277
|
+
* A source refused queue_full goes again once another source on the socket ended, so it may go after
|
|
12278
|
+
* jobs submitted later; with none of the endpoint's other sources open there, or with an upload from
|
|
12279
|
+
* the caller's own stream, which cannot be read twice, the refusal stands.
|
|
12280
|
+
*/
|
|
12175
12281
|
async run(job) {
|
|
12176
12282
|
let retried = false;
|
|
12177
12283
|
while (true) {
|
|
@@ -12182,9 +12288,9 @@ var PipelineBackend = class {
|
|
|
12182
12288
|
const { socket, source } = sent;
|
|
12183
12289
|
const uploading = source.uploads.size > 0 ? this.upload(socket, source, job) : Promise.resolve();
|
|
12184
12290
|
let whip = null;
|
|
12185
|
-
let
|
|
12291
|
+
let refused;
|
|
12186
12292
|
try {
|
|
12187
|
-
|
|
12293
|
+
refused = await this.follow(job, source, () => {
|
|
12188
12294
|
if (job.mediaStream !== void 0) {
|
|
12189
12295
|
const fail = (error) => {
|
|
12190
12296
|
job.stopOnPipeline?.();
|
|
@@ -12202,9 +12308,19 @@ var PipelineBackend = class {
|
|
|
12202
12308
|
await uploading;
|
|
12203
12309
|
await whip?.close();
|
|
12204
12310
|
}
|
|
12205
|
-
if (
|
|
12311
|
+
if (refused === null) {
|
|
12206
12312
|
return;
|
|
12207
12313
|
}
|
|
12314
|
+
if (refused === "queue_full") {
|
|
12315
|
+
if (socket.accepted.size === 0 && socket.ended === source.endedBefore) {
|
|
12316
|
+
throw new PipelineCommandError("queue_full", "the socket refused the source with none of its own open");
|
|
12317
|
+
}
|
|
12318
|
+
if (job.media.some((media) => media.once)) {
|
|
12319
|
+
throw new PipelineCommandError("queue_full", "the socket refused an upload from a stream, which cannot be sent again");
|
|
12320
|
+
}
|
|
12321
|
+
socket.cap = Math.min(socket.accepted.size, socket.cap ?? socket.accepted.size) || null;
|
|
12322
|
+
continue;
|
|
12323
|
+
}
|
|
12208
12324
|
if (retried || !job.live) {
|
|
12209
12325
|
throw new PipelineCommandError("overloaded", "the pipeline API has no capacity for another live source");
|
|
12210
12326
|
}
|
|
@@ -12218,12 +12334,27 @@ var PipelineBackend = class {
|
|
|
12218
12334
|
return null;
|
|
12219
12335
|
}
|
|
12220
12336
|
const combined = await combinedMedia(job);
|
|
12221
|
-
|
|
12337
|
+
let socket;
|
|
12338
|
+
while (true) {
|
|
12339
|
+
socket = await this.socketForJob(job);
|
|
12340
|
+
await socket.room(job);
|
|
12341
|
+
if (job.cancelled) {
|
|
12342
|
+
return null;
|
|
12343
|
+
}
|
|
12344
|
+
if (socket.open) {
|
|
12345
|
+
break;
|
|
12346
|
+
}
|
|
12347
|
+
}
|
|
12222
12348
|
const source = new OpenSource(this.newId("source"));
|
|
12223
12349
|
if (combined === null) {
|
|
12224
12350
|
uploadKeys(job).forEach((key) => source.uploads.set(key, new Credit()));
|
|
12225
12351
|
}
|
|
12226
12352
|
socket.sources.set(source.id, source);
|
|
12353
|
+
socket.held.add(source.id);
|
|
12354
|
+
if (job.live) {
|
|
12355
|
+
socket.lives.add(source.id);
|
|
12356
|
+
}
|
|
12357
|
+
source.endedBefore = socket.ended;
|
|
12227
12358
|
job.openSource = source;
|
|
12228
12359
|
job.stopOnPipeline = () => {
|
|
12229
12360
|
if (socket.open) {
|
|
@@ -12238,15 +12369,15 @@ var PipelineBackend = class {
|
|
|
12238
12369
|
}
|
|
12239
12370
|
return { socket, source };
|
|
12240
12371
|
}
|
|
12241
|
-
/** Hand the source's predictions to the job until it ends;
|
|
12372
|
+
/** Hand the source's predictions to the job until it ends; the refusal to retry on, overloaded or queue_full. */
|
|
12242
12373
|
async follow(job, source, onResult) {
|
|
12243
12374
|
while (true) {
|
|
12244
12375
|
if (job.cancelled) {
|
|
12245
|
-
return
|
|
12376
|
+
return null;
|
|
12246
12377
|
}
|
|
12247
12378
|
const message = await source.inbox.next();
|
|
12248
12379
|
if (message === CANCELLED) {
|
|
12249
|
-
return
|
|
12380
|
+
return null;
|
|
12250
12381
|
}
|
|
12251
12382
|
if (message instanceof Error) {
|
|
12252
12383
|
throw message;
|
|
@@ -12260,12 +12391,12 @@ var PipelineBackend = class {
|
|
|
12260
12391
|
break;
|
|
12261
12392
|
case "end":
|
|
12262
12393
|
if (message["reason"] === "eos") {
|
|
12263
|
-
return
|
|
12394
|
+
return null;
|
|
12264
12395
|
}
|
|
12265
12396
|
throw new PipelineSourceError(source.id, message["reason"] ?? "", message["message"] ?? "");
|
|
12266
12397
|
case "error":
|
|
12267
|
-
if (message["error"] === "overloaded") {
|
|
12268
|
-
return
|
|
12398
|
+
if (message["error"] === "overloaded" || message["error"] === "queue_full") {
|
|
12399
|
+
return message["error"];
|
|
12269
12400
|
}
|
|
12270
12401
|
throw new PipelineCommandError(message["error"] ?? "", message["message"] ?? "");
|
|
12271
12402
|
case "dropped":
|
|
@@ -12409,7 +12540,8 @@ function knownSize(body) {
|
|
|
12409
12540
|
return void 0;
|
|
12410
12541
|
}
|
|
12411
12542
|
function bodyMedia(body, contentType, size) {
|
|
12412
|
-
|
|
12543
|
+
const once = !(typeof Blob !== "undefined" && body instanceof Blob) && !(body instanceof ArrayBuffer) && !ArrayBuffer.isView(body);
|
|
12544
|
+
return { open: async () => body, contentType, size: size ?? knownSize(body), once };
|
|
12413
12545
|
}
|
|
12414
12546
|
function uploadGroup(media) {
|
|
12415
12547
|
return { group: media.map((m) => ({ upload: { contentType: m.contentType } })) };
|
|
@@ -12497,6 +12629,9 @@ var WorkerEndpoint = class extends Endpoint {
|
|
|
12497
12629
|
if (options.jobQueueLength > MAX_OPEN_SOURCES) {
|
|
12498
12630
|
throw new PipelineConfigurationError("job_queue_length", `jobQueueLength ${options.jobQueueLength} exceeds ${MAX_OPEN_SOURCES}, the open sources a pipeline API socket takes`);
|
|
12499
12631
|
}
|
|
12632
|
+
if (options.pipelineSockets !== void 0 && !(Number.isInteger(options.pipelineSockets) && options.pipelineSockets > 0)) {
|
|
12633
|
+
throw new PipelineConfigurationError("pipeline_sockets", `pipelineSockets ${options.pipelineSockets} is not a positive whole number`);
|
|
12634
|
+
}
|
|
12500
12635
|
}
|
|
12501
12636
|
super(options);
|
|
12502
12637
|
const context = {
|
|
@@ -14472,6 +14607,7 @@ var index_default = EyePop;
|
|
|
14472
14607
|
ModelType,
|
|
14473
14608
|
MotionModel,
|
|
14474
14609
|
PIPELINE_PATH,
|
|
14610
|
+
PIPELINE_SOCKETS,
|
|
14475
14611
|
PipelineBackend,
|
|
14476
14612
|
PipelineCommandError,
|
|
14477
14613
|
PipelineConfigurationError,
|