@torrent-tv/proxy 2.9.112 → 2.9.113
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/data-channel-handler.js +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 2.9.113
|
|
2
|
+
|
|
3
|
+
- **New**: The transport's own counters are written to the log for as long as a channel is open, not only when its send queue backs up. The queue was the wrong thing to watch: field 2026-08-06, a 9.26 MB segment was accepted by the transport with `maxBuffered=0 bufferedAtEnd=0` and reported as sent at 274 Mbit/s, and it never arrived — after which everything the proxy sent vanished the same way while requests kept arriving in the other direction. With nothing ever queued the existing watcher never woke, so the one question that decides the cause — did those bytes leave the machine — had no answer in the log. Every five seconds it now records bytes sent and received by the transport itself, the queue depth, the round-trip time, and the path in use. The browser records the matching figures on the same cadence (server 0.8.110), so a recurrence is settled by subtracting one line from the other rather than by reasoning.
|
|
4
|
+
|
|
1
5
|
## 2.9.112
|
|
2
6
|
|
|
3
7
|
- **New**: A session whose data went away now waits for it to come back instead of dying. Losing the input is not the session failing — the torrent can be added again and the pieces downloaded again — but a run that died that way marked the session terminal, and every request for the playlist answered 500 from then on, although the swarm was right there and the data would have returned in seconds. Such a run is now retried at the position the viewer is waiting at, backing off from 2 s to at most 15 s so a source that is genuinely unavailable costs a process every few seconds rather than continuously, and the requests being held are simply held: nothing is broken and there is nothing for the viewer to retry. The circuit breaker stays for what it was built for — a target that truly cannot be encoded — and no longer condemns a session that merely lost its data. Which of the two happened is decided by the message, tested against the exact ones the field produced.
|
package/package.json
CHANGED
|
@@ -107,8 +107,42 @@ function makeSendQueueWatcher({ log, getTransportSnapshot }) {
|
|
|
107
107
|
let lowestSinceDrain = Number.POSITIVE_INFINITY;
|
|
108
108
|
let stuckSince = 0;
|
|
109
109
|
let previous = null;
|
|
110
|
+
// Independent of the queue: the transport's own counters, sampled for as
|
|
111
|
+
// long as the channel is open. The queue was the wrong thing to watch —
|
|
112
|
+
// field 2026-08-06, a 9.26 MB segment was accepted by the transport with
|
|
113
|
+
// `maxBuffered=0 bufferedAtEnd=0`, reported as sent at 274 Mbit/s, and
|
|
114
|
+
// never arrived; everything the proxy sent from that moment on was lost the
|
|
115
|
+
// same way while requests kept coming the other direction. With nothing
|
|
116
|
+
// queued this watcher never woke, so the one question that matters — did
|
|
117
|
+
// those bytes leave the machine — has no answer in the log. It does now.
|
|
118
|
+
let heartbeatPrevious = null;
|
|
119
|
+
let heartbeatAt = 0;
|
|
110
120
|
|
|
111
121
|
const timer = setInterval(() => {
|
|
122
|
+
const sampledAt = Date.now();
|
|
123
|
+
if (sampledAt - heartbeatAt >= TRANSPORT_HEARTBEAT_MS) {
|
|
124
|
+
heartbeatAt = sampledAt;
|
|
125
|
+
const snapshot = getTransportSnapshot?.(sessionId) ?? null;
|
|
126
|
+
if (snapshot) {
|
|
127
|
+
const sent = heartbeatPrevious ? snapshot.bytesSent - heartbeatPrevious.bytesSent : null;
|
|
128
|
+
const received = heartbeatPrevious
|
|
129
|
+
? snapshot.bytesReceived - heartbeatPrevious.bytesReceived
|
|
130
|
+
: null;
|
|
131
|
+
heartbeatPrevious = snapshot;
|
|
132
|
+
let depth = 0;
|
|
133
|
+
try {
|
|
134
|
+
depth = typeof channel.bufferedAmount === "function" ? channel.bufferedAmount() : 0;
|
|
135
|
+
} catch {
|
|
136
|
+
depth = -1;
|
|
137
|
+
}
|
|
138
|
+
log(
|
|
139
|
+
`[dc-transport] ${tag} "${label}" sent=${snapshot.bytesSent}` +
|
|
140
|
+
`${sent === null ? "" : ` (+${sent})`} received=${snapshot.bytesReceived}` +
|
|
141
|
+
`${received === null ? "" : ` (+${received})`} queued=${depth}B ` +
|
|
142
|
+
`rtt=${snapshot.rtt}ms pc=${snapshot.state} ice=${snapshot.iceState} pair=${snapshot.pair}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
112
146
|
let queued = 0;
|
|
113
147
|
try {
|
|
114
148
|
queued = typeof channel.bufferedAmount === "function" ? channel.bufferedAmount() : 0;
|
|
@@ -630,6 +664,10 @@ const PARTIAL_REQUEST_TTL_MS = 60_000;
|
|
|
630
664
|
// well under a second on the LAN — and short enough that a stuck channel is
|
|
631
665
|
// named while the viewer is still looking at it.
|
|
632
666
|
const SEND_QUEUE_SAMPLE_MS = 1_000;
|
|
667
|
+
// How often the transport's own counters are written to the log, whatever the
|
|
668
|
+
// send queue is doing. Frequent enough to place a loss within a few seconds,
|
|
669
|
+
// sparse enough that a two-hour film costs a few hundred lines.
|
|
670
|
+
const TRANSPORT_HEARTBEAT_MS = 5_000;
|
|
633
671
|
const SEND_QUEUE_STUCK_MS = 5_000;
|
|
634
672
|
const DC_BUFFER_HIGH_WATER = 8 * 1024 * 1024;
|
|
635
673
|
/** Resume sending once the channel buffer drains to this many bytes. */
|