@torrent-tv/proxy 2.13.0 → 2.14.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.13.0",
3
+ "version": "2.14.1",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -73,6 +73,11 @@ export async function handleApiTranscodeSessionsPost(req, reply, { hlsSessionMan
73
73
  // (capped to source), with the realtime budget's auto-downscale + runtime
74
74
  // downswitch disabled for the session.
75
75
  const manualQuality = payload.manualQuality === true;
76
+ // Whether this browser will take its audio from a separate rendition group in
77
+ // the master playlist rather than muxed into the picture. It has to say so:
78
+ // publishing renditions AND muxing the same audio would play it twice, while
79
+ // a browser that does not know about them would get no sound at all.
80
+ const audioRenditions = payload.audioRenditions === true;
76
81
  const startPositionSeconds = Number(payload.startPositionSeconds);
77
82
  const audioTrackIndex = Number(payload.audioTrackIndex);
78
83
  // Which container to produce. The browser knows what its media stack will
@@ -96,6 +101,7 @@ export async function handleApiTranscodeSessionsPost(req, reply, { hlsSessionMan
96
101
  targetWidth: Number.isInteger(targetWidth) && targetWidth > 0 ? targetWidth : 0,
97
102
  targetHeight: Number.isInteger(targetHeight) && targetHeight > 0 ? targetHeight : 0,
98
103
  manualQuality,
104
+ audioRenditions,
99
105
  startPositionSeconds:
100
106
  Number.isFinite(startPositionSeconds) && startPositionSeconds > 0
101
107
  ? startPositionSeconds
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @file GET /transcode/:sessionId/a/:trackIndex/:fileName — one file of an audio
3
+ * rendition.
4
+ *
5
+ * A rendition is one audio track of the file, encoded once and shared by every
6
+ * quality rung, published in the master playlist as `#EXT-X-MEDIA`. It is an
7
+ * ordinary session underneath, living one directory level below the base
8
+ * session so every relative name inside its playlist resolves to it unchanged —
9
+ * the same arrangement quality variants use.
10
+ *
11
+ * Unlike a variant, a rendition is fetched ALONGSIDE the picture rather than
12
+ * instead of it: both encoders run, one for the rung and one for the audio.
13
+ */
14
+
15
+ import { serveSessionFile } from "../session-file/get.js";
16
+
17
+ /**
18
+ * @param {import("fastify").FastifyRequest} req
19
+ * @param {import("fastify").FastifyReply} reply
20
+ * @param {{ hlsSessionManager: import("../../../services/hls-session-manager.js").HlsSessionManager }} deps
21
+ * @returns {Promise<void>}
22
+ */
23
+ export async function handleTranscodeAudioFileGet(req, reply, { hlsSessionManager }) {
24
+ const baseSessionId = typeof req.params.sessionId === "string" ? req.params.sessionId : "";
25
+ const trackIndex = Number(req.params.trackIndex);
26
+ const fileName = typeof req.params.fileName === "string" ? req.params.fileName : "";
27
+
28
+ const resolved = await hlsSessionManager.resolveAudioRenditionFile(baseSessionId, trackIndex, fileName);
29
+ if (resolved.error) {
30
+ // Retryable, like every other not-ready answer on this path: a 500 for a
31
+ // rendition playlist would end the stream over something the next attempt
32
+ // may well get past.
33
+ reply.header("Retry-After", "1");
34
+ return reply.code(503).send({ error: `Could not prepare the audio rendition: ${resolved.error}` });
35
+ }
36
+ if (!resolved.sessionId) {
37
+ return reply.code(404).send({ error: "No such audio rendition for this transcode session." });
38
+ }
39
+
40
+ return serveSessionFile(req, reply, {
41
+ hlsSessionManager,
42
+ sessionId: resolved.sessionId,
43
+ fileName
44
+ });
45
+ }
package/server.js CHANGED
@@ -30,6 +30,7 @@ import { handleApiTranscodeSessionSeekPost } from "./routes/api/transcode-sessio
30
30
  import { handleStreamGet } from "./routes/stream/get.js";
31
31
  import { handleTranscodeSessionFileGet } from "./routes/transcode/session-file/get.js";
32
32
  import { handleTranscodeVariantFileGet } from "./routes/transcode/variant-file/get.js";
33
+ import { handleTranscodeAudioFileGet } from "./routes/transcode/audio-file/get.js";
33
34
  import { handleTranscodeVariantWarmGet } from "./routes/transcode/variant-warm/get.js";
34
35
  import { createSourceRegistry } from "./store/source-registry.js";
35
36
  import { WorkerTorrentPool } from "./services/torrent-worker/pool-adapter.js";
@@ -173,7 +174,10 @@ export async function startProxyServer({ host, port, transcodeAudio, ffmpegBin,
173
174
  // Reuse the media info the planner already probed for this file (same
174
175
  // ffmpeg scan) so createSession skips its own probe. Late-bound: invoked
175
176
  // only at session-create time, after playbackPlanner is initialised.
176
- getCachedMediaInfo: (params) => playbackPlanner.getCachedMediaInfo(params)
177
+ getCachedMediaInfo: (params) => playbackPlanner.getCachedMediaInfo(params),
178
+ // The file's audio tracks, for the master playlist's rendition group. Already
179
+ // probed for the browser's audio menu; read from there rather than probed again.
180
+ getCachedAudioTracks: (params) => playbackPlanner.getCachedAudioTracks(params)
177
181
  });
178
182
  const playbackPlanner = createPlaybackPlanner({
179
183
  ffmpegBin,
@@ -245,6 +249,9 @@ export async function startProxyServer({ host, port, transcodeAudio, ffmpegBin,
245
249
  app.get("/transcode/:sessionId/v/:height/warm", async (req, reply) =>
246
250
  handleTranscodeVariantWarmGet(req, reply, { hlsSessionManager })
247
251
  );
252
+ app.get("/transcode/:sessionId/a/:trackIndex/:fileName", async (req, reply) =>
253
+ handleTranscodeAudioFileGet(req, reply, { hlsSessionManager })
254
+ );
248
255
  app.get("/transcode/:sessionId/v/:height/:fileName", async (req, reply) =>
249
256
  handleTranscodeVariantFileGet(req, reply, { hlsSessionManager })
250
257
  );