@torrent-tv/proxy 2.101.40 → 2.101.41

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.40",
3
+ "version": "2.101.41",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -195,11 +195,12 @@ export class PacketIndex {
195
195
  const ordered = packets.ptsAscending;
196
196
  const scanFrom = ordered ? packets.firstAtOrAfter(startAt - packets.longestDuration) : 0;
197
197
  const scanTo = ordered ? packets.firstAtOrAfter(to) : packets.length;
198
- for (let index = scanFrom; index < scanTo; index++) {
199
- const pts = packets.ptsAt(index);
198
+ const consider = (index, pts = packets.ptsAt(index)) => {
200
199
  if (pts < to && (pts >= startAt ||
201
200
  ((mode === "transcode" || track.type === "audio") && pts + packets.durationAt(index) > startAt))) selected.push(index);
202
- }
201
+ };
202
+ if (ordered) for (let index = scanFrom; index < scanTo; index++) consider(index);
203
+ else packets.forEachInTimeRange(startAt, to, consider);
203
204
  if (selected.length === 0) {
204
205
  if (track.type === "subtitle" && (track.complete || track.coveredThrough >= to)) {
205
206
  return { kind: "result", trackId, ranges: [], packets: [], from, to, decodeFrom: from };
@@ -221,7 +222,7 @@ export class PacketIndex {
221
222
  ? { kind: "terminal", reason: "video-interval-has-no-decode-start" }
222
223
  : { kind: "needs-index", trackId, from: startAt, to };
223
224
  let endsAtKeyframe = false;
224
- if (mode === "copy") for (let index = 0; index < packets.length; index++) if (packets.ptsAt(index) === to && packets.keyframeAt(index)) { endsAtKeyframe = true; break; }
225
+ if (mode === "copy") endsAtKeyframe = packets.someAtTime(to, index => packets.keyframeAt(index));
225
226
  if (mode === "copy" && (packets.ptsAt(first) !== from ||
226
227
  (!endsAtKeyframe && (!track.complete || to < this.boundsOf(trackId).end)))) {
227
228
  return { kind: "terminal", reason: "video-copy-cut-is-not-a-keyframe" };
@@ -16,6 +16,8 @@ const HEADER_BYTES = 56;
16
16
 
17
17
  export class PacketRecords {
18
18
  #blocks = [];
19
+ // Conservative time bounds for each existing storage block.
20
+ #timeBounds = [];
19
21
  #addresses = [];
20
22
  #used = 0;
21
23
  #length = 0;
@@ -75,6 +77,7 @@ export class PacketRecords {
75
77
  const previousBytes = this.#bytes;
76
78
  if (value === 0) {
77
79
  this.#blocks = [];
80
+ this.#timeBounds = [];
78
81
  this.#addresses = [];
79
82
  this.#used = 0;
80
83
  this.#bytes = 0;
@@ -85,6 +88,8 @@ export class PacketRecords {
85
88
  const at = address.readUInt32LE(addressAt % BLOCK_BYTES + 4);
86
89
  const block = this.#blocks[blockIndex];
87
90
  this.#blocks.length = blockIndex + 1;
91
+ this.#timeBounds.length = blockIndex + 1;
92
+ this.#timeBounds[blockIndex].end = value;
88
93
  this.#addresses.length = Math.ceil(value * ADDRESS_BYTES / BLOCK_BYTES);
89
94
  // The last address block back to the size its records need, as it grew.
90
95
  const lastAddresses = this.#addresses.at(-1);
@@ -121,6 +126,27 @@ export class PacketRecords {
121
126
  return low;
122
127
  }
123
128
 
129
+ /** Visit stored records whose presentation intervals can overlap [from, to). */
130
+ forEachInTimeRange(from, to, visit) {
131
+ for (const bounds of this.#timeBounds) {
132
+ if (bounds.minPts >= to || (bounds.maxPts < from && bounds.maxEnd <= from)) continue;
133
+ for (let index = bounds.first; index < bounds.end; index++) {
134
+ const pts = this.ptsAt(index);
135
+ if (pts < to && (pts >= from || pts + this.durationAt(index) > from)) visit(index, pts);
136
+ }
137
+ }
138
+ }
139
+
140
+ someAtTime(seconds, matches) {
141
+ for (const bounds of this.#timeBounds) {
142
+ if (seconds < bounds.minPts || seconds > bounds.maxPts) continue;
143
+ for (let index = bounds.first; index < bounds.end; index++) {
144
+ if (this.ptsAt(index) === seconds && matches(index)) return true;
145
+ }
146
+ }
147
+ return false;
148
+ }
149
+
124
150
  push(packet) {
125
151
  if (this.#disposed) throw new Error("Packet records were released.");
126
152
  if (!Number.isFinite(packet.pts) || !Array.isArray(packet.ranges) ||
@@ -161,6 +187,8 @@ export class PacketRecords {
161
187
  if (newBlock) {
162
188
  block = payloadAllocation;
163
189
  this.#blocks.push(block);
190
+ this.#timeBounds.push({ first: this.#length, end: this.#length, minPts: Infinity, maxPts: -Infinity,
191
+ maxEnd: -Infinity });
164
192
  this.#bytes += block.length;
165
193
  this.#used = 0;
166
194
  }
@@ -186,6 +214,11 @@ export class PacketRecords {
186
214
  if (packet.streamId !== undefined) block.writeUInt32LE(packet.streamId, at + bytes - 4 - (packet.decodeFromIndex !== undefined ? 4 : 0));
187
215
  if (packet.decodeFromIndex !== undefined) block.writeUInt32LE(packet.decodeFromIndex, at + bytes - 4);
188
216
  this.#used += bytes;
217
+ const timeBounds = this.#timeBounds.at(-1);
218
+ timeBounds.end = this.#length + 1;
219
+ timeBounds.minPts = Math.min(timeBounds.minPts, packet.pts);
220
+ timeBounds.maxPts = Math.max(timeBounds.maxPts, packet.pts);
221
+ if (Number.isFinite(packet.duration)) timeBounds.maxEnd = Math.max(timeBounds.maxEnd, packet.pts + packet.duration);
189
222
  if (this.#descentAt < 0 && packet.pts < this.#lastPts) this.#descentAt = this.#length;
190
223
  this.#lastPts = packet.pts;
191
224
  if (packet.duration > this.#longest) this.#longest = packet.duration;
@@ -258,6 +291,10 @@ export class PacketRecords {
258
291
  const [block, at] = this.#address(this.#length - 1);
259
292
  const duration = block.readDoubleLE(at + 8) + seconds;
260
293
  block.writeDoubleLE(duration, at + 8);
294
+ const address = this.#addresses[Math.floor((this.#length - 1) * ADDRESS_BYTES / BLOCK_BYTES)];
295
+ const blockIndex = address.readUInt32LE(((this.#length - 1) * ADDRESS_BYTES) % BLOCK_BYTES);
296
+ const bounds = this.#timeBounds[blockIndex];
297
+ bounds.maxEnd = Math.max(bounds.maxEnd, block.readDoubleLE(at) + duration);
261
298
  if (duration > this.#longest) this.#longest = duration;
262
299
  }
263
300
 
@@ -267,6 +304,10 @@ export class PacketRecords {
267
304
  }
268
305
  const [block, at] = this.#address(index);
269
306
  block.writeDoubleLE(seconds, at + 8);
307
+ const address = this.#addresses[Math.floor(index * ADDRESS_BYTES / BLOCK_BYTES)];
308
+ const blockIndex = address.readUInt32LE((index * ADDRESS_BYTES) % BLOCK_BYTES);
309
+ const bounds = this.#timeBounds[blockIndex];
310
+ bounds.maxEnd = Math.max(bounds.maxEnd, block.readDoubleLE(at) + seconds);
270
311
  if (seconds > this.#longest) this.#longest = seconds;
271
312
  const flags = block.readUInt32LE(at + 48);
272
313
  block.writeUInt32LE(derived ? flags | 4 : flags & ~4, at + 48);
@@ -107,6 +107,21 @@ export class PacketRecordsChain {
107
107
  return low;
108
108
  }
109
109
 
110
+ forEachInTimeRange(from, to, visit) {
111
+ for (let part = 0; part < this.#parts.length; part++) {
112
+ const offset = this.#offsets[part];
113
+ this.#parts[part].forEachInTimeRange(from, to, (index, pts) => visit(offset + index, pts));
114
+ }
115
+ }
116
+
117
+ someAtTime(seconds, matches) {
118
+ for (let part = 0; part < this.#parts.length; part++) {
119
+ const offset = this.#offsets[part];
120
+ if (this.#parts[part].someAtTime(seconds, index => matches(offset + index))) return true;
121
+ }
122
+ return false;
123
+ }
124
+
110
125
  /** The tables are the container's: a request's index lets go of nothing. */
111
126
  dispose() {}
112
127
  releaseUnusedCapacity() {}