@torrent-tv/proxy 2.97.22 → 2.97.23

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.97.22",
3
+ "version": "2.97.23",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -123,6 +123,8 @@ export class EncodeRun {
123
123
  */
124
124
  #pendingAtStop = null;
125
125
  #closedTimings = new Map();
126
+ /** When the encoder named each closed piece on its channel, until it is published. */
127
+ #namedAt = new Map();
126
128
  #publicationError = null;
127
129
  #publicationCode = null;
128
130
  #inputTruncated = false;
@@ -622,6 +624,7 @@ export class EncodeRun {
622
624
  if (name.length === 0) {
623
625
  continue;
624
626
  }
627
+ this.#namedAt.set(name, this.now());
625
628
  if (this.#publicationError) continue;
626
629
  if (this.#stopping) {
627
630
  // Flushed by our stop. Never published; it tells where the piece closed
@@ -681,7 +684,8 @@ export class EncodeRun {
681
684
  // name a request can actually ask for.
682
685
  let served;
683
686
  try {
684
- served = this.onClosed(name, following, this.#closedTimings.get(name) ?? null, { tentative });
687
+ served = this.onClosed(name, following, this.#closedTimings.get(name) ?? null,
688
+ { tentative, namedAt: this.#namedAt.get(name) ?? null });
685
689
  } catch (error) {
686
690
  if (quiet) {
687
691
  // A piece closed before our stop that cannot be proved whole is not
@@ -702,7 +706,10 @@ export class EncodeRun {
702
706
  } finally {
703
707
  // A piece not proved whole on its own keeps its timing for the file
704
708
  // after it.
705
- if (!tentative || served) this.#closedTimings.delete(name);
709
+ if (!tentative || served) {
710
+ this.#closedTimings.delete(name);
711
+ this.#namedAt.delete(name);
712
+ }
706
713
  }
707
714
 
708
715
  if (!served) return false;
@@ -1121,7 +1121,7 @@ export class EncodeRuns {
1121
1121
  // from it — a re-encoded picture, or sound served as its own output. Such
1122
1122
  // a piece is published on its own line (torrent-tv/meta#178).
1123
1123
  publishesAlone: this.publishesAlone(session),
1124
- onClosed: (name, following, timing, { tentative = false } = {}) => {
1124
+ onClosed: (name, following, timing, { tentative = false, namedAt = null } = {}) => {
1125
1125
 
1126
1126
  const index = session.segmentFormat.segmentIndexFromName(
1127
1127
  session.segmentFormat.servedNameOf?.(name) ?? name);
@@ -1131,6 +1131,8 @@ export class EncodeRuns {
1131
1131
  if (Number.isInteger(index) && (index < safeIndex || index > run.to)) return null;
1132
1132
  let bytes = null;
1133
1133
  const address = session.outputKey ?? "";
1134
+ // Read before anything rewrites it: when the encoder finished it.
1135
+ const writtenAt = this.#host.segmentFiles.writtenAtOf?.(address, name) ?? null;
1134
1136
  const fmp4 = admittedInput && session.segmentFormat.id === "fmp4" && session.spec.carries !== "audio-only";
1135
1137
  // The first piece of a run that read the picture from its keyframe:
1136
1138
  // its leading pictures are the ones the run before made, or none.
@@ -1157,6 +1159,14 @@ export class EncodeRuns {
1157
1159
  const published = this.#host.segmentFiles.publish(address, name, session.segmentFormat, { mediaRanges, bytes });
1158
1160
  if (!published) throw new Error(`Closed piece #${index} could not be stored.`);
1159
1161
  this.#leadingByOutput.get(address)?.delete(index);
1162
+ // WHERE A LATE PIECE'S TIME WENT: the encoder writing it, this process
1163
+ // getting to its name, or its publication waiting for the file after
1164
+ // it. A piece 5.7 s apart from the one before stopped a viewer inside a
1165
+ // running run, and nothing said which (torrent-tv/meta#178).
1166
+ const since = at => (Number.isFinite(at) ? `+${Math.round(at - run.startedAt)}ms` : "unknown");
1167
+ this.#host.logger.info(`encode-run #${run.from}..#${run.to} on ${address}: #${index} written by the encoder ${since(writtenAt)}, ` +
1168
+ `named ${since(namedAt)}, published ${since(run.now())} ` +
1169
+ (following ? `when ${following} closed` : tentative ? "on its own" : "at the run's end"));
1160
1170
  return published;
1161
1171
 
1162
1172
  },
@@ -594,6 +594,23 @@ export class SegmentStore {
594
594
  return readFileSync(path.join(this.#root, directoryNameFor(key), makingName));
595
595
  }
596
596
 
597
+ /**
598
+ * When the encoder last wrote a file it has closed under its working name,
599
+ * on the kernel's clock and so apart from when this process got to it; null
600
+ * when it is not there.
601
+ *
602
+ * @param {string} key
603
+ * @param {string} makingName
604
+ * @returns {number | null}
605
+ */
606
+ writtenAtOf(key, makingName) {
607
+ try {
608
+ return statSync(path.join(this.#root, directoryNameFor(key), makingName)).mtimeMs;
609
+ } catch {
610
+ return null;
611
+ }
612
+ }
613
+
597
614
  /**
598
615
  * The media intervals a stored piece holds, read once from its bytes and kept
599
616
  * with them, or undefined where its format reads none or it is not here.