@torrent-tv/proxy 2.98.2 → 2.98.3

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.98.2",
3
+ "version": "2.98.3",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -54,20 +54,56 @@ export async function* matroskaInput({ tracks, readPacket, originSeconds = 0, in
54
54
  slices = avcPacketSlices(slices);
55
55
  payloadBytes = slices.reduce((sum, slice) => sum + slice.length, 0);
56
56
  }
57
- const blockHeader = Buffer.concat([vint(selected + 1), Buffer.from([0, 0, 0])]);
57
+ // EVERY PACKET'S CLUSTER IS WRITTEN INTO ONE BUFFER, its sizes worked out
58
+ // first: an element at a time, each id parsed from hex, each number split
59
+ // into bytes through BigInt and every part joined again, this writer was
60
+ // 6 % of the main thread over a two-hour AVI on Home Assistant (r30,
61
+ // torrent-tv/meta#178). The bytes are the same.
58
62
  const duration = Math.round(packet.duration * 1e6);
59
- const afterBlock = Buffer.concat([
60
- ...(duration > 0 ? [uintElement("9b", duration)] : []),
61
- ...(packet.discardPaddingSeconds ? [signedElement("75a2", Math.round(packet.discardPaddingSeconds * 1e9))] : []),
62
- ...(packet.keyframe ? [] : [element("fb", Buffer.from([0]))])]);
63
- const blockPrefix = prefix("a1", blockHeader.length + payloadBytes);
64
- const groupPrefix = prefix("a0", blockPrefix.length + blockHeader.length + payloadBytes + afterBlock.length);
65
- const timestamp = uintElement("e7", stamp);
66
- const clusterPrefix = prefix("1f43b675", timestamp.length + groupPrefix.length + blockPrefix.length +
67
- blockHeader.length + payloadBytes + afterBlock.length);
68
- yield Buffer.concat([clusterPrefix, timestamp, groupPrefix, blockPrefix, blockHeader]);
63
+ const padding = packet.discardPaddingSeconds ? Math.round(packet.discardPaddingSeconds * 1e9) : null;
64
+ if (duration > 0 && !Number.isSafeInteger(duration)) throw new TypeError("EBML unsigned integer is invalid.");
65
+ if (padding !== null && !Number.isSafeInteger(padding)) throw new TypeError("EBML signed integer is outside its exact range.");
66
+ const blockHeaderBytes = vintWidth(selected + 1) + 3;
67
+ const durationWidth = duration > 0 ? uintWidth(duration) : 0;
68
+ const afterBytes = (duration > 0 ? 1 + vintWidth(durationWidth) + durationWidth : 0) +
69
+ (padding !== null ? 2 + 1 + 8 : 0) + (packet.keyframe ? 0 : 3);
70
+ const blockSize = blockHeaderBytes + payloadBytes;
71
+ const blockPrefixBytes = 1 + vintWidth(blockSize);
72
+ const groupSize = blockPrefixBytes + blockSize + afterBytes;
73
+ const groupPrefixBytes = 1 + vintWidth(groupSize);
74
+ const stampWidth = uintWidth(stamp);
75
+ const timestampBytes = 1 + vintWidth(stampWidth) + stampWidth;
76
+ const clusterSize = timestampBytes + groupPrefixBytes + groupSize;
77
+ const head = Buffer.allocUnsafe(4 + vintWidth(clusterSize) + timestampBytes + groupPrefixBytes + blockPrefixBytes + blockHeaderBytes);
78
+ let at = head.writeUInt32BE(0x1f43b675, 0);
79
+ at = writeVint(head, at, clusterSize);
80
+ head[at++] = 0xe7;
81
+ at = writeVint(head, at, stampWidth);
82
+ at = writeUint(head, at, stamp, stampWidth);
83
+ head[at++] = 0xa0;
84
+ at = writeVint(head, at, groupSize);
85
+ head[at++] = 0xa1;
86
+ at = writeVint(head, at, blockSize);
87
+ at = writeVint(head, at, selected + 1);
88
+ head[at++] = 0; head[at++] = 0; head[at++] = 0;
89
+ yield head;
69
90
  for (const slice of slices) yield slice;
70
- if (afterBlock.length) yield afterBlock;
91
+ if (afterBytes > 0) {
92
+ const after = Buffer.allocUnsafe(afterBytes);
93
+ let offset = 0;
94
+ if (duration > 0) {
95
+ after[offset++] = 0x9b;
96
+ offset = writeVint(after, offset, durationWidth);
97
+ offset = writeUint(after, offset, duration, durationWidth);
98
+ }
99
+ if (padding !== null) {
100
+ offset = after.writeUInt16BE(0x75a2, offset);
101
+ offset = writeVint(after, offset, 8);
102
+ offset = after.writeBigInt64BE(BigInt(padding), offset);
103
+ }
104
+ if (!packet.keyframe) { after[offset++] = 0xfb; after[offset++] = 0x81; after[offset++] = 0; }
105
+ yield after;
106
+ }
71
107
  }
