@torrent-tv/proxy 2.9.126 → 2.9.127
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/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/services/torrent-worker/client.js +29 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 2.9.127
|
|
2
|
+
|
|
3
|
+
- **New**: A read that hands the file over out of order now says so. A sequential read walks forwards, so each fragment either continues the piece before it or moves to the very next one; anything else means the bytes reaching the decoder are not the file's bytes in order. Measured 2026-08-09 on a 1080p file with an AC-3 track: the encoder ran at 7.7-9.3x, produced its first segment in 9.1 s, reached 00:02:19 of 02:29:58 — and the AC-3 decoder reported "new coupling strategy must be present in block 0", "exponent 26 is out-of-range" and "invalid coupling range" while the piece store showed no spills and 100% of reads served from memory. Video was being COPIED in the same run, so the viewer lost the picture and the sound together: one fault, not two. The bounds check added in 2.9.126 catches a fragment outside the shared pool and stayed silent throughout, so the bytes came from the pool legitimately and belonged somewhere else. This names which piece arrived where.
|
|
4
|
+
|
|
1
5
|
## 2.9.126
|
|
2
6
|
|
|
3
7
|
- **Fix**: A read whose offset lies outside its piece pool ends short and says so, instead of taking the whole source down without a word. The pool is a growable `SharedArrayBuffer` shared with the torrent thread, and an offset only means anything against the buffer of the store that produced it; when the two disagreed, building the view threw `RangeError: Invalid typed array length: 8388608` — one piece — which the process-wide handler swallowed. Reads then stopped answering for good. Measured 2026-08-09: ffmpeg was fed cut-up frames and reported them as a broken AC-3 stream ("new coupling strategy must be present in block 0"), no segment could be closed because no audio frames were produced, and segment #305 was held for a minute eight times running while 76 seeders delivered 35 MB/s. The file was fine; the reads were not. The log line now carries the offset, the length and the pool's size, so a recurrence names its own cause instead of being reconstructed from a decoder's complaints.
|
package/package.json
CHANGED
|
@@ -37,6 +37,14 @@ export class TorrentWorkerClient {
|
|
|
37
37
|
#caller;
|
|
38
38
|
/** Receive-side handles for in-flight reads, keyed by request id. */
|
|
39
39
|
#reads = new Map();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The last piece served to each open read, so a fragment arriving out of
|
|
43
|
+
* order can be named. Cleared when the read ends.
|
|
44
|
+
*
|
|
45
|
+
* @type {Map<number, number>}
|
|
46
|
+
*/
|
|
47
|
+
#lastPieceByRead = new Map();
|
|
40
48
|
/** Each torrent's piece pool, so a fragment can be read where it lies. */
|
|
41
49
|
#poolBySource = new Map();
|
|
42
50
|
/** Which pool an in-flight read belongs to, keyed by request id. */
|
|
@@ -104,6 +112,25 @@ export class TorrentWorkerClient {
|
|
|
104
112
|
this.#worker.postMessage({ type: Event.FRAGMENT_DONE, id: message.id });
|
|
105
113
|
break;
|
|
106
114
|
}
|
|
115
|
+
// A sequential read walks the file forwards, so each fragment either
|
|
116
|
+
// continues the piece before it or moves to the very next one.
|
|
117
|
+
// Anything else means the bytes handed to the decoder are not the
|
|
118
|
+
// file's bytes in order — which is what a decoder complaining about
|
|
119
|
+
// its input has twice turned out to mean (2.9.126, and the AC-3
|
|
120
|
+
// failure of 2026-08-09: the encoder ran at 9.3x, the piece store
|
|
121
|
+
// reported no spills and 100% of reads from memory, and the decoder
|
|
122
|
+
// still saw "new coupling strategy must be present in block 0"). The
|
|
123
|
+
// bounds check catches a fragment outside the pool; this catches one
|
|
124
|
+
// inside it that belongs somewhere else.
|
|
125
|
+
const lastPiece = this.#lastPieceByRead.get(message.id);
|
|
126
|
+
if (lastPiece !== undefined && message.pieceIndex !== lastPiece && message.pieceIndex !== lastPiece + 1) {
|
|
127
|
+
logger.warn(
|
|
128
|
+
`torrent-worker: read ${message.id} jumped from piece ${lastPiece} to ` +
|
|
129
|
+
`${message.pieceIndex} (${message.length}B at pool offset ${message.offset}) — ` +
|
|
130
|
+
"the consumer is being handed the file out of order"
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
this.#lastPieceByRead.set(message.id, message.pieceIndex);
|
|
107
134
|
const view = new Uint8Array(pool, message.offset, message.length);
|
|
108
135
|
|
|
109
136
|
const reader = this.#fragmentReaders.get(message.id);
|
|
@@ -132,6 +159,7 @@ export class TorrentWorkerClient {
|
|
|
132
159
|
break;
|
|
133
160
|
}
|
|
134
161
|
case Event.READ_END:
|
|
162
|
+
this.#lastPieceByRead.delete(message.id);
|
|
135
163
|
this.#reads.get(message.id)?.close();
|
|
136
164
|
this.#reads.delete(message.id);
|
|
137
165
|
this.#fragmentReaders.get(message.id)?.close();
|
|
@@ -260,6 +288,7 @@ export class TorrentWorkerClient {
|
|
|
260
288
|
void this.#caller.call(Command.CANCEL_READ, { readId }).catch(() => undefined);
|
|
261
289
|
this.#reads.delete(readId);
|
|
262
290
|
this.#poolByRead.delete(readId);
|
|
291
|
+
this.#lastPieceByRead.delete(readId);
|
|
263
292
|
}
|
|
264
293
|
});
|
|
265
294
|
this.#reads.set(readId, receive);
|