@torrent-tv/proxy 2.9.114 → 2.9.115
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 +1 -1
- package/package.json +1 -1
- package/services/hls-session-manager.js +18 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
## 2.9.114
|
|
2
2
|
|
|
3
3
|
- **Fix**: A seek leaked a pinned piece, and enough of them destroyed the torrent. A piece is pinned before its fragment is handed to the reader and released by whoever received it — but a consumer that ABANDONS the read never gets the chance, and a seek abandons it every time: the encoder is killed, the response is torn down, the loop is left between two fragments. Field 2026-08-06, one seek was enough: the store answered `Every resident piece is pinned; no slot can be freed`, and it answered it to the WebTorrent client, which closed the store and destroyed the torrent — after which every read failed with `File 0 not found`, the session went terminal, and the segment the viewer was waiting for returned an instant error. The pin of a fragment still in the consumer's hands is now dropped by the reader itself on every exit, abandonment included. Covered by a test that abandons a read mid-fragment and checks the store has nothing pinned.
|
|
4
|
-
- **Chore**: The look-ahead
|
|
4
|
+
- **Chore**: The look-ahead reports that ffmpeg's position and the segments on disk disagree on the EDGES of that state — once when they part company, once when they meet again, with how long it lasted. It was printed per call of a function that runs on every segment request, which after a seek meant three hundred times a minute; a rate limit would only have hidden that the line was in the wrong place, and it could not have said how long the disagreement went on.
|
|
5
5
|
|
|
6
6
|
## 2.9.113
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -162,9 +162,6 @@ const MAX_SEEK_FAILURES = 3;
|
|
|
162
162
|
// wait, not a verdict. Backed off so a source that is truly unavailable costs a
|
|
163
163
|
// process every few seconds rather than continuously, and never given up on —
|
|
164
164
|
// the session's own idle TTL is what ends it if the viewer leaves.
|
|
165
|
-
// How often the look-ahead may report that ffmpeg's position and the segments
|
|
166
|
-
// on disk disagree. It is a diagnostic, not an event.
|
|
167
|
-
const LOOKAHEAD_DISAGREEMENT_LOG_MS = 60_000;
|
|
168
165
|
const INPUT_RETRY_BASE_MS = 2_000;
|
|
169
166
|
const INPUT_RETRY_MAX_MS = 15_000;
|
|
170
167
|
// Idle TTL: a session is disposed this long after the last segment/playlist
|
|
@@ -1847,22 +1844,29 @@ export class HlsSessionManager {
|
|
|
1847
1844
|
}
|
|
1848
1845
|
// Worth knowing when the two disagree wildly — it is the only trace of
|
|
1849
1846
|
// whatever made ffmpeg report a position it had not reached.
|
|
1847
|
+
// Reported on its EDGES, because it is a state and not a stream: it starts
|
|
1848
|
+
// when the two figures part company and ends when they meet again, however
|
|
1849
|
+
// often this function happens to run. Printing it per call meant printing
|
|
1850
|
+
// it at the rate of segment requests — three hundred times a minute after
|
|
1851
|
+
// one seek — and a limit of one a minute would only have hidden that the
|
|
1852
|
+
// line was in the wrong place. Two lines per occurrence also say how long
|
|
1853
|
+
// it lasted, which no sampled version could.
|
|
1850
1854
|
const claimed = Number(session.progress?.processedSeconds);
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
const now = Date.now();
|
|
1856
|
-
if (
|
|
1857
|
-
Number.isFinite(claimed) &&
|
|
1858
|
-
Math.abs(claimed - encodedTo) > LOOKAHEAD_PAUSE_SECONDS &&
|
|
1859
|
-
now - (session.lookAheadDisagreementLoggedAt ?? 0) > LOOKAHEAD_DISAGREEMENT_LOG_MS
|
|
1860
|
-
) {
|
|
1861
|
-
session.lookAheadDisagreementLoggedAt = now;
|
|
1855
|
+
const disagrees =
|
|
1856
|
+
Number.isFinite(claimed) && Math.abs(claimed - encodedTo) > LOOKAHEAD_PAUSE_SECONDS;
|
|
1857
|
+
if (disagrees && !session.lookAheadDisagreementSince) {
|
|
1858
|
+
session.lookAheadDisagreementSince = Date.now();
|
|
1862
1859
|
logger.info(
|
|
1863
1860
|
`transcode ${session.id} ffmpeg claims ${Math.round(claimed)}s processed ` +
|
|
1864
1861
|
`but has produced through ${Math.round(encodedTo)}s (segment #${producedThrough})`
|
|
1865
1862
|
);
|
|
1863
|
+
} else if (!disagrees && session.lookAheadDisagreementSince) {
|
|
1864
|
+
const lastedMs = Date.now() - session.lookAheadDisagreementSince;
|
|
1865
|
+
session.lookAheadDisagreementSince = 0;
|
|
1866
|
+
logger.info(
|
|
1867
|
+
`transcode ${session.id} ffmpeg's position and the segments on disk agree again ` +
|
|
1868
|
+
`after ${(lastedMs / 1000).toFixed(1)}s (produced through ${Math.round(encodedTo)}s)`
|
|
1869
|
+
);
|
|
1866
1870
|
}
|
|
1867
1871
|
// Where the viewer is. Before the first segment request, the position the
|
|
1868
1872
|
// run started at — so a session nobody has read from yet is bounded too.
|