@torrent-tv/proxy 2.30.1 → 2.30.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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## 2.30.2
|
|
2
|
+
|
|
3
|
+
- **Fix**: The cut-time shift of 2.28.0 is reverted — the field measured it and it moved the cuts OFF the source's keyframes rather than onto them. Of 75 pieces the picture produced afterwards, only **nine** began at a time the container's own table names, against **70 of 75** for the soundtrack, which the change never touched; the median distance from the playlist went from 0.04 s to 4.33 s. Before it, every piece began exactly on a named keyframe and it was the playlist that disagreed with them — which is the correction path's business, not the cut list's. The reasoning that produced the shift (that the muxer decides its cuts before the output is relabelled) was argued from ffmpeg's semantics rather than measured, and the measurement says otherwise.
|
|
4
|
+
- **Fix**: The steering line compared two different things. `steered onto N of M holders` summed the successes over every attempt of a wait while taking M from the last attempt alone, which is how the log came to read `steered onto 12 of 6 holders`. Both halves are now totals over the same attempts: `steered onto N of M asks (K peers held it)`.
|
|
5
|
+
|
|
1
6
|
## 2.30.1
|
|
2
7
|
|
|
3
8
|
- **Fix**: A seek was undone a second after it was made. Measured 2026-08-17: the viewer jumped to 2083.4 s, both runs restarted at segment #373 — correctly — and then a request for #371, issued by the player BEFORE the jump and reissued a second later, dragged the encoder back to #370. The viewer sat at #374 waiting for it to return. Two things let that happen, and both are fixed. The behind-head repair refuses a request that is behind the position the VIEWER themselves reported: its existing guard only holds while a seek is still settling, which by then it was not. And a segment request may no longer move the recorded viewer position BACKWARDS past a reported seek — playback only ever moves forward from one, so nothing legitimate is lost, while a stale request can no longer rewrite the viewer's own statement, which is how the repair came to believe it. A reported seek is the viewer stating where they are; a request is evidence about where the player is reading, and evidence may refine a statement forward, never contradict it backwards. Pinned by `test/stale-request-after-seek.test.js`, whose control case shows the same traffic still repairing a genuinely misplaced run when the viewer has said nothing.
|
package/package.json
CHANGED
|
@@ -3567,30 +3567,22 @@ export class HlsSessionManager {
|
|
|
3567
3567
|
const gridCutTimes = explicitTimes && (!session.transcodeVideo || session.cutGrid === "keyframe")
|
|
3568
3568
|
? segmentCutTimesFrom(session.segmentBoundaries, safeIndex)
|
|
3569
3569
|
: null;
|
|
3570
|
-
//
|
|
3571
|
-
// timestamps, not against the labels we ask it to write. That branch keeps
|
|
3572
|
-
// the source's timestamps (`-copyts`) and re-labels the output 0-based with
|
|
3573
|
-
// `-output_ts_offset -sourceStartTime`; the cut list, being applied before
|
|
3574
|
-
// that relabelling, must therefore be stated in the SOURCE's terms.
|
|
3570
|
+
// Cut times are stated on the grid, for both branches.
|
|
3575
3571
|
//
|
|
3576
|
-
//
|
|
3577
|
-
//
|
|
3578
|
-
//
|
|
3579
|
-
//
|
|
3580
|
-
//
|
|
3581
|
-
//
|
|
3582
|
-
//
|
|
3583
|
-
//
|
|
3584
|
-
// playlist and the media drifted apart by a whole segment and the player
|
|
3585
|
-
// refetched what it could not place.
|
|
3572
|
+
// 2.28.0 added `sourceStartTime` to them on the copy branch, reasoning that
|
|
3573
|
+
// the muxer decides its cuts before the output is relabelled. The field
|
|
3574
|
+
// measured it the next session and the reasoning was wrong: of 75 pieces
|
|
3575
|
+
// the picture produced, only NINE began at a time the container's own
|
|
3576
|
+
// keyframe table names (the soundtrack, untouched by the change, scored 70
|
|
3577
|
+
// of 75). Before it, every piece began exactly on a named keyframe and it
|
|
3578
|
+
// was the PLAYLIST that disagreed with them. So the shift moved the cuts
|
|
3579
|
+
// OFF the keyframes rather than onto them, and it is gone.
|
|
3586
3580
|
//
|
|
3587
|
-
//
|
|
3588
|
-
//
|
|
3589
|
-
//
|
|
3590
|
-
//
|
|
3591
|
-
const cutTimes = gridCutTimes
|
|
3592
|
-
? gridCutTimes.map((time) => Number((time + sourceStartTime).toFixed(6)))
|
|
3593
|
-
: gridCutTimes;
|
|
3581
|
+
// What remains true, and is what that measurement is really about: the
|
|
3582
|
+
// picture cuts where the source's keyframes are, and the playlist must be
|
|
3583
|
+
// built from those same times. That is the correction path's job, not the
|
|
3584
|
+
// cut list's.
|
|
3585
|
+
const cutTimes = gridCutTimes;
|
|
3594
3586
|
|
|
3595
3587
|
// A second chance for a predecessor that survived the escalation above —
|
|
3596
3588
|
// the first block is the one that does the work. Its exit is ignored
|
|
@@ -87,7 +87,7 @@ function speedOf(wire) {
|
|
|
87
87
|
* @param {object} torrent
|
|
88
88
|
* @param {number} pieceIndex
|
|
89
89
|
* @param {number} [limit] - How many wires to push it onto.
|
|
90
|
-
* @returns {{ asked: number, considered: number, fastestBytesPerSecond: number }}
|
|
90
|
+
* @returns {{ asked: number, attempted: number, considered: number, fastestBytesPerSecond: number }}
|
|
91
91
|
* `asked` counts requests the library actually placed: it refuses when a
|
|
92
92
|
* wire's pipeline is full or when nothing can be reserved even with hotswap,
|
|
93
93
|
* and that refusal is information — a piece nobody can be asked for is
|
|
@@ -95,7 +95,7 @@ function speedOf(wire) {
|
|
|
95
95
|
*/
|
|
96
96
|
export function askFastestWiresFor(torrent, pieceIndex, limit = 3) {
|
|
97
97
|
if (!canPlaceRequests(torrent) || !Number.isInteger(pieceIndex) || pieceIndex < 0) {
|
|
98
|
-
return { asked: 0, considered: 0, fastestBytesPerSecond: 0 };
|
|
98
|
+
return { asked: 0, attempted: 0, considered: 0, fastestBytesPerSecond: 0 };
|
|
99
99
|
}
|
|
100
100
|
const candidates = wiresForPiece(torrent, pieceIndex);
|
|
101
101
|
let asked = 0;
|
|
@@ -116,6 +116,13 @@ export function askFastestWiresFor(torrent, pieceIndex, limit = 3) {
|
|
|
116
116
|
return {
|
|
117
117
|
asked,
|
|
118
118
|
considered: candidates.length,
|
|
119
|
+
// How many of the asks the library placed, against how many it was asked
|
|
120
|
+
// for. The caller sums these over the whole wait, and summing `asked`
|
|
121
|
+
// against a `considered` taken from the LAST attempt is how the field log
|
|
122
|
+
// came to read "steered onto 12 of 6 holders" — a ratio of two different
|
|
123
|
+
// things. Both halves are returned per attempt so the caller can add each
|
|
124
|
+
// to its own total.
|
|
125
|
+
attempted: Math.min(candidates.length, Math.max(1, limit)),
|
|
119
126
|
fastestBytesPerSecond: candidates.length > 0 ? speedOf(candidates[0]) : 0
|
|
120
127
|
};
|
|
121
128
|
}
|
|
@@ -517,12 +517,15 @@ export async function* readFragments({
|
|
|
517
517
|
// holder delivers — measured 2026-08-17, the swarm had a fivefold surplus
|
|
518
518
|
// of bandwidth and the reader still waited 1.0-4.5 s, 47 times in two
|
|
519
519
|
// minutes, on pieces five peers already had.
|
|
520
|
-
let pushed = { asked: 0, considered: 0, fastestBytesPerSecond: 0 };
|
|
520
|
+
let pushed = { asked: 0, attempted: 0, considered: 0, fastestBytesPerSecond: 0 };
|
|
521
521
|
const pushToFastest = () => {
|
|
522
522
|
try {
|
|
523
523
|
const result = askFastestWiresFor(torrent, pieceIndex);
|
|
524
524
|
pushed = {
|
|
525
525
|
asked: pushed.asked + result.asked,
|
|
526
|
+
// Summed like the successes, so the line compares two totals over
|
|
527
|
+
// the same attempts instead of a total against a snapshot.
|
|
528
|
+
attempted: (pushed.attempted ?? 0) + result.attempted,
|
|
526
529
|
considered: result.considered,
|
|
527
530
|
fastestBytesPerSecond: result.fastestBytesPerSecond
|
|
528
531
|
};
|
|
@@ -592,7 +595,7 @@ export async function* readFragments({
|
|
|
592
595
|
// What WE did about it, so the next session says whether steering
|
|
593
596
|
// the piece onto faster holders shortens the tail — by number
|
|
594
597
|
// rather than by impression.
|
|
595
|
-
`; steered onto ${pushed.asked} of ${pushed.considered}
|
|
598
|
+
`; steered onto ${pushed.asked} of ${pushed.attempted} asks (${pushed.considered} peers held it)` +
|
|
596
599
|
(pushed.fastestBytesPerSecond > 0
|
|
597
600
|
? `, fastest ${Math.round(pushed.fastestBytesPerSecond / 1024)}KB/s`
|
|
598
601
|
: "")
|
|
@@ -97,7 +97,7 @@ test("a build without the request entry is reported, not silently skipped", () =
|
|
|
97
97
|
assert.equal(canPlaceRequests({ wires: [] }), false);
|
|
98
98
|
assert.equal(canPlaceRequests({ wires: [], _request: () => true }), true);
|
|
99
99
|
const result = askFastestWiresFor({ wires: [wire({ speed: 1 })] }, 1);
|
|
100
|
-
assert.deepEqual(result, { asked: 0, considered: 0, fastestBytesPerSecond: 0 });
|
|
100
|
+
assert.deepEqual(result, { asked: 0, attempted: 0, considered: 0, fastestBytesPerSecond: 0 });
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
test("a wire that cannot say how fast it is ranks last rather than throwing", () => {
|