@torrent-tv/proxy 2.100.1 → 2.100.2

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.100.1",
3
+ "version": "2.100.2",
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
@@ -892,6 +892,7 @@ export async function startProxyServer({
892
892
  const params = await containerOver({ sourceKey: output.file.sourceKey, fileIndex });
893
893
  if (!params) return { kind: "needs-source" };
894
894
  if (timing) {
895
+ params.walkAccount = timing.walk ??= { lockMs: 0, reads: 0, bytes: 0 };
895
896
  params.onTimed = (_statement, waitedMs, readMs) => {
896
897
  timing.reads += 1;
897
898
  timing.queuedMs += waitedMs;
@@ -974,7 +975,9 @@ export async function startProxyServer({
974
975
  const startedAt = Date.now();
975
976
  void container.learnPackets({ from: source.input.from, to: source.input.to, trackIds: [...sounds] }, read).then(learned =>
976
977
  logger.info(`encode input #${from}..#${to} of ${output.outputKey}: soundtrack packets of track(s) ${[...sounds].join(",")} ` +
977
- `over ${source.input.from.toFixed(3)}-${source.input.to.toFixed(3)}s ${learned ? "walked" : "not all walked"} from its copy in ${Date.now() - startedAt}ms`),
978
+ `over ${source.input.from.toFixed(3)}-${source.input.to.toFixed(3)}s ` +
979
+ (learned === "walked" ? "walked" : `not all walked (${learned === "memory" ? "memory refused" : "the copy does not hold the rest"})`) +
980
+ ` from its copy in ${Date.now() - startedAt}ms`),
978
981
  error => logger.warn(`encode input #${from}..#${to} of ${output.outputKey}: walking the soundtrack from its copy failed: ${error?.message ?? error}`));
979
982
  }
980
983
  },
@@ -648,6 +648,9 @@ function describeTiming(timing, now) {
648
648
  `${timing.resolutions} resolution(s) ${timing.resolveMs}ms ` +
649
649
  `(${timing.reads} file read(s): queued ${timing.queuedMs}ms, reading ${timing.readMs}ms), ` +
650
650
  `held ranges ${timing.heldMs}ms, budget ${timing.budgetMs}ms` +
651
+ // Where a packet input's walk spent its read: waiting for the same
652
+ // track's earlier walks, or reading the file (r62, torrent-tv/meta#178).
653
+ (timing.walk ? `; packet walk waited ${timing.walk.lockMs}ms for its track and read ${timing.walk.reads} time(s), ${timing.walk.bytes} bytes of the file` : "") +
651
654
  (timing.openApproximate ? "; sized by the host's startup open, this output has not opened yet" : "");
652
655
  }
653
656
 
@@ -215,7 +215,7 @@ export class ContainerOrchestrator {
215
215
  value = await container.readSourceRanges(params.packetInterval);
216
216
  if (value.kind !== "result") return { ...value, requestId };
217
217
  } else if (statement === "packets") {
218
- value = await container.readPacketIndex(params.packetInterval, { isCurrent: params.isCurrent });
218
+ value = await container.readPacketIndex(params.packetInterval, { isCurrent: params.isCurrent, account: params.walkAccount });
219
219
  if (value === null) return { kind: "terminal", reason: "request-obsolete", requestId };
220
220
  await value?.prepareAudioDependencies?.(params.packetInterval, container.readRange);
221
221
  } else {
@@ -789,8 +789,10 @@ export class MatroskaContainer extends Container {
789
789
  * A walk whose request is no longer current stops between clusters and
790
790
  * answers null. `readRange`, when given, is where this one walk reads
791
791
  * instead of the file's own reader; what it does not hold is unavailable.
792
+ * `account`, when given, is told how long each track's walk waited for
793
+ * that track's earlier walks and what it read (`lockMs`, `reads`, `bytes`).
792
794
  */
793
- async readPacketIndex(interval, { isCurrent, readRange } = {}) {
795
+ async readPacketIndex(interval, { isCurrent, readRange, account } = {}) {
794
796
  if (this.#packets) return this.#packets;
795
797
  const layout = await this.#segmentLayout();
796
798
  if (!layout) throw new Error("Matroska Segment is absent.");
@@ -810,7 +812,7 @@ export class MatroskaContainer extends Container {
810
812
  let whole = true;
811
813
  for (const track of requested) {
812
814
  const chain = await this.#readTrackPackets(layout, tracks, track, start, interval, isCurrent,
813
- readRange ? strictReader(readRange, this.fileSize) : this.readRange);
815
+ readRange ? strictReader(readRange, this.fileSize) : this.readRange, account);
814
816
  if (!chain) return null;
815
817
  streams.set(track.trackNumber, chain.records);
816
818
  if (chain.reachesEnd) complete.add(track.trackNumber);
@@ -835,15 +837,17 @@ export class MatroskaContainer extends Container {
835
837
  *
836
838
  * @param {{ from: number, to: number, trackIds: number[] }} interval
837
839
  * @param {(start: number, end: number) => Buffer | null} readRange - Null where it does not hold the bytes.
838
- * @returns {Promise<boolean>} Whether the interval's packets are recorded.
840
+ * @returns {Promise<"walked" | "missing" | "memory">} Whether the
841
+ * interval's packets are recorded, or what stopped the walk: bytes the
842
+ * copy does not hold, or memory refused.
839
843
  */
840
844
  async learnPackets(interval, readRange) {
841
845
  try {
842
- return await this.readPacketIndex(interval, { readRange }) !== null;
846
+ return await this.readPacketIndex(interval, { readRange }) !== null ? "walked" : "missing";
843
847
  } catch (error) {
844
- // What the copy does not hold, or memory refused: the sound's own walk
845
- // reads it later, from where this one stopped.
846
- if (isUnavailable(error) || error instanceof IndexMemoryUnavailable) return false;
848
+ // The sound's own walk reads the rest later, from where this one stopped.
849
+ if (isUnavailable(error)) return "missing";
850
+ if (error instanceof IndexMemoryUnavailable) return "memory";
847
851
  throw error;
848
852
  }
849
853
  }
@@ -861,13 +865,21 @@ export class MatroskaContainer extends Container {
861
865
  *
862
866
  * @returns {Promise<{ records: PacketRecordsChain, startAt: number, reachesEnd: boolean }>}
863
867
  */
864
- async #readTrackPackets(layout, tracks, track, start, interval, isCurrent, read) {
868
+ async #readTrackPackets(layout, tracks, track, start, interval, isCurrent, reader, account = null) {
865
869
  const id = track.trackNumber;
866
870
  const previous = this.#packetWalks.get(id) ?? Promise.resolve();
867
871
  let finished;
868
872
  const walk = previous.then(() => new Promise(resolve => { finished = resolve; }));
869
873
  this.#packetWalks.set(id, walk);
874
+ const waitedFrom = Date.now();
870
875
  await previous;
876
+ const read = account ? async (start, end) => {
877
+ const bytes = await reader(start, end);
878
+ account.reads += 1;
879
+ account.bytes += bytes.length;
880
+ return bytes;
881
+ } : reader;
882
+ if (account) account.lockMs += Date.now() - waitedFrom;
871
883
  try {
872
884
  const readings = this.#packetReadings.get(id) ?? [];
873
885
  this.#packetReadings.set(id, readings);