@torrent-tv/proxy 2.107.11 → 2.107.13

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.11",
3
+ "version": "2.107.13",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
package/server.js CHANGED
@@ -332,16 +332,20 @@ export async function startProxyServer({
332
332
  /** @type {Record<string, object[]>} */
333
333
  const calibratedModes = {};
334
334
  if (videoEncoder) {
335
- calibratedModes[videoEncoder.kind] =
336
- (await calibrateEncoder({ ffmpegBin, encoder: videoEncoder, logger, processorCores })).modes;
335
+ const selectedCalibration = await calibrateEncoder({ ffmpegBin, encoder: videoEncoder, logger, processorCores });
336
+ if (selectedCalibration.assessmentComplete) {
337
+ calibratedModes[videoEncoder.kind] = selectedCalibration.modes;
338
+ }
337
339
  if (videoEncoder.kind !== "software") {
338
- calibratedModes.software =
339
- (await calibrateEncoder({ ffmpegBin, encoder: softwareDescriptor(), logger, processorCores })).modes;
340
+ const softwareCalibration = await calibrateEncoder({ ffmpegBin, encoder: softwareDescriptor(), logger, processorCores });
341
+ if (softwareCalibration.assessmentComplete) {
342
+ calibratedModes.software = softwareCalibration.modes;
343
+ }
340
344
  // A device that passed detection's own test and then none of whose modes
341
345
  // passed the product's is not used: detection encodes a synthetic pattern
342
346
  // with arguments of its own, and it is the product's arguments a viewer
343
347
  // is served with.
344
- if (calibratedModes[videoEncoder.kind].length === 0) {
348
+ if (Array.isArray(calibratedModes[videoEncoder.kind]) && calibratedModes[videoEncoder.kind].length === 0) {
345
349
  logger.warn(
346
350
  `calibration: ${videoEncoder.name} passed detection but none of its modes produced correct segments ` +
347
351
  "through the product's own arguments; encoding in software instead"
@@ -190,7 +190,7 @@ async function checkModeProducesCorrectSegments({ ffmpegBin, encoder, rung, segm
190
190
  * @param {object} [params.measure] - Replaces the three readings, for a check
191
191
  * that must not run ffmpeg: `check`, `rawFrames`, `speed`. `speed` answers a
192
192
  * number, or `{ speed, freeShare }` as `measureEncodeSlope` does.
193
- * @returns {Promise<{ modes: CalibratedMode[], refused: Array<{ preset: string, reason: string }> }>}
193
+ * @returns {Promise<{ modes: CalibratedMode[], refused: Array<{ preset: string, reason: string }>, assessmentComplete: boolean }>}
194
194
  */
195
195
  export async function calibrateEncoder({
196
196
  ffmpegBin,
@@ -227,6 +227,11 @@ export async function calibrateEncoder({
227
227
  }
228
228
  }
229
229
  const qualified = rungs.filter((rung) => checked.get(rung)?.ok === true);
230
+ // A mode that failed the correctness check is a proven refusal. Otherwise,
231
+ // the host must produce at least one throughput reading before an empty
232
+ // mode list can mean that no output is sustainable. A missing/unfinished
233
+ // calibration input is not evidence that the host cannot encode.
234
+ let assessmentComplete = qualified.length === 0;
230
235
 
231
236
  /** @type {Map<string | null, SizeReading[]>} */
232
237
  const readings = new Map(qualified.map((rung) => [rung, []]));
@@ -264,6 +269,7 @@ export async function calibrateEncoder({
264
269
  activeRungs.delete(rung);
265
270
  continue;
266
271
  }
272
+ assessmentComplete = true;
267
273
  readings.get(rung).push({
268
274
  width: frame.width,
269
275
  height: frame.height,
@@ -319,7 +325,9 @@ export async function calibrateEncoder({
319
325
  );
320
326
  }
321
327
  log.info(
322
- `calibration: ${encoder.name} ${modes.length} of ${rungs.length} mode(s) usable ` +
328
+ `calibration: ${encoder.name} ${assessmentComplete
329
+ ? `${modes.length} of ${rungs.length} mode(s) usable`
330
+ : "performance was not measured"} ` +
323
331
  `(${((Date.now() - startedAt) / 1000).toFixed(1)}s)` +
324
332
  (freeShares.length > 0
325
333
  ? `; other work took ${Math.round((1 - Math.max(...freeShares)) * 100)}-${Math.round((1 - Math.min(...freeShares)) * 100)}% ` +
@@ -327,7 +335,7 @@ export async function calibrateEncoder({
327
335
  : encoder.kind === "software" ? "; the machine's load during the readings was not readable, so the figures are as read" : "") +
328
336
  (refused.length > 0 ? `; not usable: ${refused.map((one) => `${one.preset} (${one.reason})`).join(", ")}` : "")
329
337
  );
330
- return { modes, refused };
338
+ return { modes, refused, assessmentComplete };
331
339
  }
332
340
 
333
341
  /**
@@ -228,8 +228,14 @@ export class PacketIndex {
228
228
  return { kind: "terminal", reason: "video-copy-cut-is-not-a-keyframe" };
229
229
  }
230
230
  }
231
- // Coverage is established by the parser after its codec-required lookahead;
232
- // packets outside the interval are inspected but are not appended to input.
231
+ // A transcoder needs the first audio packet at or after the cut to flush
232
+ // samples decoded from the packet before it. The output filter still trims
233
+ // that lookahead to the requested interval. Video and copied audio do not
234
+ // need a packet beyond the cut.
235
+ if (track.type === "audio" && mode === "transcode" && ordered) {
236
+ const trailing = packets.firstAtOrAfter(to);
237
+ if (trailing < packets.length) last = Math.max(last, trailing);
238
+ }
233
239
  if (!track.complete && track.coveredThrough < to) {
234
240
  return { kind: "needs-index", trackId, from: startAt, to };
235
241
  }