@torrent-tv/proxy 2.101.27 → 2.101.28

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.101.27",
3
+ "version": "2.101.28",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -282,6 +282,7 @@ export class LocalObservations {
282
282
  linkMbps: nonNegative(features.linkMbps),
283
283
  linkSampleAgeMs: nonNegative(features.linkSampleAgeMs),
284
284
  preparedOutputBytes: nonNegative(features.preparedOutputBytes),
285
+ fileAvailable: typeof features.fileAvailable === "boolean" ? features.fileAvailable : null,
285
286
  headerRemaining: nonNegative(features.headerRemaining),
286
287
  peers: nonNegative(features.peers),
287
288
  at: this.#now()
@@ -292,9 +293,10 @@ export class LocalObservations {
292
293
 
293
294
  /**
294
295
  * Predict this host's first-frame wait from the closest measured start with
295
- * the same media suffix. Distance is the sum of squared log-ratios for the
296
- * independent measured inputs; it does not mix bytes, bit rates and peers
297
- * in their raw units.
296
+ * the same media suffix. Distance uses squared log-ratios for numeric
297
+ * inputs and exact-state distance for categorical inputs; it does not mix
298
+ * bytes, bit rates and peers in their raw units. Missing data contributes
299
+ * distance only when the other observation measured that input.
298
300
  *
299
301
  * @param {object} features
300
302
  * @returns {number | null}
@@ -305,24 +307,39 @@ export class LocalObservations {
305
307
  const candidates = readings.filter(reading => reading.extension === (features.extension ?? ""));
306
308
  if (candidates.length === 0) return null;
307
309
  const dimensions = ["fileLength", "downloadSpeed", "linkMbps", "linkSampleAgeMs", "preparedOutputBytes",
308
- "headerRemaining", "peers"];
310
+ "fileAvailable", "headerRemaining", "peers"];
309
311
  const scales = new Map(dimensions.map(key => [key, median(candidates
310
- .map(reading => nonNegative(reading[key]))
312
+ .map(reading => key === "fileAvailable" ? null : nonNegative(reading[key]))
311
313
  .filter(value => value !== null && value > 0))]));
312
314
  let best = null;
313
315
  for (const reading of candidates) {
314
316
  let distance = 0;
315
317
  let used = 0;
318
+ let shared = 0;
316
319
  for (const key of dimensions) {
320
+ const current = key === "fileAvailable"
321
+ ? (typeof features[key] === "boolean" ? features[key] : null)
322
+ : nonNegative(features[key]);
323
+ const previous = key === "fileAvailable"
324
+ ? (typeof reading[key] === "boolean" ? reading[key] : null)
325
+ : nonNegative(reading[key]);
326
+ if (current === null && previous === null) continue;
327
+ if (current === null || previous === null) {
328
+ distance += 1;
329
+ used += 1;
330
+ continue;
331
+ }
332
+ shared += 1;
317
333
  const scale = scales.get(key);
318
- const current = nonNegative(features[key]);
319
- const previous = nonNegative(reading[key]);
320
- if (scale === null || current === null || previous === null) continue;
321
- const difference = Math.log1p(current / scale) - Math.log1p(previous / scale);
334
+ const difference = typeof current === "boolean" && typeof previous === "boolean"
335
+ ? Number(current !== previous)
336
+ : scale === null
337
+ ? Number(current !== previous)
338
+ : Math.log1p(current / scale) - Math.log1p(previous / scale);
322
339
  distance += difference * difference;
323
340
  used += 1;
324
341
  }
325
- if (used === 0) continue;
342
+ if (used === 0 || shared === 0) continue;
326
343
  distance /= used;
327
344
  if (!best || distance < best.distance || (distance === best.distance && reading.at > best.reading.at)) {
328
345
  best = { distance, reading };
@@ -120,6 +120,7 @@ export class ViewerRequests {
120
120
  ? linkMbps : this.#host.viewers.get(consumerId)?.linkReading()?.linkMbps,
121
121
  linkSampleAgeMs: Number.isFinite(linkSampleAgeMs) && linkSampleAgeMs >= 0 ? linkSampleAgeMs : null,
122
122
  preparedOutputBytes: this.#preparedOutputBytes(sourceKey, fileIndex),
123
+ fileAvailable: null,
123
124
  headerRemaining: null,
124
125
  peers: null
125
126
  };
@@ -163,6 +164,7 @@ export class ViewerRequests {
163
164
  linkSampleAgeMs: Number.isFinite(linkSampleAgeMs) && linkSampleAgeMs >= 0 ? linkSampleAgeMs : null,
164
165
  preparedOutputBytes: Number.isSafeInteger(fileIndex) && fileIndex >= 0
165
166
  ? this.#preparedOutputBytes(sourceKey, fileIndex) : null,
167
+ fileAvailable: typeof stats?.fileAvailable === "boolean" ? stats.fileAvailable : null,
166
168
  headerRemaining: Number.isFinite(stats?.headerBytes) && Number.isFinite(stats?.headerDownloadedBytes)
167
169
  ? Math.max(0, stats.headerBytes - stats.headerDownloadedBytes) : null,
168
170
  peers: stats?.connectedPeers ?? stats?.numPeers