@torrent-tv/proxy 2.9.56 → 2.9.57

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,7 @@
1
+ ## 2.9.57
2
+
3
+ - **Fix**: A request for a not-yet-produced segment was held open for up to **30 seconds** before answering. iOS's native HLS player (AVPlayer) enforces a hard **~3.5 s deadline on response headers** and raises `-12889` ("No response for media file") once it passes — it then cancels in-flight requests, probes neighbouring positions, and can restart the stream from the beginning. Because a seek restarts ffmpeg and its first segment takes far longer than 3.5 s to appear, that deadline was hit on **every** seek, which is the root of the field-reported "seek loads for ages, then jumps back to the start" and of the playlist-scanning traffic that 2.9.55 tried (and failed) to work around from the wrong end. The request is now held only ~2 s and then answered with the same retryable 503, which resets the player's deadline and lets it re-request; a ready or nearly-ready segment is still served on the first request, so the fast path is unchanged. Independent of segment format — it affected `fmp4` and `mpegts` equally. hls.js is unaffected (it consumes the 503 through its retry policy); the browser widens that retry budget to match (server-side change, `fragLoadPolicy` `maxNumRetry` 8 → 12).
4
+
1
5
  ## 2.9.56
2
6
 
3
7
  - **Fix**: Reverted the "only the newest request may steer the encoder" guard added in 2.9.55 — it made seeking worse, not better, and is withdrawn rather than patched over. Its premise was that the newest in-flight segment request is the one the viewer actually wants; that does not hold. When the player cannot get its target segment it starts SCANNING the playlist, firing dozens of requests spread across the whole file within half a second (field log: `#178`, `#681`, `#725`, `#807`, `#74`, `#245`, `#387` …). Under that traffic the "newest" request is an arbitrary scan probe, so the guard steered the encoder away from the real seek target; the target segment was never produced and the player gave up and reset to the beginning of the file. The underlying ping-pong (several requests from one scrub taking turns restarting ffmpeg) is a real defect and remains open — but a correct fix has to tell a VIEWER seek apart from the player's own scan, which arrival order does not express. The 2.9.55 progress-timeline fix (video-copy branch) is unaffected and stays.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.9.56",
3
+ "version": "2.9.57",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -1,8 +1,17 @@
1
+ /**
2
+ * How long a request for a not-yet-produced file is held before answering with
3
+ * a retryable 503. Must stay comfortably below iOS AVPlayer's ~3.5 s
4
+ * response-header deadline (see the call site for the full rationale).
5
+ */
6
+ const SEGMENT_WAIT_MS = 2_000;
7
+
1
8
  /**
2
9
  * Serve HLS playlist and segment files from an active transcode session.
3
10
  *
4
- * Polls until the requested file appears (up to 15 s) so that HLS clients
5
- * do not receive a 404 during the ffmpeg warmup phase.
11
+ * Briefly waits for the requested file to appear, then answers with a
12
+ * retryable 503 rather than holding the connection, so clients — in
13
+ * particular iOS's native HLS player — never hit their own response
14
+ * deadline while a segment is still being produced.
6
15
  *
7
16
  * GET /transcode/:sessionId/:fileName
8
17
  *
@@ -14,7 +23,20 @@
14
23
  export async function handleTranscodeSessionFileGet(req, reply, { hlsSessionManager }) {
15
24
  const sessionId = typeof req.params.sessionId === "string" ? req.params.sessionId : "";
16
25
  const fileName = typeof req.params.fileName === "string" ? req.params.fileName : "";
17
- const result = await waitForSessionFile(hlsSessionManager, sessionId, fileName, 30_000);
26
+ // Hold the request only briefly, then answer "retry" instead of waiting for
27
+ // the segment. iOS's native HLS player (AVPlayer) enforces a hard ~3.5 s
28
+ // deadline on RESPONSE HEADERS and raises -12889 ("No response for media
29
+ // file") when it passes — it then cancels in-flight requests, probes
30
+ // neighbouring positions and can restart the stream from the beginning. That
31
+ // is exactly the post-seek "player thrashing" seen in the field, because a
32
+ // seek restarts ffmpeg and the first segment then takes far longer than 3.5 s
33
+ // to appear. Holding the connection for 30 s (as this did) guaranteed the
34
+ // timeout on every seek. A short hold keeps the fast path intact (a ready or
35
+ // nearly-ready segment is still served on the first request) while a slow one
36
+ // gets a prompt retryable answer, which resets the player's own deadline.
37
+ // hls.js is unaffected: it consumes the 503 through its retry policy, whose
38
+ // budget the client widens to match (see hls-player.js fragLoadPolicy).
39
+ const result = await waitForSessionFile(hlsSessionManager, sessionId, fileName, SEGMENT_WAIT_MS);
18
40
 
19
41
  if (result.kind === "not-found") {
20
42
  return reply.code(404).send({ error: "Transcode session file was not found." });