@torrent-tv/proxy 2.101.37 → 2.101.38

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.101.37",
3
+ "version": "2.101.38",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -219,6 +219,7 @@ export async function handleApiTranscodeSessionsPost(req, reply, { viewerRequest
219
219
  return reply.code(500).send({ error: `Failed to prepare transcode session: ${message}` });
220
220
  } finally {
221
221
  reply.raw?.removeListener?.("close", cancelled);
222
+ const handlerReturnAt = performance.now();
222
223
  const elapsed = (key) => Number.isFinite(timing[key]) ? timing[key].toFixed(1) : "n/a";
223
224
  logger.info(
224
225
  `transcode-session timing outcome=${cancellation.signal.aborted ? "cancelled" : "handler-return"} ` +
@@ -226,7 +227,8 @@ export async function handleApiTranscodeSessionsPost(req, reply, { viewerRequest
226
227
  `consumer=${consumerId || "unknown"} file=${fileIndex} openCalls=${timing.openCalls} ` +
227
228
  `openMs=${timing.openMs.toFixed(1)} planWaitInclusiveMs=${elapsed("planWaitInclusiveMs")} ` +
228
229
  `masterMs=${elapsed("masterMs")} offerMs=${elapsed("offerMs")} tracksMs=${elapsed("tracksMs")} ` +
229
- `handlerMs=${(performance.now() - timing.startedAt).toFixed(1)}`
230
+ `handlerReturnAtMs=${handlerReturnAt.toFixed(3)} ` +
231
+ `handlerMs=${(handlerReturnAt - timing.startedAt).toFixed(1)}`
230
232
  );
231
233
  }
232
234
  }
package/server.js CHANGED
@@ -153,6 +153,15 @@ export async function startProxyServer({
153
153
  // receives torrent source payloads that may be arbitrarily large.
154
154
  bodyLimit: 256 * 1024 * 1024 // 256 MB
155
155
  });
156
+ const forwardedSessionResponseStarts = new WeakMap();
157
+ const logForwardedSessionResponseStage = (stage, req) => {
158
+ const startedAt = forwardedSessionResponseStarts.get(req);
159
+ const requestId = req.headers["x-ttv-forwarded-request-id"];
160
+ if (startedAt === undefined || typeof requestId !== "string") return;
161
+ const at = performance.now();
162
+ logger.info(`[forecast-http] stage=${stage} requestId=${requestId} atMs=${at.toFixed(3)} ` +
163
+ `elapsedMs=${(at - startedAt).toFixed(3)}`);
164
+ };
156
165
 
157
166
  await app.register(fastifyHelmet, {
158
167
  // Proxy serves media to a different origin (registry UI), so CORP must allow cross-origin usage.
@@ -173,20 +182,32 @@ export async function startProxyServer({
173
182
  });
174
183
  app.addHook("onRequest", async (req, reply) => {
175
184
  const requestId = req.headers["x-ttv-forwarded-request-id"];
176
- if (typeof requestId !== "string") return;
185
+ if (typeof requestId !== "string" || req.method !== "POST" || req.url !== "/api/transcode-sessions") return;
177
186
  const startedAt = performance.now();
187
+ forwardedSessionResponseStarts.set(req, startedAt);
188
+ logForwardedSessionResponseStage("onRequest", req);
178
189
  reply.raw.once("finish", () => {
190
+ logForwardedSessionResponseStage("finish", req);
179
191
  logger.info(`[http-response] requestId=${requestId} method=${req.method} url=${req.url} ` +
180
192
  `responseMs=${(performance.now() - startedAt).toFixed(1)} status=${reply.statusCode} ` +
181
193
  `contentLength=${reply.raw.getHeader("content-length") ?? "unknown"}`);
182
194
  });
183
195
  reply.raw.once("close", () => {
184
196
  if (!reply.raw.writableFinished) {
197
+ logForwardedSessionResponseStage("close", req);
185
198
  logger.warn(`[http-response] requestId=${requestId} method=${req.method} url=${req.url} ` +
186
199
  `closedMs=${(performance.now() - startedAt).toFixed(1)} status=${reply.statusCode}`);
187
200
  }
188
201
  });
189
202
  });
203
+ app.addHook("preSerialization", async (req, _reply, payload) => {
204
+ logForwardedSessionResponseStage("preSerialization", req);
205
+ return payload;
206
+ });
207
+ app.addHook("onSend", async (req, _reply, payload) => {
208
+ logForwardedSessionResponseStage("onSend", req);
209
+ return payload;
210
+ });
190
211
 
191
212
  const sourceRegistry = createSourceRegistry(200);
192
213
  // The torrent runs on its own thread. Profiling a live seek (2026-08-02)
@@ -1182,6 +1182,13 @@ export function createDataChannelHandler({
1182
1182
  // route to return a response — e.g. long-polling until an HLS segment is
1183
1183
  // finalized by ffmpeg) vs. the body transfer over the data channel.
1184
1184
  const fetchStartedAt = Date.now();
1185
+ const traceSessionResponse = method === "POST" && path === "/api/transcode-sessions" &&
1186
+ typeof requestId === "string";
1187
+ const fetchStartedAtMonotonic = traceSessionResponse ? performance.now() : null;
1188
+ if (traceSessionResponse) {
1189
+ log(`[forecast-http] stage=local-fetch-start requestId=${requestId} ` +
1190
+ `atMs=${fetchStartedAtMonotonic.toFixed(3)}`);
1191
+ }
1185
1192
  try {
1186
1193
  response = await fetch(targetUrl, {
1187
1194
  method,
@@ -1197,6 +1204,12 @@ export function createDataChannelHandler({
1197
1204
  send(channel, { type: "response-error", requestId, error: fetchError?.message ?? String(fetchError) });
1198
1205
  return;
1199
1206
  }
1207
+ if (traceSessionResponse) {
1208
+ const headersAt = performance.now();
1209
+ log(`[forecast-http] stage=local-fetch-headers requestId=${requestId} ` +
1210
+ `atMs=${headersAt.toFixed(3)} elapsedMs=${(headersAt - fetchStartedAtMonotonic).toFixed(3)} ` +
1211
+ `status=${response.status}`);
1212
+ }
1200
1213
 
1201
1214
  if (response.status !== 200 && response.status !== 206) {
1202
1215
  log(`[dc] ${method} ${path}${queryInfo} → ${response.status}`);