72
108
  }
73
109
 
@@ -128,31 +164,52 @@ function matroskaCodec(codec) {
128
164
  return declared;
129
165
  }
130
166
 
131
- function vint(value) {
167
+ /** How many bytes an EBML size of this value takes: the narrowest whose all-ones value it is below. */
168
+ function vintWidth(value) {
132
169
  if (!Number.isSafeInteger(value) || value < 0) throw new TypeError("EBML value is outside its integer range.");
133
- for (let width = 1; width <= 8; width++) {
134
- if (BigInt(value) >= (1n << BigInt(7 * width)) - 1n) continue;
135
- const encoded = Buffer.alloc(width);
136
- let bits = BigInt(value) | (1n << BigInt(7 * width));
137
- for (let at = width - 1; at >= 0; at--) { encoded[at] = Number(bits & 255n); bits >>= 8n; }
138
- return encoded;
170
+ // 2 ** 56 - 1 is above every safe integer, so eight bytes always hold one.
171
+ for (let width = 1; width < 8; width++) if (value < 2 ** (7 * width) - 1) return width;
172
+ return 8;
173
+ }
174
+
175
+ /** The value as an EBML size at `at`, its width's marker bit set; the next offset. */
176
+ function writeVint(target, at, value) {
177
+ const width = vintWidth(value);
178
+ writeUint(target, at, value, width);
179
+ target[at] |= 1 << (8 - width);
180
+ return at + width;
181
+ }
182
+
183
+ /** How many bytes an unsigned integer takes, one at least. */
184
+ function uintWidth(value) {
185
+ let width = 1;
186
+ for (let rest = Math.floor(value / 256); rest > 0; rest = Math.floor(rest / 256)) width += 1;
187
+ return width;
188
+ }
189
+
190
+ /** The value big-endian in `width` bytes at `at`; the next offset. */
191
+ function writeUint(target, at, value, width) {
192
+ let rest = value;
193
+ for (let index = width - 1; index >= 0; index--) {
194
+ target[at + index] = rest % 256;
195
+ rest = Math.floor(rest / 256);
139
196
  }
140
- throw new TypeError("EBML value has no valid width.");
197
+ return at + width;
198
+ }
199
+
200
+ function vint(value) {
201
+ const encoded = Buffer.alloc(vintWidth(value));
202
+ writeVint(encoded, 0, value);
203
+ return encoded;
141
204
  }
142
205
 
143
206
  function prefix(id, size) { return Buffer.concat([Buffer.from(id, "hex"), vint(size)]); }
144
207
  function element(id, payload) { return Buffer.concat([prefix(id, payload.length), payload]); }
145
208
  function textElement(id, text) { return element(id, Buffer.from(text, "utf8")); }
146
- function signedElement(id, value) {
147
- if (!Number.isSafeInteger(value)) throw new TypeError("EBML signed integer is outside its exact range.");
148
- const bytes = Buffer.alloc(8);
149
- bytes.writeBigInt64BE(BigInt(value));
150
- return element(id, bytes);
151
- }
152
209
  function uintElement(id, value) {
153
210
  if (!Number.isSafeInteger(value) || value < 0) throw new TypeError("EBML unsigned integer is invalid.");
154
- let bits = BigInt(value);
155
- const bytes = [];
156
- do { bytes.unshift(Number(bits & 255n)); bits >>= 8n; } while (bits > 0n);
157
- return element(id, Buffer.from(bytes));
211
+ const width = uintWidth(value);
212
+ const bytes = Buffer.alloc(width);
213
+ writeUint(bytes, 0, value, width);
214
+ return element(id, bytes);
158
215
  }