@torrent-tv/proxy 2.101.36 → 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
|
@@ -84,6 +84,8 @@ export async function handleApiTranscodeSessionsPost(req, reply, { viewerRequest
|
|
|
84
84
|
const cancellation = new AbortController();
|
|
85
85
|
const timing = { startedAt: performance.now(), openMs: 0, openCalls: 0,
|
|
86
86
|
planWaitInclusiveMs: null, masterMs: null, offerMs: null, tracksMs: null };
|
|
87
|
+
const forwardedRequestId = typeof req.headers?.["x-ttv-forwarded-request-id"] === "string"
|
|
88
|
+
? req.headers["x-ttv-forwarded-request-id"] : "unknown";
|
|
87
89
|
const cancelled = () => cancellation.abort();
|
|
88
90
|
reply.raw?.once?.("close", cancelled);
|
|
89
91
|
if (reply.raw?.destroyed) cancelled();
|
|
@@ -217,13 +219,16 @@ export async function handleApiTranscodeSessionsPost(req, reply, { viewerRequest
|
|
|
217
219
|
return reply.code(500).send({ error: `Failed to prepare transcode session: ${message}` });
|
|
218
220
|
} finally {
|
|
219
221
|
reply.raw?.removeListener?.("close", cancelled);
|
|
222
|
+
const handlerReturnAt = performance.now();
|
|
220
223
|
const elapsed = (key) => Number.isFinite(timing[key]) ? timing[key].toFixed(1) : "n/a";
|
|
221
224
|
logger.info(
|
|
222
225
|
`transcode-session timing outcome=${cancellation.signal.aborted ? "cancelled" : "handler-return"} ` +
|
|
226
|
+
`requestId=${forwardedRequestId} ` +
|
|
223
227
|
`consumer=${consumerId || "unknown"} file=${fileIndex} openCalls=${timing.openCalls} ` +
|
|
224
228
|
`openMs=${timing.openMs.toFixed(1)} planWaitInclusiveMs=${elapsed("planWaitInclusiveMs")} ` +
|
|
225
229
|
`masterMs=${elapsed("masterMs")} offerMs=${elapsed("offerMs")} tracksMs=${elapsed("tracksMs")} ` +
|
|
226
|
-
`
|
|
230
|
+
`handlerReturnAtMs=${handlerReturnAt.toFixed(3)} ` +
|
|
231
|
+
`handlerMs=${(handlerReturnAt - timing.startedAt).toFixed(1)}`
|
|
227
232
|
);
|
|
228
233
|
}
|
|
229
234
|
}
|
package/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import fastifyCors from "@fastify/cors";
|
|
|
11
11
|
import fastifyHelmet from "@fastify/helmet";
|
|
12
12
|
import getPort from "get-port";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
|
+
import { performance } from "node:perf_hooks";
|
|
14
15
|
import { handleHealthGet } from "./routes/health/get.js";
|
|
15
16
|
import { handleMediaGet } from "./routes/media/get.js";
|
|
16
17
|
import { ProbeRequests } from "./services/media/ProbeRequests.js";
|
|
@@ -152,6 +153,15 @@ export async function startProxyServer({
|
|
|
152
153
|
// receives torrent source payloads that may be arbitrarily large.
|
|
153
154
|
bodyLimit: 256 * 1024 * 1024 // 256 MB
|
|
154
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
|
+
};
|
|
155
165
|
|
|
156
166
|
await app.register(fastifyHelmet, {
|
|
157
167
|
// Proxy serves media to a different origin (registry UI), so CORP must allow cross-origin usage.
|
|
@@ -170,6 +180,34 @@ export async function startProxyServer({
|
|
|
170
180
|
app.addHook("onRequest", async (_req, reply) => {
|
|
171
181
|
reply.header("Access-Control-Allow-Private-Network", "true");
|
|
172
182
|
});
|
|
183
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
184
|
+
const requestId = req.headers["x-ttv-forwarded-request-id"];
|
|
185
|
+
if (typeof requestId !== "string" || req.method !== "POST" || req.url !== "/api/transcode-sessions") return;
|
|
186
|
+
const startedAt = performance.now();
|
|
187
|
+
forwardedSessionResponseStarts.set(req, startedAt);
|
|
188
|
+
logForwardedSessionResponseStage("onRequest", req);
|
|
189
|
+
reply.raw.once("finish", () => {
|
|
190
|
+
logForwardedSessionResponseStage("finish", req);
|
|
191
|
+
logger.info(`[http-response] requestId=${requestId} method=${req.method} url=${req.url} ` +
|
|
192
|
+
`responseMs=${(performance.now() - startedAt).toFixed(1)} status=${reply.statusCode} ` +
|
|
193
|
+
`contentLength=${reply.raw.getHeader("content-length") ?? "unknown"}`);
|
|
194
|
+
});
|
|
195
|
+
reply.raw.once("close", () => {
|
|
196
|
+
if (!reply.raw.writableFinished) {
|
|
197
|
+
logForwardedSessionResponseStage("close", req);
|
|
198
|
+
logger.warn(`[http-response] requestId=${requestId} method=${req.method} url=${req.url} ` +
|
|
199
|
+
`closedMs=${(performance.now() - startedAt).toFixed(1)} status=${reply.statusCode}`);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
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
|
+
});
|
|
173
211
|
|
|
174
212
|
const sourceRegistry = createSourceRegistry(200);
|
|
175
213
|
// The torrent runs on its own thread. Profiling a live seek (2026-08-02)
|
|
@@ -276,13 +276,16 @@ export class LocalObservations {
|
|
|
276
276
|
*/
|
|
277
277
|
noteFirstFrame({ seconds, features, featureSnapshots = null, stages = null }) {
|
|
278
278
|
if (!Number.isFinite(seconds) || seconds < 0 || !features || typeof features !== "object") return;
|
|
279
|
+
const measuredStages = stagesOf(stages);
|
|
280
|
+
if (!measuredStages) return;
|
|
279
281
|
const key = this.#firstFrameKey();
|
|
280
282
|
const snapshots = [features, ...(Array.isArray(featureSnapshots) ? featureSnapshots : [])]
|
|
281
283
|
.filter(value => value && typeof value === "object").slice(0, 2).map(firstFrameFeatures);
|
|
282
|
-
const
|
|
284
|
+
const usableReadings = (this.#entries.firstFrame[key] ?? []).filter(reading => stagesOf(reading.stages));
|
|
285
|
+
const readings = [...usableReadings, {
|
|
283
286
|
...snapshots[0],
|
|
284
287
|
featureSnapshots: snapshots,
|
|
285
|
-
stages:
|
|
288
|
+
stages: measuredStages,
|
|
286
289
|
at: this.#now()
|
|
287
290
|
}].slice(-READINGS_KEPT);
|
|
288
291
|
this.#entries.firstFrame[key] = readings;
|
|
@@ -1171,13 +1171,24 @@ export function createDataChannelHandler({
|
|
|
1171
1171
|
log(`[dc] ${method} ${path}${queryInfo}${bodyInfo}`);
|
|
1172
1172
|
|
|
1173
1173
|
const targetUrl = `http://127.0.0.1:${proxyPort}${path}${query ? `?${query}` : ""}`;
|
|
1174
|
-
const requestHeaders = {
|
|
1174
|
+
const requestHeaders = {
|
|
1175
|
+
...(forwardedHeaders ?? {}),
|
|
1176
|
+
host: `127.0.0.1:${proxyPort}`,
|
|
1177
|
+
"x-ttv-forwarded-request-id": requestId
|
|
1178
|
+
};
|
|
1175
1179
|
|
|
1176
1180
|
let response;
|
|
1177
1181
|
// [net-debug] TEMPORARY: time spent in the local fetch (waiting for the
|
|
1178
1182
|
// route to return a response — e.g. long-polling until an HLS segment is
|
|
1179
1183
|
// finalized by ffmpeg) vs. the body transfer over the data channel.
|
|
1180
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
|
+
}
|
|
1181
1192
|
try {
|
|
1182
1193
|
response = await fetch(targetUrl, {
|
|
1183
1194
|
method,
|
|
@@ -1193,6 +1204,12 @@ export function createDataChannelHandler({
|
|
|
1193
1204
|
send(channel, { type: "response-error", requestId, error: fetchError?.message ?? String(fetchError) });
|
|
1194
1205
|
return;
|
|
1195
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
|
+
}
|
|
1196
1213
|
|
|
1197
1214
|
if (response.status !== 200 && response.status !== 206) {
|
|
1198
1215
|
log(`[dc] ${method} ${path}${queryInfo} → ${response.status}`);
|