@torrent-tv/proxy 2.107.37 → 2.107.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.107.37",
3
+ "version": "2.107.38",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -229,7 +229,18 @@ export class ViewerRequests {
229
229
  sourceObserved: true, waitElapsedMs });
230
230
  const featuresChanged = this.#recordEarlyWaitSnapshot(context, context.features);
231
231
  const elapsedMs = waitElapsedSince(context, waitElapsedMs);
232
- const measuredWorkSeconds = processing.firstRequiredSegmentsSeconds;
232
+ // The wait ends when the start forecast lets the picture start, and the
233
+ // first required pieces being there is only part of that: a source
234
+ // supplied slower than the film plays is held until the cushion carries
235
+ // it to the end. Counted to the first pieces alone, the estimate reached
236
+ // zero with seven minutes still to wait, and the page had no time to show
237
+ // (Home Assistant 2026-10-26, Despicable Me 3 BDRemux at 0.53x,
238
+ // torrent-tv/meta#239).
239
+ const firstRequiredSeconds = processing.firstRequiredSegmentsSeconds;
240
+ const startGateSeconds = processing.startGateSeconds;
241
+ const measuredWorkSeconds = Number.isFinite(startGateSeconds) && startGateSeconds >= 0
242
+ ? Math.max(startGateSeconds, Number.isFinite(firstRequiredSeconds) ? firstRequiredSeconds : 0)
243
+ : firstRequiredSeconds;
233
244
  const hasActiveEstimate = Number.isFinite(measuredWorkSeconds) && measuredWorkSeconds >= 0;
234
245
  const stagesChanged = Array.isArray(processing.firstRequiredStages) &&
235
246
  JSON.stringify(processing.firstRequiredStages) !== JSON.stringify(context.activeFirstRequiredStages);
@@ -258,7 +269,8 @@ export class ViewerRequests {
258
269
  const seconds = earlyWaitRemainingSeconds(totalSeconds, elapsedMs);
259
270
  if (!Number.isFinite(seconds) || seconds < 0) return null;
260
271
  this.#logEarlyForecast(context, { elapsedMs, seconds, totalSeconds, historical,
261
- firstRequiredSegmentsSeconds: hasActiveEstimate ? measuredWorkSeconds : context.activeFirstRequiredSegmentsSeconds,
272
+ firstRequiredSegmentsSeconds: hasActiveEstimate ? firstRequiredSeconds ?? null : context.activeFirstRequiredSegmentsSeconds,
273
+ startGateSeconds: hasActiveEstimate && Number.isFinite(startGateSeconds) ? startGateSeconds : null,
262
274
  firstRequiredStages: context.activeFirstRequiredStages });
263
275
  return { version: 1, waitId, revision: context.revision, remainingSeconds: seconds, ageMs: 0 };
264
276
  }
@@ -269,11 +281,14 @@ export class ViewerRequests {
269
281
  }
270
282
 
271
283
  #logEarlyForecast(context, { elapsedMs, seconds, totalSeconds, historical, firstRequiredSegmentsSeconds,
272
- firstRequiredStages }) {
284
+ startGateSeconds = null, firstRequiredStages }) {
273
285
  if (context.loggedForecastRevision === context.revision) return;
274
- const basis = Number.isFinite(firstRequiredSegmentsSeconds) ? "first-required" : "historical-stages";
275
- const estimate = basis === "first-required"
276
- ? { firstRequiredSegmentsSeconds, ...(Array.isArray(firstRequiredStages) ? { firstRequiredStages } : {}) }
286
+ const gated = Number.isFinite(startGateSeconds) &&
287
+ !(Number.isFinite(firstRequiredSegmentsSeconds) && firstRequiredSegmentsSeconds >= startGateSeconds);
288
+ const basis = gated ? "start-gate" : Number.isFinite(firstRequiredSegmentsSeconds) ? "first-required" : "historical-stages";
289
+ const estimate = basis !== "historical-stages"
290
+ ? { firstRequiredSegmentsSeconds, ...(Number.isFinite(startGateSeconds) ? { startGateSeconds } : {}),
291
+ ...(Array.isArray(firstRequiredStages) ? { firstRequiredStages } : {}) }
277
292
  : { stagesMs: historical?.stagesMs ?? null, observations: historical?.observations ?? null };
278
293
  logger.info(`early wait forecast ${JSON.stringify({ waitId: context.waitId, consumer: context.consumerId,
279
294
  generation: context.generation, features: context.features, elapsedMs, revision: context.revision,
@@ -1121,6 +1136,10 @@ export class ViewerRequests {
1121
1136
  videoSpeedX: videoOperation?.speed,
1122
1137
  audioSpeedX: audioOperation?.speed,
1123
1138
  firstRequiredSegmentsSeconds: forecast.firstRequiredSegmentsSeconds,
1139
+ // When the start forecast lets the picture start: its delay while
1140
+ // it holds the start, and nothing to add once it does not.
1141
+ ...(forecast.ready !== true && Number.isFinite(forecast.delaySeconds)
1142
+ ? { startGateSeconds: forecast.delaySeconds } : {}),
1124
1143
  firstRequiredStages: forecast.firstRequiredStages
1125
1144
  } });
1126
1145
  if (waitEstimate) forecast.waitEstimate = waitEstimate;