@torrent-tv/proxy 2.9.82 → 2.9.83

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.83
2
+
3
+ - **Fix**: Playback died a few seconds in after 2.9.82. The previous muxer wrote each segment under a temporary name and renamed it once complete, so a file appearing WAS a finished segment; the one that takes explicit cut times has no such option and creates the file when writing starts. The route kept judging readiness by existence, so the player was handed a segment that was still being written, rejected it and stopped — while the encoder ran happily ahead, which is exactly how it looked in the field: three segments served, then silence with the transcode at 7.5x. A segment is now considered finished once the next one has been started, or once the run producing it has ended.
4
+
1
5
  ## 2.9.82
2
6
 
3
7
  - **Fix**: The playlist and the real segments now describe the same thing. On the copy path ffmpeg was given only a target duration and chose its own cut points, while the playlist was built from the container keyframe index — two independent calculations tied together by nothing but the assumption that they agree. They do not: the index is a navigation table and is not obliged to list every keyframe. On a field file it held 1902 while ffmpeg found roughly twice as many and cut twice as often, so segment #876 meant 1:26:50 to the player and about minute 58 to ffmpeg. A seek into the middle landed at the end and the reported duration drifted. ffmpeg now receives the very boundaries the playlist was built from, via the `segment` muxer, which takes the list outright — agreement by construction instead of by luck. Verified on deliberately uneven keyframes: cuts requested at 4.44, 10.36, 16.28, 22.2 and 28.12 s landed exactly there. Two measured details are encoded in the code: those times count from the start of the RUN, not of the file (starting at 12 s and asking for 18 s put the cut at 29.4), and a tolerance absorbs rounding so a boundary recorded a hair late cannot skip to the next keyframe and silently double a segment. MPEG-TS only for now — fMP4 can do this too, but only as self-contained fragments, which removes the shared init segment and the `tfdt` rewriting built around it; that is a separate change and not one to make blind.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.9.82",
3
+ "version": "2.9.83",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -1868,6 +1868,9 @@ export class HlsSessionManager {
1868
1868
  stdio: ["ignore", "pipe", "pipe"]
1869
1869
  });
1870
1870
  session.ffmpeg = ffmpeg;
1871
+ // Whether this run cuts at times we gave it. Decides how a segment is
1872
+ // judged finished — see getFileStream.
1873
+ session.usesExplicitCuts = Boolean(cutTimes && cutTimes.length > 0);
1871
1874
  session.encodeStartIndex = safeIndex;
1872
1875
  session.pendingRestartIndex = -1;
1873
1876
  session.lastRestartAt = Date.now();
@@ -2491,6 +2494,27 @@ export class HlsSessionManager {
2491
2494
  const isPlaylist = fileName === PLAYLIST_FILE_NAME;
2492
2495
  try {
2493
2496
  await access(filePath);
2497
+
2498
+ // Existing is not the same as finished. The `hls` muxer wrote each
2499
+ // segment to a temporary name and renamed it once complete, so a file
2500
+ // appearing WAS a finished segment. The `segment` muxer has no such
2501
+ // option: the file appears when writing begins. Serving it then hands the
2502
+ // player a truncated segment, which it rejects and then simply stops —
2503
+ // observed as playback dying a few seconds in with the encoder still
2504
+ // running happily ahead. A segment is finished once the NEXT one has been
2505
+ // started, or once the run producing it has ended.
2506
+ if (!isPlaylist && session.usesExplicitCuts) {
2507
+ const index = this.segmentFormat.segmentIndexFromName(fileName);
2508
+ const isLast = index >= Math.max(0, (session.segmentBoundaries?.length ?? 1) - 2);
2509
+ if (!isLast && session.ffmpeg) {
2510
+ const nextPath = path.join(session.dirPath, this.segmentFormat.segmentFileName(index + 1));
2511
+ try {
2512
+ await access(nextPath);
2513
+ } catch {
2514
+ return { kind: "warming-up" };
2515
+ }
2516
+ }
2517
+ }
2494
2518
  // Cold-start: log the first servable SEGMENT of this session exactly once
2495
2519
  // — the time from session-create entry to a playable first segment.
2496
2520
  if (!isPlaylist && !session.firstSegmentLogged) {